aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/Entrypoint/main.cpp2
-rw-r--r--core/src/Model/EvaluatedValue.cpp49
-rw-r--r--core/src/Model/EvaluatedValue.hpp64
-rw-r--r--core/src/Model/Workflow.cpp98
-rw-r--r--core/src/Model/Workflow.hpp101
-rw-r--r--core/src/Model/WorkflowSteps.cpp11
-rw-r--r--core/src/Model/WorkflowSteps.hpp44
-rw-r--r--core/src/Model/fwd.hpp20
-rw-r--r--core/src/UI/Localization.hpp1
-rw-r--r--core/src/UI/UI.hpp2
-rw-r--r--core/src/UI/UI_DatabaseView.cpp18
-rw-r--r--core/src/UI/UI_Items.cpp2
-rw-r--r--core/src/UI/UI_MainWindow.cpp5
-rw-r--r--core/src/UI/UI_Workflows.cpp (renamed from core/src/UI/UI_Export.cpp)2
-rw-r--r--core/src/Utils/Sigslot.hpp4
15 files changed, 412 insertions, 11 deletions
diff --git a/core/src/Entrypoint/main.cpp b/core/src/Entrypoint/main.cpp
index 3016467..8f9832c 100644
--- a/core/src/Entrypoint/main.cpp
+++ b/core/src/Entrypoint/main.cpp
@@ -130,7 +130,7 @@ int main(int argc, char* argv[]) {
// Configure default fonts
{
// Includes latin alphabet, although for some reason smaller than if rendered using 18 point NotoSans regular
- io.Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.otf", 18, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
+ io.Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.otf", 18, nullptr, io.Fonts->GetGlyphRangesChineseFull());
ImWchar iconRanges[] = { ICON_MIN_FA, ICON_MAX_FA };
ImFontConfig config;
diff --git a/core/src/Model/EvaluatedValue.cpp b/core/src/Model/EvaluatedValue.cpp
new file mode 100644
index 0000000..c86f00b
--- /dev/null
+++ b/core/src/Model/EvaluatedValue.cpp
@@ -0,0 +1,49 @@
+#include "EvaluatedValue.hpp"
+
+BaseValue::BaseValue(Type type)
+ : mType{ type } {
+}
+
+BaseValue::Type BaseValue::GetType() const {
+ return mType;
+}
+
+NumericValue::NumericValue()
+ : BaseValue(BaseValue::NumericType) {
+}
+
+int64_t NumericValue::GetInt() const {
+ return static_cast<int64_t>(mValue);
+}
+
+double NumericValue::GetValue() const {
+ return mValue;
+}
+
+void NumericValue::SetValue(double value) {
+ mValue = value;
+}
+
+TextValue::TextValue()
+ : BaseValue(BaseValue::TextType) {
+}
+
+const std::string& TextValue::GetValue() const {
+ return mValue;
+}
+
+void TextValue::SetValue(const std::string& value) {
+ mValue = value;
+}
+
+DateTimeValue::DateTimeValue()
+ : BaseValue(BaseValue::DateTimeType) {
+}
+
+const std::chrono::time_point<std::chrono::system_clock>& DateTimeValue::GetValue() const {
+ return mValue;
+}
+
+void DateTimeValue::SetValue(const std::chrono::time_point<std::chrono::system_clock>& value) {
+ mValue = value;
+}
diff --git a/core/src/Model/EvaluatedValue.hpp b/core/src/Model/EvaluatedValue.hpp
new file mode 100644
index 0000000..838fc39
--- /dev/null
+++ b/core/src/Model/EvaluatedValue.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+#include <string>
+
+class BaseValue {
+public:
+ enum Type {
+ NumericType,
+ TextType,
+ DateTimeType,
+
+ /// An unspecified type, otherwise known as "any" in some contexts.
+ InvalidType,
+ TypeCount = InvalidType,
+ };
+
+private:
+ Type mType;
+
+public:
+ BaseValue(Type type);
+ virtual ~BaseValue() = default;
+
+ BaseValue(const BaseValue&) = delete;
+ BaseValue& operator=(const BaseValue&) = delete;
+ BaseValue(BaseValue&&) = default;
+ BaseValue& operator=(BaseValue&&) = default;
+
+ Type GetType() const;
+};
+
+class NumericValue : public BaseValue {
+private:
+ double mValue;
+
+public:
+ NumericValue();
+
+ int64_t GetInt() const;
+ double GetValue() const;
+ void SetValue(double value);
+};
+
+class TextValue : public BaseValue {
+private:
+ std::string mValue;
+
+public:
+ TextValue();
+ const std::string& GetValue() const;
+ void SetValue(const std::string& value);
+};
+
+class DateTimeValue : public BaseValue {
+private:
+ std::chrono::time_point<std::chrono::system_clock> mValue;
+
+public:
+ DateTimeValue();
+ const std::chrono::time_point<std::chrono::system_clock>& GetValue() const;
+ void SetValue(const std::chrono::time_point<std::chrono::system_clock>& value);
+};
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp
new file mode 100644
index 0000000..96031c1
--- /dev/null
+++ b/core/src/Model/Workflow.cpp
@@ -0,0 +1,98 @@
+#include "Workflow.hpp"
+
+bool WorkflowStep::InputNode::IsConnected() const {
+ return ConnectedNodeIndex != -1;
+}
+
+bool WorkflowStep::OutputNode::IsConnected() const {
+ return ConnectedNodeIndex != -1;
+}
+
+size_t WorkflowStep::GetId() const {
+ return mId;
+}
+
+void WorkflowStep::ConnectInput(int nodeId, size_t outputId, int outputNodeId) {
+ mWorkflow->Connect(mId, nodeId, outputId, outputNodeId);
+}
+
+void WorkflowStep::DisconnectInput(int nodeId) {
+ mWorkflow->DisconnectByDestination(mId, nodeId);
+}
+
+bool WorkflowStep::IsInputConnected(int nodeId) const {
+ return mInputs[nodeId].IsConnected();
+}
+
+void WorkflowStep::ConnectOutput(int nodeId, size_t inputId, int inputNodeId) {
+ mWorkflow->Connect(inputId, inputNodeId, mId, nodeId);
+}
+
+void WorkflowStep::DisconnectOutput(int nodeId) {
+ mWorkflow->DisconnectBySource(mId, nodeId);
+}
+
+bool WorkflowStep::IsOutputConnected(int nodeId) const {
+ return mOutputs[nodeId].IsConnected();
+}
+
+WorkflowConnection* Workflow::GetConnectionById(size_t id) {
+ return &mConnections[id];
+}
+
+WorkflowStep* Workflow::GetStepById(size_t id) {
+ return mSteps[id].get();
+}
+
+void WorkflowStep::OnIOAboutToChange() {
+ // TODO more robust solution that handles changes incrementally
+ for (size_t i = 0; i < mInputs.size(); ++i) {
+ DisconnectInput(i);
+ }
+ for (size_t i = 0; i < mOutputs.size(); ++i) {
+ DisconnectOutput(i);
+ }
+}
+
+void WorkflowStep::OnIOChanged() {
+}
+
+void Workflow::Connect(size_t source, int sourceNode, size_t destination, int destinationNode) {
+ auto& src = *mSteps[source];
+ auto& o = src.mInputs[sourceNode];
+ if (o.IsConnected()) {
+ DisconnectBySource(source, sourceNode);
+ }
+
+ o.ConnectedStep = destination;
+ o.ConnectedNodeIndex = destinationNode;
+
+ auto& dst = *mSteps[destination];
+ auto& i = dst.mInputs[destinationNode];
+ i.ConnectedStep = source;
+ i.ConnectedNodeIndex = sourceNode;
+}
+
+void Workflow::DisconnectBySource(size_t source, int sourceNode) {
+ auto& src = *mSteps[source];
+ auto& o = src.mOutputs[sourceNode];
+ if (!o.IsConnected()) return;
+
+ auto& i = mSteps[o.ConnectedStep]->mInputs[o.ConnectedNodeIndex];
+ i.ConnectedStep = WorkflowStep::kInvalidId;
+ i.ConnectedNodeIndex = -1;
+ o.ConnectedStep = WorkflowStep::kInvalidId;
+ o.ConnectedNodeIndex = -1;
+}
+
+void Workflow::DisconnectByDestination(size_t destination, int destinationNode) {
+ auto& dst = *mSteps[destination];
+ auto& i = dst.mInputs[destinationNode];
+ if (!i.IsConnected()) return;
+
+ auto& o = mSteps[i.ConnectedStep]->mOutputs[i.ConnectedNodeIndex];
+ i.ConnectedStep = WorkflowStep::kInvalidId;
+ i.ConnectedNodeIndex = -1;
+ o.ConnectedStep = WorkflowStep::kInvalidId;
+ o.ConnectedNodeIndex = -1;
+}
diff --git a/core/src/Model/Workflow.hpp b/core/src/Model/Workflow.hpp
new file mode 100644
index 0000000..a90ef21
--- /dev/null
+++ b/core/src/Model/Workflow.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "EvaluatedValue.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <vector>
+
+class WorkflowConnection {
+public:
+ size_t Source;
+ size_t Destination;
+};
+
+class WorkflowStep {
+public:
+ static constexpr auto kInvalidId = std::numeric_limits<size_t>::max();
+
+ // TODO do we even need this?
+ // enum Type {
+ // NumericAdditionType,
+ // NumericSubtractionType,
+ // NumericMultiplicationType,
+ // NumericDivisionType,
+ // NumericExpressionType,
+ // TextFormattingType,
+ // FormInputType,
+ // DatabaseRowsInputType,
+ // DocumentTemplateExpansionType,
+ //
+ // InvalidType,
+ // TypeCount = InvalidType,
+ // };
+
+protected:
+ struct InputNode {
+ size_t ConnectedStep = kInvalidId;
+ int ConnectedNodeIndex = -1;
+ BaseValue::Type MatchingType = BaseValue::InvalidType;
+
+ bool IsConnected() const;
+ };
+
+ struct OutputNode {
+ size_t ConnectedStep = kInvalidId;
+ int ConnectedNodeIndex = -1;
+ BaseValue::Type MatchingType = BaseValue::InvalidType;
+
+ bool IsConnected() const;
+ };
+
+ friend class Workflow;
+ Workflow* mWorkflow;
+ size_t mId;
+ std::vector<InputNode> mInputs;
+ std::vector<OutputNode> mOutputs;
+
+public:
+ virtual ~WorkflowStep() = default;
+
+ WorkflowStep(const WorkflowStep&) = delete;
+ WorkflowStep& operator=(const WorkflowStep&) = delete;
+ WorkflowStep(WorkflowStep&&) = default;
+ WorkflowStep& operator=(WorkflowStep&&) = default;
+
+ size_t GetId() const;
+
+ void ConnectInput(int nodeId, size_t outputId, int outputNodeId);
+ void DisconnectInput(int nodeId);
+ bool IsInputConnected(int nodeId) const;
+
+ void ConnectOutput(int nodeId, size_t inputId, int inputNodeId);
+ void DisconnectOutput(int nodeId);
+ bool IsOutputConnected(int nodeId) const;
+
+protected:
+ /// Child classes should call this whenever \c mInputs and \c mOutputs are about to be modified (append or remove)
+ /// after invocation of the constructor.
+ void OnIOAboutToChange();
+ void OnIOChanged();
+};
+
+class Workflow {
+private:
+ std::vector<WorkflowConnection> mConnections;
+ std::vector<std::unique_ptr<WorkflowStep>> mSteps;
+
+public:
+ WorkflowConnection* GetConnectionById(size_t id);
+ WorkflowStep* GetStepById(size_t id);
+
+ void Connect(size_t source, int sourceNode, size_t destination, int destinationNode);
+ void DisconnectBySource(size_t source, int sourceNode);
+ void DisconnectByDestination(size_t destination, int destinationNode);
+};
+
+class WorkflowEvaluationContext {
+
+};
diff --git a/core/src/Model/WorkflowSteps.cpp b/core/src/Model/WorkflowSteps.cpp
new file mode 100644
index 0000000..815c5ca
--- /dev/null
+++ b/core/src/Model/WorkflowSteps.cpp
@@ -0,0 +1,11 @@
+#include "WorkflowSteps.hpp"
+
+NumericOperationStep::NumericOperationStep(NumericOperationStep::Type type)
+ : mType{ type } {
+ mInputs.resize(2);
+ mInputs[0].MatchingType = BaseValue::NumericType;
+ mInputs[1].MatchingType = BaseValue::NumericType;
+
+ mOutputs.resize(1);
+ mOutputs[0].MatchingType = BaseValue::NumericType;
+}
diff --git a/core/src/Model/WorkflowSteps.hpp b/core/src/Model/WorkflowSteps.hpp
new file mode 100644
index 0000000..819ca63
--- /dev/null
+++ b/core/src/Model/WorkflowSteps.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "Model/Workflow.hpp"
+
+#include <memory>
+
+class NumericOperationStep : public WorkflowStep {
+public:
+ enum Type {
+ Addition,
+ Subtraction,
+ Multiplication,
+ Division,
+
+ InvalidType,
+ TypeCount = InvalidType,
+ };
+
+private:
+ Type mType;
+
+public:
+ NumericOperationStep(Type type);
+};
+
+class NumericExpressionStep : public WorkflowStep {
+public:
+};
+
+class FormatTextStep : public WorkflowStep {
+public:
+};
+
+class FormInputStep : public WorkflowStep {
+public:
+};
+
+class DatabaseRowsInputStep : public WorkflowStep {
+public:
+};
+
+class DocumentTemplateExpansionStep : public WorkflowStep {
+public:
+};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index 4bd508c..a2156aa 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -1,5 +1,11 @@
#pragma once
+// EvaluatedValue.hpp
+class BaseValue;
+class NumericValue;
+class TextValue;
+class DateTimeValue;
+
// Filter.hpp
class TableRowsFilter;
@@ -23,3 +29,17 @@ class SalesTable;
class PurchasesTable;
class DeliveryTable;
class TransactionModel;
+
+// Workflow.hpp
+class WorkflowConnection;
+class WorkflowStep;
+class Workflow;
+class WorkflowEvaluationContext;
+
+// WorkflowSteps.hpp
+class NumericOperationStep;
+class NumericExpressionStep;
+class FormatTextStep;
+class FormInputStep;
+class DatabaseRowsInputStep;
+class DocumentTemplateExpansionStep;
diff --git a/core/src/UI/Localization.hpp b/core/src/UI/Localization.hpp
index f476458..3f09fb2 100644
--- a/core/src/UI/Localization.hpp
+++ b/core/src/UI/Localization.hpp
@@ -27,6 +27,7 @@ public:
BasicTranslation ProjectTab{ "MainWindow.Tab.Project"sv };
BasicTranslation DatabaseViewTab{ "MainWindow.Tab.DatabaseView"sv };
BasicTranslation ItemsTab{ "MainWindow.Tab.Items"sv };
+ BasicTranslation WorkflowsTab{ "MainWindow.Tab.Workflows"sv };
/* Project tab */
diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp
index ab35321..9e97118 100644
--- a/core/src/UI/UI.hpp
+++ b/core/src/UI/UI.hpp
@@ -27,6 +27,6 @@ void MainWindow();
void SettingsTab();
void DatabaseViewTab();
void ItemsTab();
-void ExportTab();
+void WorkflowsTab();
} // namespace UI
diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp
index 8791c5b..884ab51 100644
--- a/core/src/UI/UI_DatabaseView.cpp
+++ b/core/src/UI/UI_DatabaseView.cpp
@@ -10,6 +10,7 @@
#include <IconsFontAwesome.h>
#include <SQLiteCpp/Statement.h>
#include <imgui.h>
+#include <chrono>
#include <cstdint>
#include <ctime>
#include <memory>
@@ -196,7 +197,7 @@ private:
void UpdateLastPage() {
mLastPage = mActiveEntries.empty()
- ? 0
+ ? 0 // TODO calc page
: CalcPageForRowId(mActiveEntries.back());
}
@@ -258,10 +259,17 @@ private:
return "";
}
- auto t = static_cast<time_t>(epoch);
- std::string str(29, '\0');
- std::strftime(str.data(), 21, "%Y-%m-%dT%H:%M:%S.", std::localtime(&t));
- return str;
+ namespace chrono = std::chrono;
+ using Clock = chrono::system_clock;
+
+ chrono::milliseconds d{ epoch };
+ chrono::time_point<chrono::system_clock> tp{ d };
+ auto t = chrono::system_clock::to_time_t(tp);
+
+ char data[32];
+ std::strftime(data, sizeof(data), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
+
+ return std::string(data);
};
int customerCol = stmt.getColumnIndex("Customer");
diff --git a/core/src/UI/UI_Items.cpp b/core/src/UI/UI_Items.cpp
index a094f76..56d6c2a 100644
--- a/core/src/UI/UI_Items.cpp
+++ b/core/src/UI/UI_Items.cpp
@@ -93,7 +93,7 @@ void ItemListEntries(ItemList<T>& list, int& selectedIdx) {
constexpr bool kHasEmail = requires(T t) { t.GetEmail(); };
constexpr bool kHasStock = requires(T t) { t.GetPrice(); };
constexpr bool kHasPrice = requires(T t) { t.GetPrice(); };
- constexpr int kColumns = 1 /* Name column */ + kHasDescription + kHasEmail;
+ constexpr int kColumns = 1 /* Name column */ + kHasDescription + kHasEmail + kHasStock + kHasPrice;
auto ls = LocaleStrings::Instance.get();
auto& uis = UIState::GetInstance();
diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp
index 30505d6..f1d1e5e 100644
--- a/core/src/UI/UI_MainWindow.cpp
+++ b/core/src/UI/UI_MainWindow.cpp
@@ -223,6 +223,11 @@ void UI::MainWindow() {
ImGui::EndTabItem();
}
+ if (ImGui::BeginTabItem()) {
+ UI::WorkflowsTab();
+ ImGui::EndTabItem();
+ }
+
endTab:
ImGui::EndTabBar();
}
diff --git a/core/src/UI/UI_Export.cpp b/core/src/UI/UI_Workflows.cpp
index 06b49f5..2b0368e 100644
--- a/core/src/UI/UI_Export.cpp
+++ b/core/src/UI/UI_Workflows.cpp
@@ -4,6 +4,6 @@
#include <imgui.h>
-void UI::ExportTab() {
+void UI::WorkflowsTab() {
// TODO
}
diff --git a/core/src/Utils/Sigslot.hpp b/core/src/Utils/Sigslot.hpp
index 2751d9a..2a191b4 100644
--- a/core/src/Utils/Sigslot.hpp
+++ b/core/src/Utils/Sigslot.hpp
@@ -135,7 +135,7 @@ public:
SlotGuard(SlotGuard&&) = default;
SlotGuard& operator=(SlotGuard&&) = default;
- /// Disconnect all connection associated with this SlotGuard.
+ /// DisconnectBySource all connection associated with this SlotGuard.
void DisconnectAll();
private:
@@ -144,7 +144,7 @@ private:
/// Remove the connection data in this associated with slotId. This does not invoke
/// the connections' stub's RemoveConnection function.
void RemoveConnection(int slotId);
- /// Disconnect all connections from the given stub associated with this SlotGuard.
+ /// DisconnectBySource all connections from the given stub associated with this SlotGuard.
/// Implementation for SignalStub::RemoveConnectionsFor(SlotGuard&)
void RemoveConnectionFor(SignalStub& stub);
};