aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-04-19 14:00:47 -0700
committerrtk0c <[email protected]>2021-04-19 14:00:47 -0700
commit1e09caaa2980fe901453b4b90985967a51157887 (patch)
treedf61974f9a5efa9a6732bd6d7b1ec1e6d1af182a
parentb00b306de1140cb7b759ed0f639e8210fd7dffa6 (diff)
Split workflow into multiple files, fix unity build
-rw-r--r--3rdparty/imgui-node-editor/CMakeLists.txt1
-rw-r--r--3rdparty/imgui/CMakeLists.txt1
-rw-r--r--3rdparty/implot/CMakeLists.txt1
-rw-r--r--CMakeLists.txt2
-rw-r--r--core/CMakeLists.txt39
-rw-r--r--core/src/Model/Workflow/Evaluation.cpp183
-rw-r--r--core/src/Model/Workflow/Evaluation.hpp57
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.cpp15
-rw-r--r--core/src/Model/Workflow/Nodes/DocumentNodes.hpp12
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.cpp86
-rw-r--r--core/src/Model/Workflow/Nodes/NumericNodes.hpp41
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.cpp (renamed from core/src/Model/WorkflowNodes.cpp)117
-rw-r--r--core/src/Model/Workflow/Nodes/TextNodes.hpp50
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.cpp26
-rw-r--r--core/src/Model/Workflow/Nodes/UserInputNodes.hpp21
-rw-r--r--core/src/Model/Workflow/Nodes/fwd.hpp15
-rw-r--r--core/src/Model/Workflow/Value.cpp9
-rw-r--r--core/src/Model/Workflow/Value.hpp28
-rw-r--r--core/src/Model/Workflow/Values/BasicValues.cpp (renamed from core/src/Model/EvaluatedValue.cpp)10
-rw-r--r--core/src/Model/Workflow/Values/BasicValues.hpp (renamed from core/src/Model/EvaluatedValue.hpp)29
-rw-r--r--core/src/Model/Workflow/Values/fwd.hpp6
-rw-r--r--core/src/Model/Workflow/Workflow.cpp (renamed from core/src/Model/Workflow.cpp)180
-rw-r--r--core/src/Model/Workflow/Workflow.hpp (renamed from core/src/Model/Workflow.hpp)52
-rw-r--r--core/src/Model/Workflow/fwd.hpp14
-rw-r--r--core/src/Model/WorkflowNodes.hpp101
-rw-r--r--core/src/Model/fwd.hpp21
-rw-r--r--core/src/UI/UI_Workflows.cpp96
27 files changed, 687 insertions, 526 deletions
diff --git a/3rdparty/imgui-node-editor/CMakeLists.txt b/3rdparty/imgui-node-editor/CMakeLists.txt
index 2730a8e..47199dc 100644
--- a/3rdparty/imgui-node-editor/CMakeLists.txt
+++ b/3rdparty/imgui-node-editor/CMakeLists.txt
@@ -1,6 +1,7 @@
file(GLOB IMGUI_NODE_EDITOR_SOURCES *.cpp)
add_library(imgui-node-editor ${IMGUI_NODE_EDITOR_SOURCES})
+set_target_properties(imgui-node-editor PROPERTIES UNITY_BUILD OFF)
target_include_directories(imgui-node-editor PRIVATE
${CMAKE_SOURCE_DIR}/3rdparty/imgui-node-editor
${CMAKE_SOURCE_DIR}/3rdparty/imgui
diff --git a/3rdparty/imgui/CMakeLists.txt b/3rdparty/imgui/CMakeLists.txt
index de80f9a..bf09c56 100644
--- a/3rdparty/imgui/CMakeLists.txt
+++ b/3rdparty/imgui/CMakeLists.txt
@@ -5,6 +5,7 @@ file(GLOB IMGUI_SOURCES *.cpp)
# the build flags twice both in here and in core/CMakeLists.txt
add_library(imgui ${IMGUI_SOURCES})
+set_target_properties(imgui PROPERTIES UNITY_BUILD OFF)
target_include_directories(imgui
PRIVATE
${CMAKE_SOURCE_DIR}/3rdparty/imgui
diff --git a/3rdparty/implot/CMakeLists.txt b/3rdparty/implot/CMakeLists.txt
index 6b92b02..4fae8f2 100644
--- a/3rdparty/implot/CMakeLists.txt
+++ b/3rdparty/implot/CMakeLists.txt
@@ -1,6 +1,7 @@
file(GLOB IMPLOT_SOURCES *.cpp)
add_library(implot ${IMPLOT_SOURCES})
+set_target_properties(implot PROPERTIES UNITY_BUILD OFF)
target_include_directories(implot PRIVATE
${CMAKE_SOURCE_DIR}/3rdparty/implot
${CMAKE_SOURCE_DIR}/3rdparty/imgui
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0517e4c..2fecd26 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.18)
project(Cplt LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index efa43ed..ccb5aeb 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -14,13 +14,13 @@ elseif(APPLE)
option(BUILD_CORE_WITH_METAL_BACKEND ON)
endif()
-function(add_source_group GROUP_NAME)
- set(${GROUP_NAME} ${ARGN} PARENT_SCOPE)
+macro(add_source_group GROUP_NAME)
+ set(${GROUP_NAME} ${ARGN})
set_source_files_properties(${ARGN}
- PROPERTIES
+ PROPERTIES
UNITY_GROUP "${GROUP_NAME}"
)
-endfunction()
+endmacro()
set(ENTRYPOINT_MODULE_SOURCES
src/Entrypoint/main.cpp
@@ -33,16 +33,30 @@ set(ENTRYPOINT_MODULE_SOURCES
)
add_source_group(MODEL_MODULE_SOURCES
- src/Model/EvaluatedValue.cpp
src/Model/Filter.cpp
src/Model/GlobalStates.cpp
src/Model/Items.cpp
src/Model/Project.cpp
src/Model/TransactionsModel.cpp
- src/Model/Workflow.cpp
- src/Model/WorkflowNodes.cpp
)
+add_source_group(MODEL_WORKFLOW_MODULE_SOURCES
+ src/Model/Workflow/Evaluation.cpp
+ src/Model/Workflow/Value.cpp
+ src/Model/Workflow/Workflow.cpp
+)
+
+add_source_group(MODEL_WORKFLOW_NODES_MODULE_SOURCES
+ src/Model/Workflow/Nodes/DocumentNodes.cpp
+ src/Model/Workflow/Nodes/UserInputNodes.cpp
+ src/Model/Workflow/Nodes/NumericNodes.cpp
+ src/Model/Workflow/Nodes/TextNodes.cpp
+)
+
+add_source_group(MODEL_WORKFLOW_VALUES_MODULE_SOURCES
+ src/Model/Workflow/Values/BasicValues.cpp
+ )
+
add_source_group(UI_MODULE_SOURCES
src/UI/Localization.cpp
src/UI/States.cpp
@@ -80,6 +94,9 @@ function(add_executable_variant TARGET_NAME)
add_executable(${TARGET_NAME}
${ENTRYPOINT_MODULE_SOURCES}
${MODEL_MODULE_SOURCES}
+ ${MODEL_WORKFLOW_MODULE_SOURCES}
+ ${MODEL_WORKFLOW_NODES_MODULE_SOURCES}
+ ${MODEL_WORKFLOW_VALUES_MODULE_SOURCES}
${UI_MODULE_SOURCES}
${UTILS_MODULE_SOURCES}
${UTILS_DIALOG_MODULE_SOURCES}
@@ -187,13 +204,9 @@ function(add_executable_variant TARGET_NAME)
endif()
endif()
- if(BUILD_CORE_WITH_UNITY_BUILD)
+ if(CMAKE_UNITY_BUILD)
message("CpltCore: - using unity build")
- set_target_properties(${TARGET_NAME}
- PROPERTIES
- UNITY_BUILD ON
- UNITY_BUILD_MODE GROUP
- )
+ set_target_properties(${TARGET_NAME} PROPERTIES UNITY_BUILD_MODE GROUP)
else()
message("CpltCore: - using regular build")
endif()
diff --git a/core/src/Model/Workflow/Evaluation.cpp b/core/src/Model/Workflow/Evaluation.cpp
new file mode 100644
index 0000000..111d34e
--- /dev/null
+++ b/core/src/Model/Workflow/Evaluation.cpp
@@ -0,0 +1,183 @@
+#include "Evaluation.hpp"
+
+#include <queue>
+
+namespace {
+enum class EvaluationStatus {
+ Unevaluated,
+ Success,
+ Failed,
+};
+} // namespace
+
+struct WorkflowEvaluationContext::RuntimeNode {
+ EvaluationStatus Status = EvaluationStatus::Unevaluated;
+};
+
+struct WorkflowEvaluationContext::RuntimeConnection {
+ std::unique_ptr<BaseValue> Value;
+
+ bool IsAvailableValue() const {
+ return Value != nullptr;
+ }
+};
+
+WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow)
+ : mWorkflow{ &workflow } {
+ mRuntimeNodes.resize(workflow.mNodes.size());
+ mRuntimeConnections.resize(workflow.mConnections.size());
+}
+
+BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) {
+ if (constant) {
+ return mWorkflow->GetConstantById(id);
+ } else {
+ return mRuntimeConnections[id].Value.get();
+ }
+}
+
+BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) {
+ if (inputPin.IsConnected()) {
+ return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection());
+ } else {
+ return nullptr;
+ }
+}
+
+void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) {
+ mRuntimeConnections[id].Value = std::move(value);
+}
+
+void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) {
+ if (outputPin.IsConnected()) {
+ SetConnectionValue(outputPin.Connection, std::move(value));
+ }
+}
+
+void WorkflowEvaluationContext::Run() {
+ std::queue<size_t> candidates; // Stores index to nodes
+ int evaluatedCount = 0;
+ int erroredCount = 0;
+
+ // Evaluate all the input nodes first
+ for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
+ if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::InputType) {
+ candidates.push(i);
+ }
+ }
+
+ auto AddOutputsToCandidates = [&](size_t idx) {
+ auto& node = *mWorkflow->mNodes[idx];
+ auto& rNode = mRuntimeNodes[idx];
+ for (auto& pin : node.mOutputs) {
+ if (!pin.IsConnected()) continue;
+ // TODO support the other variant
+ if (pin.GetSupportedDirection() != WorkflowConnection::OneToMany) continue;
+
+ auto& rConn = mRuntimeConnections[pin.Connection];
+ auto& conn = mWorkflow->mConnections[pin.Connection];
+ if (rConn.IsAvailableValue()) {
+ for (WorkflowConnection::ConnectionPoint& cp : conn.MultiConnections) {
+ if (rNode.Status != EvaluationStatus::Unevaluated) {
+ candidates.push(cp.Node);
+ }
+ }
+ }
+ }
+ };
+ auto FindCandidates = [&]() {
+ for (size_t i = 0; i < mWorkflow->mNodes.size(); ++i) {
+ auto& node = mWorkflow->mNodes[i];
+ auto& rNode = mRuntimeNodes[i];
+
+ if (rNode.Status != EvaluationStatus::Unevaluated) {
+ continue;
+ }
+
+ for (auto& pin : node->mInputs) {
+ if (!pin.IsConnected()) continue;
+
+ auto& rConn = mRuntimeConnections[pin.Connection];
+ if (!rConn.IsAvailableValue()) {
+ goto skip;
+ }
+ }
+
+ candidates.push(i);
+
+ skip:
+ continue;
+ }
+ };
+
+ while (true) {
+ while (!candidates.empty()) {
+ auto idx = candidates.front();
+ auto& node = *mWorkflow->mNodes[idx];
+ auto& rNode = mRuntimeNodes[idx];
+ candidates.pop();
+
+ int preEvalErrors = mErrors.size();
+ node.Evaluate(*this);
+ if (preEvalErrors != mErrors.size()) {
+ erroredCount++;
+ } else {
+ evaluatedCount++;
+ AddOutputsToCandidates(idx);
+ }
+ }
+
+ if (evaluatedCount + erroredCount >= mRuntimeNodes.size()) {
+ break;
+ }
+
+ // Candidates empty, but there are still possibly-evaluable nodes
+ FindCandidates();
+ }
+
+ for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
+ if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::OutputType) {
+ // TODO
+ }
+ }
+}
+
+void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
+ mErrors.push_back(WorkflowEvaluationError{
+ .Message = std::move(message),
+ .NodeId = node.GetId(),
+ .PinId = pinId,
+ .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
+ .Type = WorkflowEvaluationError::Error,
+ });
+}
+
+void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) {
+ mErrors.push_back(WorkflowEvaluationError{
+ .Message = std::move(message),
+ .NodeId = node.GetId(),
+ .PinId = -1,
+ .PinType = WorkflowEvaluationError::NoPin,
+ .Type = WorkflowEvaluationError::Error,
+ });
+}
+
+void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
+ mErrors.push_back(WorkflowEvaluationError{
+ .Message = std::move(message),
+ .NodeId = node.GetId(),
+ .PinId = pinId,
+ .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
+ .Type = WorkflowEvaluationError::Warning,
+ });
+}
+
+void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) {
+ mErrors.push_back(WorkflowEvaluationError{
+ .Message = std::move(message),
+ .NodeId = node.GetId(),
+ .PinId = -1,
+ .PinType = WorkflowEvaluationError::NoPin,
+ .Type = WorkflowEvaluationError::Warning,
+ });
+}
diff --git a/core/src/Model/Workflow/Evaluation.hpp b/core/src/Model/Workflow/Evaluation.hpp
new file mode 100644
index 0000000..be2e862
--- /dev/null
+++ b/core/src/Model/Workflow/Evaluation.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "Model/Workflow/Workflow.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+class WorkflowEvaluationError {
+public:
+ enum MessageType : int16_t {
+ Error,
+ Warning,
+ };
+
+ enum PinType : int16_t {
+ NoPin,
+ InputPin,
+ OutputPin,
+ };
+
+public:
+ std::string Message;
+ size_t NodeId;
+ int PinId;
+ PinType PinType;
+ MessageType Type;
+};
+
+class WorkflowEvaluationContext {
+private:
+ struct RuntimeNode;
+ struct RuntimeConnection;
+
+ Workflow* mWorkflow;
+ std::vector<RuntimeNode> mRuntimeNodes;
+ std::vector<RuntimeConnection> mRuntimeConnections;
+ std::vector<WorkflowEvaluationError> mErrors;
+ std::vector<WorkflowEvaluationError> mWarnings;
+
+public:
+ WorkflowEvaluationContext(Workflow& workflow);
+
+ BaseValue* GetConnectionValue(size_t id, bool constant);
+ BaseValue* GetConnectionValue(const WorkflowNode::InputPin& inputPin);
+ void SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value);
+ void SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value);
+
+ void ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin);
+ void ReportError(std::string message, const WorkflowNode& node);
+ void ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin);
+ void ReportWarning(std::string message, const WorkflowNode& node);
+
+ /// Run until all possible paths have been evaluated.
+ void Run();
+};
diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp
new file mode 100644
index 0000000..66d2eae
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp
@@ -0,0 +1,15 @@
+#include "DocumentNodes.hpp"
+
+#include "Model/Workflow/Evaluation.hpp"
+#include "Model/Workflow/Values/BasicValues.hpp"
+
+bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) {
+ return node->GetKind() == KD_DocumentTemplateExpansion;
+}
+
+DocumentTemplateExpansionNode::DocumentTemplateExpansionNode()
+ : WorkflowNode(TransformType, KD_DocumentTemplateExpansion) {
+}
+
+void DocumentTemplateExpansionNode::Evaluate(WorkflowEvaluationContext& ctx) {
+}
diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.hpp b/core/src/Model/Workflow/Nodes/DocumentNodes.hpp
new file mode 100644
index 0000000..3b775ec
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/DocumentNodes.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Model/Workflow/Workflow.hpp"
+
+class DocumentTemplateExpansionNode : public WorkflowNode {
+public:
+ static bool IsInstance(const WorkflowNode* node);
+ DocumentTemplateExpansionNode();
+
+ // TODO
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+};
diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.cpp b/core/src/Model/Workflow/Nodes/NumericNodes.cpp
new file mode 100644
index 0000000..1722224
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/NumericNodes.cpp
@@ -0,0 +1,86 @@
+#include "NumericNodes.hpp"
+
+#include "Model/Workflow/Evaluation.hpp"
+#include "Model/Workflow/Values/BasicValues.hpp"
+#include "Utils/Macros.hpp"
+#include "Utils/RTTI.hpp"
+
+#include <cassert>
+#include <utility>
+
+WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) {
+ switch (type) {
+ case Addition: return KD_NumericAddition;
+ case Subtraction: return KD_NumericSubtraction;
+ case Multiplication: return KD_NumericMultiplication;
+ case Division: return KD_NumericDivision;
+ default: UNREACHABLE;
+ }
+}
+
+NumericOperationNode::OperationType NumericOperationNode::NodeKindToOperationType(Kind kind) {
+ switch (kind) {
+ case KD_NumericAddition: return Addition;
+ case KD_NumericSubtraction: return Subtraction;
+ case KD_NumericMultiplication: return Multiplication;
+ case KD_NumericDivision: return Division;
+ default: UNREACHABLE;
+ }
+}
+
+bool NumericOperationNode::IsInstance(const WorkflowNode* node) {
+ return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision;
+}
+
+NumericOperationNode::NumericOperationNode(OperationType type)
+ : WorkflowNode(TransformType, OperationTypeToNodeKind(type))
+ , mType{ type } {
+ mInputs.resize(2);
+ mInputs[0].MatchingType = BaseValue::KD_Numeric;
+ mInputs[1].MatchingType = BaseValue::KD_Numeric;
+
+ mOutputs.resize(1);
+ mOutputs[0].MatchingType = BaseValue::KD_Numeric;
+}
+
+void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) {
+ auto lhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[0]));
+ if (!lhsVal) return;
+ double lhs = lhsVal->GetValue();
+
+ auto rhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[1]));
+ if (!rhsVal) return;
+ double rhs = rhsVal->GetValue();
+
+ double res;
+ switch (mType) {
+ case Addition: res = lhs + rhs; break;
+ case Subtraction: res = lhs - rhs; break;
+ case Multiplication: res = lhs * rhs; break;
+ case Division: {
+ if (rhs == 0.0) {
+ // TODO localize
+ ctx.ReportError("Error: division by 0", *this);
+ return;
+ }
+ res = lhs / rhs;
+ } break;
+
+ default: return;
+ }
+
+ auto value = std::make_unique<NumericValue>();
+ value->SetValue(res);
+ ctx.SetConnectionValue(mOutputs[0], std::move(value));
+}
+
+bool NumericExpressionNode::IsInstance(const WorkflowNode* node) {
+ return node->GetKind() == KD_NumericExpression;
+}
+
+NumericExpressionNode::NumericExpressionNode()
+ : WorkflowNode(TransformType, KD_NumericExpression) {
+}
+
+void NumericExpressionNode::Evaluate(WorkflowEvaluationContext& ctx) {
+}
diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.hpp b/core/src/Model/Workflow/Nodes/NumericNodes.hpp
new file mode 100644
index 0000000..32610f6
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/NumericNodes.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "Model/Workflow/Workflow.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <variant>
+#include <vector>
+
+class NumericOperationNode : public WorkflowNode {
+public:
+ enum OperationType {
+ Addition,
+ Subtraction,
+ Multiplication,
+ Division,
+
+ InvalidType,
+ TypeCount = InvalidType,
+ };
+
+private:
+ OperationType mType;
+
+public:
+ static Kind OperationTypeToNodeKind(OperationType type);
+ static OperationType NodeKindToOperationType(Kind kind);
+ static bool IsInstance(const WorkflowNode* node);
+ NumericOperationNode(OperationType type);
+
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+};
+
+class NumericExpressionNode : public WorkflowNode {
+public:
+ static bool IsInstance(const WorkflowNode* node);
+ NumericExpressionNode();
+
+ // TODO
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+}; \ No newline at end of file
diff --git a/core/src/Model/WorkflowNodes.cpp b/core/src/Model/Workflow/Nodes/TextNodes.cpp
index f58c8bb..3852c66 100644
--- a/core/src/Model/WorkflowNodes.cpp
+++ b/core/src/Model/Workflow/Nodes/TextNodes.cpp
@@ -1,5 +1,7 @@
-#include "WorkflowNodes.hpp"
+#include "TextNodes.hpp"
+#include "Model/Workflow/Evaluation.hpp"
+#include "Model/Workflow/Values/BasicValues.hpp"
#include "Utils/Macros.hpp"
#include "Utils/RTTI.hpp"
#include "Utils/Variant.hpp"
@@ -7,85 +9,10 @@
#include <cassert>
#include <utility>
#include <variant>
+#include <vector>
-WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) {
- switch (type) {
- case Addition: return KD_NumericAddition;
- case Subtraction: return KD_NumericSubtraction;
- case Multiplication: return KD_NumericMultiplication;
- case Division: return KD_NumericDivision;
- default: UNREACHABLE;
- }
-}
-
-NumericOperationNode::OperationType NumericOperationNode::NodeKindToOperationType(Kind kind) {
- switch (kind) {
- case KD_NumericAddition: return Addition;
- case KD_NumericSubtraction: return Subtraction;
- case KD_NumericMultiplication: return Multiplication;
- case KD_NumericDivision: return Division;
- default: UNREACHABLE;
- }
-}
-
-bool NumericOperationNode::IsInstance(const WorkflowNode* node) {
- return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision;
-}
-
-NumericOperationNode::NumericOperationNode(OperationType type)
- : WorkflowNode(TransformType, OperationTypeToNodeKind(type))
- , mType{ type } {
- mInputs.resize(2);
- mInputs[0].MatchingType = BaseValue::KD_Numeric;
- mInputs[1].MatchingType = BaseValue::KD_Numeric;
-
- mOutputs.resize(1);
- mOutputs[0].MatchingType = BaseValue::KD_Numeric;
-}
-
-void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) {
- auto lhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[0]));
- if (!lhsVal) return;
- double lhs = lhsVal->GetValue();
-
- auto rhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[1]));
- if (!rhsVal) return;
- double rhs = rhsVal->GetValue();
-
- double res;
- switch (mType) {
- case Addition: res = lhs + rhs; break;
- case Subtraction: res = lhs - rhs; break;
- case Multiplication: res = lhs * rhs; break;
- case Division: {
- if (rhs == 0.0) {
- // TODO localize
- ctx.ReportError("Error: division by 0", *this);
- return;
- }
- res = lhs / rhs;
- } break;
-
- default: return;
- }
-
- auto value = std::make_unique<NumericValue>();
- value->SetValue(res);
- ctx.SetConnectionValue(mOutputs[0], std::move(value));
-}
-
-bool NumericExpressionNode::IsInstance(const WorkflowNode* node) {
- return node->GetKind() == KD_NumericExpression;
-}
-
-NumericExpressionNode::NumericExpressionNode()
- : WorkflowNode(TransformType, KD_NumericExpression) {
-}
-
-struct TextFormatterNode::Argument {
- ArgumentType ArgumentType;
- int PinIdx;
-
+class TextFormatterNode::Impl {
+public:
template <class TFunction>
static void ForArguments(std::vector<Element>::iterator begin, std::vector<Element>::iterator end, const TFunction& func) {
for (auto it = begin; it != end; ++it) {
@@ -157,7 +84,7 @@ void TextFormatterNode::SetElement(int idx, ArgumentType argument) {
mElements[idx] = Argument{
.ArgumentType = argument,
- .PinIdx = Argument::FindPinForElement(mElements, idx),
+ .PinIdx = Impl::FindPinForElement(mElements, idx),
};
/* `original` is invalid from this point */
},
@@ -191,7 +118,7 @@ void TextFormatterNode::InsertElement(int idx, ArgumentType argument) {
assert(idx >= 0);
if (idx >= mElements.size()) AppendElement(argument);
- int pinIdx = Argument::FindPinForElement(mElements, idx);
+ int pinIdx = Impl::FindPinForElement(mElements, idx);
// Create pin
auto& pin = InsertInputPin(pinIdx);
@@ -278,35 +205,11 @@ void TextFormatterNode::PreRemoveElement(int idx) {
auto& elm = mElements[idx];
if (auto arg = std::get_if<Argument>(&elm)) {
RemoveInputPin(arg->PinIdx);
- Argument::ForArguments(
+ Impl::ForArguments(
mElements.begin() + idx + 1,
mElements.end(),
[&](Argument& arg) {
arg.PinIdx--;
});
}
-}
-
-bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) {
- return node->GetKind() == KD_DocumentTemplateExpansion;
-}
-
-DocumentTemplateExpansionNode::DocumentTemplateExpansionNode()
- : WorkflowNode(TransformType, KD_DocumentTemplateExpansion) {
-}
-
-bool FormInputNode::IsInstance(const WorkflowNode* node) {
- return node->GetKind() == KD_FormInput;
-}
-
-FormInputNode::FormInputNode()
- : WorkflowNode(InputType, KD_FormInput) {
-}
-
-bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) {
- return node->GetKind() == KD_DatabaseRowsInput;
-}
-
-DatabaseRowsInputNode::DatabaseRowsInputNode()
- : WorkflowNode(InputType, KD_DatabaseRowsInput) {
-}
+} \ No newline at end of file
diff --git a/core/src/Model/Workflow/Nodes/TextNodes.hpp b/core/src/Model/Workflow/Nodes/TextNodes.hpp
new file mode 100644
index 0000000..278db32
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/TextNodes.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "Model/Workflow/Workflow.hpp"
+
+#include <cstddef>
+#include <memory>
+#include <variant>
+#include <vector>
+
+class TextFormatterNode : public WorkflowNode {
+public:
+ enum ArgumentType {
+ NumericArgument,
+ TextArgument,
+ DateTimeArgument,
+ };
+
+private:
+ class Impl;
+
+ struct Argument {
+ ArgumentType ArgumentType;
+ int PinIdx;
+ };
+ using Element = std::variant<std::string, Argument>;
+
+ std::vector<Element> mElements;
+ int mMinOutputChars;
+
+public:
+ static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg);
+ static bool IsInstance(const WorkflowNode* node);
+ TextFormatterNode();
+
+ int GetElementCount() const;
+ const Element& GetElement(int idx) const;
+
+ void SetElement(int idx, std::string text);
+ void SetElement(int idx, ArgumentType argument);
+ void InsertElement(int idx, std::string text);
+ void InsertElement(int idx, ArgumentType argument);
+ void AppendElement(std::string text);
+ void AppendElement(ArgumentType argument);
+ void RemoveElement(int idx);
+
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+
+private:
+ void PreRemoveElement(int idx);
+}; \ No newline at end of file
diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp
new file mode 100644
index 0000000..f59226a
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp
@@ -0,0 +1,26 @@
+#include "UserInputNodes.hpp"
+
+#include "Model/Workflow/Evaluation.hpp"
+#include "Model/Workflow/Values/BasicValues.hpp"
+
+bool FormInputNode::IsInstance(const WorkflowNode* node) {
+ return node->GetKind() == KD_FormInput;
+}
+
+FormInputNode::FormInputNode()
+ : WorkflowNode(InputType, KD_FormInput) {
+}
+
+void FormInputNode::Evaluate(WorkflowEvaluationContext& ctx) {
+}
+
+bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) {
+ return node->GetKind() == KD_DatabaseRowsInput;
+}
+
+DatabaseRowsInputNode::DatabaseRowsInputNode()
+ : WorkflowNode(InputType, KD_DatabaseRowsInput) {
+}
+
+void DatabaseRowsInputNode::Evaluate(WorkflowEvaluationContext& ctx) {
+}
diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.hpp b/core/src/Model/Workflow/Nodes/UserInputNodes.hpp
new file mode 100644
index 0000000..fe66cb4
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/UserInputNodes.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Model/Workflow/Workflow.hpp"
+
+class FormInputNode : public WorkflowNode {
+public:
+ static bool IsInstance(const WorkflowNode* node);
+ FormInputNode();
+
+ // TODO
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+};
+
+class DatabaseRowsInputNode : public WorkflowNode {
+public:
+ static bool IsInstance(const WorkflowNode* node);
+ DatabaseRowsInputNode();
+
+ // TODO
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
+};
diff --git a/core/src/Model/Workflow/Nodes/fwd.hpp b/core/src/Model/Workflow/Nodes/fwd.hpp
new file mode 100644
index 0000000..4153825
--- /dev/null
+++ b/core/src/Model/Workflow/Nodes/fwd.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+// DocumentNodes.hpp
+class DocumentTemplateExpansionNode;
+
+// InputNodes.hpp
+class FormInputNode;
+class DatabaseRowsInputNode;
+
+// NumericNodes.hpp
+class NumericOperationNode;
+class NumericExpressionNode;
+
+// TextNodes.hpp
+class TextFormatterNode;
diff --git a/core/src/Model/Workflow/Value.cpp b/core/src/Model/Workflow/Value.cpp
new file mode 100644
index 0000000..7e5aabf
--- /dev/null
+++ b/core/src/Model/Workflow/Value.cpp
@@ -0,0 +1,9 @@
+#include "Value.hpp"
+
+BaseValue::BaseValue(Kind kind)
+ : mKind{ kind } {
+}
+
+BaseValue::Kind BaseValue::GetKind() const {
+ return mKind;
+}
diff --git a/core/src/Model/Workflow/Value.hpp b/core/src/Model/Workflow/Value.hpp
new file mode 100644
index 0000000..eb99c14
--- /dev/null
+++ b/core/src/Model/Workflow/Value.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+class BaseValue {
+public:
+ enum Kind {
+ KD_Numeric,
+ KD_Text,
+ KD_DateTime,
+
+ /// An unspecified type, otherwise known as "any" in some contexts.
+ KindInvalid,
+ KindCount = KindInvalid,
+ };
+
+private:
+ Kind mKind;
+
+public:
+ BaseValue(Kind kind);
+ virtual ~BaseValue() = default;
+
+ BaseValue(const BaseValue&) = delete;
+ BaseValue& operator=(const BaseValue&) = delete;
+ BaseValue(BaseValue&&) = default;
+ BaseValue& operator=(BaseValue&&) = default;
+
+ Kind GetKind() const;
+};
diff --git a/core/src/Model/EvaluatedValue.cpp b/core/src/Model/Workflow/Values/BasicValues.cpp
index 685d50f..fd70acd 100644
--- a/core/src/Model/EvaluatedValue.cpp
+++ b/core/src/Model/Workflow/Values/BasicValues.cpp
@@ -1,15 +1,7 @@
-#include "EvaluatedValue.hpp"
+#include "BasicValues.hpp"
#include <charconv>
-BaseValue::BaseValue(Kind kind)
- : mKind{ kind } {
-}
-
-BaseValue::Kind BaseValue::GetKind() const {
- return mKind;
-}
-
bool NumericValue::IsInstance(const BaseValue* value) {
return value->GetKind() == KD_Numeric;
}
diff --git a/core/src/Model/EvaluatedValue.hpp b/core/src/Model/Workflow/Values/BasicValues.hpp
index 880fd3e..a116c8c 100644
--- a/core/src/Model/EvaluatedValue.hpp
+++ b/core/src/Model/Workflow/Values/BasicValues.hpp
@@ -1,36 +1,11 @@
#pragma once
+#include "Model/Workflow/Value.hpp"
+
#include <chrono>
#include <cstdint>
#include <string>
-class BaseValue {
-public:
- enum Kind {
- KD_Numeric,
- KD_Text,
- KD_DateTime,
-
- /// An unspecified type, otherwise known as "any" in some contexts.
- KindInvalid,
- KindCount = KindInvalid,
- };
-
-private:
- Kind mKind;
-
-public:
- BaseValue(Kind kind);
- virtual ~BaseValue() = default;
-
- BaseValue(const BaseValue&) = delete;
- BaseValue& operator=(const BaseValue&) = delete;
- BaseValue(BaseValue&&) = default;
- BaseValue& operator=(BaseValue&&) = default;
-
- Kind GetKind() const;
-};
-
class NumericValue : public BaseValue {
private:
double mValue;
diff --git a/core/src/Model/Workflow/Values/fwd.hpp b/core/src/Model/Workflow/Values/fwd.hpp
new file mode 100644
index 0000000..24f8119
--- /dev/null
+++ b/core/src/Model/Workflow/Values/fwd.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+// BasicValues.hpp
+class NumericValue;
+class TextValue;
+class DateTimeValue;
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow/Workflow.cpp
index 7ca1b9e..a32149e 100644
--- a/core/src/Model/Workflow.cpp
+++ b/core/src/Model/Workflow/Workflow.cpp
@@ -394,183 +394,3 @@ std::pair<std::unique_ptr<WorkflowNode>&, size_t> Workflow::AllocWorkflowStep()
auto id = mNodes.size();
return { mNodes.emplace_back(std::unique_ptr<WorkflowNode>()), id };
}
-
-namespace {
-enum class EvaluationStatus {
- Unevaluated,
- Success,
- Failed,
-};
-} // namespace
-
-struct WorkflowEvaluationContext::RuntimeNode {
- EvaluationStatus Status = EvaluationStatus::Unevaluated;
-};
-
-struct WorkflowEvaluationContext::RuntimeConnection {
- std::unique_ptr<BaseValue> Value;
-
- bool IsAvailableValue() const {
- return Value != nullptr;
- }
-};
-
-WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow)
- : mWorkflow{ &workflow } {
- mRuntimeNodes.resize(workflow.mNodes.size());
- mRuntimeConnections.resize(workflow.mConnections.size());
-}
-
-BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) {
- if (constant) {
- return mWorkflow->GetConstantById(id);
- } else {
- return mRuntimeConnections[id].Value.get();
- }
-}
-
-BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) {
- if (inputPin.IsConnected()) {
- return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection());
- } else {
- return nullptr;
- }
-}
-
-void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) {
- mRuntimeConnections[id].Value = std::move(value);
-}
-
-void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) {
- if (outputPin.IsConnected()) {
- SetConnectionValue(outputPin.Connection, std::move(value));
- }
-}
-
-void WorkflowEvaluationContext::Run() {
- std::queue<size_t> candidates; // Stores index to nodes
- int evaluatedCount = 0;
- int erroredCount = 0;
-
- // Evaluate all the input nodes first
- for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
- if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::InputType) {
- candidates.push(i);
- }
- }
-
- auto AddOutputsToCandidates = [&](size_t idx) {
- auto& node = *mWorkflow->mNodes[idx];
- auto& rNode = mRuntimeNodes[idx];
- for (auto& pin : node.mOutputs) {
- if (!pin.IsConnected()) continue;
- // TODO support the other variant
- if (pin.GetSupportedDirection() != WorkflowConnection::OneToMany) continue;
-
- auto& rConn = mRuntimeConnections[pin.Connection];
- auto& conn = mWorkflow->mConnections[pin.Connection];
- if (rConn.IsAvailableValue()) {
- for (WorkflowConnection::ConnectionPoint& cp : conn.MultiConnections) {
- if (rNode.Status != EvaluationStatus::Unevaluated) {
- candidates.push(cp.Node);
- }
- }
- }
- }
- };
- auto FindCandidates = [&]() {
- for (size_t i = 0; i < mWorkflow->mNodes.size(); ++i) {
- auto& node = mWorkflow->mNodes[i];
- auto& rNode = mRuntimeNodes[i];
-
- if (rNode.Status != EvaluationStatus::Unevaluated) {
- continue;
- }
-
- for (auto& pin : node->mInputs) {
- if (!pin.IsConnected()) continue;
-
- auto& rConn = mRuntimeConnections[pin.Connection];
- if (!rConn.IsAvailableValue()) {
- goto skip;
- }
- }
-
- candidates.push(i);
-
- skip:
- continue;
- }
- };
-
- while (true) {
- while (!candidates.empty()) {
- auto idx = candidates.front();
- auto& node = *mWorkflow->mNodes[idx];
- auto& rNode = mRuntimeNodes[idx];
- candidates.pop();
-
- int preEvalErrors = mErrors.size();
- node.Evaluate(*this);
- if (preEvalErrors != mErrors.size()) {
- erroredCount++;
- } else {
- evaluatedCount++;
- AddOutputsToCandidates(idx);
- }
- }
-
- if (evaluatedCount + erroredCount >= mRuntimeNodes.size()) {
- break;
- }
-
- // Candidates empty, but there are still possibly-evaluable nodes
- FindCandidates();
- }
-
- for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
- if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::OutputType) {
- // TODO
- }
- }
-}
-
-void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = pinId,
- .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
- .Type = WorkflowEvaluationError::Error,
- });
-}
-
-void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = -1,
- .PinType = WorkflowEvaluationError::NoPin,
- .Type = WorkflowEvaluationError::Error,
- });
-}
-
-void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = pinId,
- .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
- .Type = WorkflowEvaluationError::Warning,
- });
-}
-
-void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = -1,
- .PinType = WorkflowEvaluationError::NoPin,
- .Type = WorkflowEvaluationError::Warning,
- });
-}
diff --git a/core/src/Model/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp
index f1459ba..a7b2c31 100644
--- a/core/src/Model/Workflow.hpp
+++ b/core/src/Model/Workflow/Workflow.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "Model/EvaluatedValue.hpp"
+#include "Value.hpp"
#include "cplt_fwd.hpp"
#include <cstddef>
@@ -8,6 +8,7 @@
#include <limits>
#include <memory>
#include <span>
+#include <string>
#include <vector>
class WorkflowConnection {
@@ -160,52 +161,3 @@ private:
std::pair<WorkflowConnection&, size_t> AllocWorkflowConnection();
std::pair<std::unique_ptr<WorkflowNode>&, size_t> AllocWorkflowStep();
};
-
-class WorkflowEvaluationError {
-public:
- enum MessageType : int16_t {
- Error,
- Warning,
- };
-
- enum PinType : int16_t {
- NoPin,
- InputPin,
- OutputPin,
- };
-
-public:
- std::string Message;
- size_t NodeId;
- int PinId;
- PinType PinType;
- MessageType Type;
-};
-
-class WorkflowEvaluationContext {
-private:
- struct RuntimeNode;
- struct RuntimeConnection;
-
- Workflow* mWorkflow;
- std::vector<RuntimeNode> mRuntimeNodes;
- std::vector<RuntimeConnection> mRuntimeConnections;
- std::vector<WorkflowEvaluationError> mErrors;
- std::vector<WorkflowEvaluationError> mWarnings;
-
-public:
- WorkflowEvaluationContext(Workflow& workflow);
-
- BaseValue* GetConnectionValue(size_t id, bool constant);
- BaseValue* GetConnectionValue(const WorkflowNode::InputPin& inputPin);
- void SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value);
- void SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value);
-
- void ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin);
- void ReportError(std::string message, const WorkflowNode& node);
- void ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin);
- void ReportWarning(std::string message, const WorkflowNode& node);
-
- /// Run until all possible paths have been evaluated.
- void Run();
-};
diff --git a/core/src/Model/Workflow/fwd.hpp b/core/src/Model/Workflow/fwd.hpp
new file mode 100644
index 0000000..2323a91
--- /dev/null
+++ b/core/src/Model/Workflow/fwd.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Model/Workflow/Nodes/fwd.hpp"
+#include "Model/Workflow/Values/fwd.hpp"
+
+// Value.hpp
+class BaseValue;
+
+// Workflow.hpp
+class WorkflowConnection;
+class WorkflowNode;
+class Workflow;
+class WorkflowEvaluationError;
+class WorkflowEvaluationContext;
diff --git a/core/src/Model/WorkflowNodes.hpp b/core/src/Model/WorkflowNodes.hpp
deleted file mode 100644
index 677158c..0000000
--- a/core/src/Model/WorkflowNodes.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-
-#include "Model/Workflow.hpp"
-
-#include <cstddef>
-#include <memory>
-#include <variant>
-#include <vector>
-
-class NumericOperationNode : public WorkflowNode {
-public:
- enum OperationType {
- Addition,
- Subtraction,
- Multiplication,
- Division,
-
- InvalidType,
- TypeCount = InvalidType,
- };
-
-private:
- OperationType mType;
-
-public:
- static Kind OperationTypeToNodeKind(OperationType type);
- static OperationType NodeKindToOperationType(Kind kind);
- static bool IsInstance(const WorkflowNode* node);
- NumericOperationNode(OperationType type);
-
- virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
-};
-
-class NumericExpressionNode : public WorkflowNode {
-public:
- static bool IsInstance(const WorkflowNode* node);
- NumericExpressionNode();
-
- // TODO
-};
-
-class TextFormatterNode : public WorkflowNode {
-public:
- enum ArgumentType {
- NumericArgument,
- TextArgument,
- DateTimeArgument,
- };
-
-private:
- struct Argument;
- using Element = std::variant<std::string, Argument>;
-
- std::vector<Element> mElements;
- int mMinOutputChars;
-
-public:
- static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg);
- static bool IsInstance(const WorkflowNode* node);
- TextFormatterNode();
-
- int GetElementCount() const;
- const Element& GetElement(int idx) const;
-
- void SetElement(int idx, std::string text);
- void SetElement(int idx, ArgumentType argument);
- void InsertElement(int idx, std::string text);
- void InsertElement(int idx, ArgumentType argument);
- void AppendElement(std::string text);
- void AppendElement(ArgumentType argument);
- void RemoveElement(int idx);
-
- virtual void Evaluate(WorkflowEvaluationContext& ctx) override;
-
-private:
- void PreRemoveElement(int idx);
-};
-
-class DocumentTemplateExpansionNode : public WorkflowNode {
-public:
- static bool IsInstance(const WorkflowNode* node);
- DocumentTemplateExpansionNode();
-
- // TODO
-};
-
-class FormInputNode : public WorkflowNode {
-public:
- static bool IsInstance(const WorkflowNode* node);
- FormInputNode();
-
- // TODO
-};
-
-class DatabaseRowsInputNode : public WorkflowNode {
-public:
- static bool IsInstance(const WorkflowNode* node);
- DatabaseRowsInputNode();
-
- // TODO
-};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index 5a75394..594599c 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -1,10 +1,6 @@
#pragma once
-// EvaluatedValue.hpp
-class BaseValue;
-class NumericValue;
-class TextValue;
-class DateTimeValue;
+#include "Model/Workflow/fwd.hpp"
// Filter.hpp
class TableRowsFilter;
@@ -29,18 +25,3 @@ class SalesTable;
class PurchasesTable;
class DeliveryTable;
class TransactionModel;
-
-// Workflow.hpp
-class WorkflowConnection;
-class WorkflowNode;
-class Workflow;
-class WorkflowEvaluationError;
-class WorkflowEvaluationContext;
-
-// WorkflowSteps.hpp
-class NumericOperationNode;
-class NumericExpressionNode;
-class TextFormatterNode;
-class DocumentTemplateExpansionNode;
-class FormInputNode;
-class DatabaseRowsInputNode;
diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp
index 101fa7b..0adfdc2 100644
--- a/core/src/UI/UI_Workflows.cpp
+++ b/core/src/UI/UI_Workflows.cpp
@@ -1,11 +1,16 @@
#include "UI.hpp"
-#include "Model/Workflow.hpp"
-#include "Model/WorkflowNodes.hpp"
+#include "Model/Workflow/Nodes/DocumentNodes.hpp"
+#include "Model/Workflow/Nodes/NumericNodes.hpp"
+#include "Model/Workflow/Nodes/TextNodes.hpp"
+#include "Model/Workflow/Nodes/UserInputNodes.hpp"
+#include "Model/Workflow/Workflow.hpp"
#include "UI/Localization.hpp"
+#include "Utils/Macros.hpp"
#include <imgui.h>
#include <memory>
+#include <span>
#include <vector>
namespace {
@@ -13,12 +18,41 @@ class WorkflowCreationMenu {
private:
using WorkflowNodeConstructor = std::unique_ptr<WorkflowNode> (*)();
+ enum Category {
+ NumericCategory,
+ TextCategory,
+ DocumentsCategory,
+ UserInputCategory,
+ SystemInputCategory,
+ OutputCategory,
+ };
+
struct Candidate {
WorkflowNodeConstructor Constructor;
+ std::string Name;
+ Category Category;
};
std::vector<Candidate> mCandidates;
+#define SUB_RANGE_ACCESS(Type, AccessorName, storage, begin, nextBegin) \
+ std::span<Type> AccessorName() { return { &storage[begin], (size_t)(nextBegin - begin) }; }
+
+ int mTextOffset;
+ int mDocumentOffset;
+ int mUserInputOffset;
+ int mSystemInputNodes;
+ int mOutputOffset;
+
+ SUB_RANGE_ACCESS(Candidate, GetNumericNodes, mCandidates, 0, mTextOffset);
+ SUB_RANGE_ACCESS(Candidate, GetTextNodes, mCandidates, mTextOffset, mDocumentOffset);
+ SUB_RANGE_ACCESS(Candidate, GetDocumentNodes, mCandidates, mDocumentOffset, mUserInputOffset);
+ SUB_RANGE_ACCESS(Candidate, GetUserInputNodes, mCandidates, mUserInputOffset, mSystemInputNodes);
+ SUB_RANGE_ACCESS(Candidate, GetSystemInputNodes, mCandidates, mSystemInputNodes, mOutputOffset);
+ SUB_RANGE_ACCESS(Candidate, GetOutputNodes, mCandidates, mOutputOffset, mCandidates.size());
+
+#undef SUB_RANGE_ACCESS
+
public:
WorkflowCreationMenu() {
SetupCandidates();
@@ -26,41 +60,67 @@ public:
private:
void SetupCandidates() {
+ // Numeric nodes offset start at 0
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Addition); },
+ .Name = "Add",
+ .Category = NumericCategory,
});
-
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); },
+ .Name = "Subtract",
+ .Category = NumericCategory,
});
-
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); },
+ .Name = "Multiply",
+ .Category = NumericCategory,
});
-
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); },
+ .Name = "Divide",
+ .Category = NumericCategory,
+ });
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericExpressionNode>(); },
+ .Name = "Evaluate expression",
+ .Category = NumericCategory,
});
-// mCandidates.push_back(Candidate{
-// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericExpressionNode>(); },
-// });
-
+ mTextOffset = mCandidates.size();
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<TextFormatterNode>(); },
+ .Name = "Fill template text",
+ .Category = TextCategory,
+ });
+
+ mDocumentOffset = mCandidates.size();
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DocumentTemplateExpansionNode>(); },
+ .Name = "Document template",
+ .Category = Category::DocumentsCategory,
+ });
+
+ /* Inputs */
+
+ mUserInputOffset = mCandidates.size();
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<FormInputNode>(); },
+ .Name = "Input: form",
+ .Category = Category::UserInputCategory,
+ });
+
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DatabaseRowsInputNode>(); },
+ .Name = "Input: database rows",
+ .Category = Category::UserInputCategory,
});
-// mCandidates.push_back(Candidate{
-// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DocumentTemplateExpansionNode>(); },
-// });
+ mSystemInputNodes = mCandidates.size();
-// mCandidates.push_back(Candidate{
-// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<FormInputNode>(); },
-// });
+ /* Outputs */
-// mCandidates.push_back(Candidate{
-// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DatabaseRowsInputNode>(); },
-// });
+ mOutputOffset = mCandidates.size();
}
};