From 00fd95526677d670d002ca81069636f0f74b91f7 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Wed, 28 Apr 2021 15:18:51 -0700 Subject: Code cleanup, fix database view paging and selection --- core/src/Model/TransactionsModel.cpp | 6 +-- core/src/Model/TransactionsModel.hpp | 1 + core/src/Model/Workflow/Nodes/DocumentNodes.cpp | 2 +- core/src/Model/Workflow/Nodes/NumericNodes.cpp | 4 +- core/src/Model/Workflow/Nodes/TextNodes.cpp | 2 +- core/src/Model/Workflow/Nodes/UserInputNodes.cpp | 4 +- core/src/Model/Workflow/Value_RTTI.cpp | 2 +- core/src/Model/Workflow/Values/BasicValues.cpp | 23 ++++++-- core/src/Model/Workflow/Values/BasicValues.hpp | 3 ++ core/src/Model/Workflow/Workflow.hpp | 21 ++++++-- core/src/Model/Workflow/Workflow_Main.cpp | 69 ++++++++++++++++++------ core/src/Model/Workflow/Workflow_RTTI.cpp | 3 +- core/src/UI/UI_DatabaseView.cpp | 22 +++++++- core/src/UI/UI_Items.cpp | 36 +++++-------- core/src/UI/UI_Workflows.cpp | 31 +++++++++++ 15 files changed, 170 insertions(+), 59 deletions(-) (limited to 'core/src') diff --git a/core/src/Model/TransactionsModel.cpp b/core/src/Model/TransactionsModel.cpp index 6035528..968f0c0 100644 --- a/core/src/Model/TransactionsModel.cpp +++ b/core/src/Model/TransactionsModel.cpp @@ -9,9 +9,9 @@ namespace fs = std::filesystem; SalesTable::SalesTable(TransactionModel& db) // language=SQLite - : GetRowsStatement(db.GetSQLite(), R"""( -SELECT * FROM Sales WHERE rowid >= ? AND rowid < ? -)""") + : GetRowCountStatement(db.GetSQLite(), "SELECT Count(*) FROM Sales") + // language=SQLite + , GetRowsStatement(db.GetSQLite(), "SELECT * FROM Sales WHERE rowid >= ? AND rowid < ?") // language=SQLite // TODO , FilterRowsStatement(db.GetSQLite(), R"""( diff --git a/core/src/Model/TransactionsModel.hpp b/core/src/Model/TransactionsModel.hpp index beed663..117c27a 100644 --- a/core/src/Model/TransactionsModel.hpp +++ b/core/src/Model/TransactionsModel.hpp @@ -8,6 +8,7 @@ class SalesTable { public: + SQLite::Statement GetRowCountStatement; SQLite::Statement GetRowsStatement; SQLite::Statement FilterRowsStatement; diff --git a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp index 66d2eae..255d446 100644 --- a/core/src/Model/Workflow/Nodes/DocumentNodes.cpp +++ b/core/src/Model/Workflow/Nodes/DocumentNodes.cpp @@ -8,7 +8,7 @@ bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) { } DocumentTemplateExpansionNode::DocumentTemplateExpansionNode() - : WorkflowNode(TransformType, KD_DocumentTemplateExpansion) { + : WorkflowNode(KD_DocumentTemplateExpansion) { } void DocumentTemplateExpansionNode::Evaluate(WorkflowEvaluationContext& ctx) { diff --git a/core/src/Model/Workflow/Nodes/NumericNodes.cpp b/core/src/Model/Workflow/Nodes/NumericNodes.cpp index 1722224..f054421 100644 --- a/core/src/Model/Workflow/Nodes/NumericNodes.cpp +++ b/core/src/Model/Workflow/Nodes/NumericNodes.cpp @@ -33,7 +33,7 @@ bool NumericOperationNode::IsInstance(const WorkflowNode* node) { } NumericOperationNode::NumericOperationNode(OperationType type) - : WorkflowNode(TransformType, OperationTypeToNodeKind(type)) + : WorkflowNode(OperationTypeToNodeKind(type)) , mType{ type } { mInputs.resize(2); mInputs[0].MatchingType = BaseValue::KD_Numeric; @@ -79,7 +79,7 @@ bool NumericExpressionNode::IsInstance(const WorkflowNode* node) { } NumericExpressionNode::NumericExpressionNode() - : WorkflowNode(TransformType, KD_NumericExpression) { + : WorkflowNode(KD_NumericExpression) { } void NumericExpressionNode::Evaluate(WorkflowEvaluationContext& ctx) { diff --git a/core/src/Model/Workflow/Nodes/TextNodes.cpp b/core/src/Model/Workflow/Nodes/TextNodes.cpp index 3852c66..72bd666 100644 --- a/core/src/Model/Workflow/Nodes/TextNodes.cpp +++ b/core/src/Model/Workflow/Nodes/TextNodes.cpp @@ -49,7 +49,7 @@ bool TextFormatterNode::IsInstance(const WorkflowNode* node) { } TextFormatterNode::TextFormatterNode() - : WorkflowNode(TransformType, KD_TextFormatting) { + : WorkflowNode(KD_TextFormatting) { } int TextFormatterNode::GetElementCount() const { diff --git a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp index f59226a..435cf6d 100644 --- a/core/src/Model/Workflow/Nodes/UserInputNodes.cpp +++ b/core/src/Model/Workflow/Nodes/UserInputNodes.cpp @@ -8,7 +8,7 @@ bool FormInputNode::IsInstance(const WorkflowNode* node) { } FormInputNode::FormInputNode() - : WorkflowNode(InputType, KD_FormInput) { + : WorkflowNode(KD_FormInput) { } void FormInputNode::Evaluate(WorkflowEvaluationContext& ctx) { @@ -19,7 +19,7 @@ bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) { } DatabaseRowsInputNode::DatabaseRowsInputNode() - : WorkflowNode(InputType, KD_DatabaseRowsInput) { + : WorkflowNode(KD_DatabaseRowsInput) { } void DatabaseRowsInputNode::Evaluate(WorkflowEvaluationContext& ctx) { diff --git a/core/src/Model/Workflow/Value_RTTI.cpp b/core/src/Model/Workflow/Value_RTTI.cpp index f2c8f92..2a1ac39 100644 --- a/core/src/Model/Workflow/Value_RTTI.cpp +++ b/core/src/Model/Workflow/Value_RTTI.cpp @@ -17,6 +17,6 @@ std::unique_ptr BaseValue::CreateByKind(BaseValue::Kind kind) { case KD_Numeric: return std::make_unique(); case KD_Text: return std::make_unique(); case KD_DateTime: return std::make_unique(); - case InvalidKind: return nullptr; + case InvalidKind: UNREACHABLE; } } diff --git a/core/src/Model/Workflow/Values/BasicValues.cpp b/core/src/Model/Workflow/Values/BasicValues.cpp index fd70acd..11d30b7 100644 --- a/core/src/Model/Workflow/Values/BasicValues.cpp +++ b/core/src/Model/Workflow/Values/BasicValues.cpp @@ -1,6 +1,7 @@ #include "BasicValues.hpp" #include +#include bool NumericValue::IsInstance(const BaseValue* value) { return value->GetKind() == KD_Numeric; @@ -10,17 +11,31 @@ NumericValue::NumericValue() : BaseValue(BaseValue::KD_Numeric) { } -std::string NumericValue::GetString() const { - char buf[64]; - auto res = std::to_chars(buf, buf + std::size(buf), mValue); +template +static std::string NumberToString(T value) { + constexpr auto kSize = std::numeric_limits::max_digits10; + char buf[kSize]; + + auto res = std::to_chars(buf, buf + kSize, value); if (res.ec == std::errc()) { return std::string(buf, res.ptr); } else { - // TODO larger buffer return ""; } } +std::string NumericValue::GetTruncatedString() const { + return ::NumberToString((int64_t)mValue); +} + +std::string NumericValue::GetRoundedString() const { + return ::NumberToString((int64_t)std::round(mValue)); +} + +std::string NumericValue::GetString() const { + return ::NumberToString(mValue); +} + int64_t NumericValue::GetInt() const { return static_cast(mValue); } diff --git a/core/src/Model/Workflow/Values/BasicValues.hpp b/core/src/Model/Workflow/Values/BasicValues.hpp index a116c8c..e2e990d 100644 --- a/core/src/Model/Workflow/Values/BasicValues.hpp +++ b/core/src/Model/Workflow/Values/BasicValues.hpp @@ -14,7 +14,10 @@ public: static bool IsInstance(const BaseValue* value); NumericValue(); + std::string GetTruncatedString() const; + std::string GetRoundedString() const; std::string GetString() const; + int64_t GetInt() const; double GetValue() const; void SetValue(double value); diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp index db341af..139a96e 100644 --- a/core/src/Model/Workflow/Workflow.hpp +++ b/core/src/Model/Workflow/Workflow.hpp @@ -113,7 +113,7 @@ public: static const char* FormatType(Type type); static std::unique_ptr CreateByKind(Kind kind); - WorkflowNode(Type type, Kind kind); + WorkflowNode(Kind kind); virtual ~WorkflowNode() = default; WorkflowNode(const WorkflowNode&) = delete; @@ -179,10 +179,24 @@ private: bool mDepthsDirty = true; public: + /* Graph access */ + + const std::vector& GetConnections() const; + std::vector& GetConnections(); + const std::vector>& GetNodes() const; + std::vector>& GetNodes(); + const std::vector>& GetConstants() const; + std::vector>& GetConstants(); + WorkflowConnection* GetConnectionById(size_t id); WorkflowNode* GetStepById(size_t id); BaseValue* GetConstantById(size_t id); + const std::vector>& GetDepthGroups() const; + bool DoesDepthNeedsUpdate() const; + + /* Graph mutation */ + void AddStep(std::unique_ptr step); void RemoveStep(size_t id); @@ -192,8 +206,7 @@ public: bool DisconnectBySource(WorkflowNode& sourceNode, int sourcePin); bool DisconnectByDestination(WorkflowNode& destinationNode, int destinationPin); - const std::vector>& GetDepthGroups() const; - bool DoesDepthNeedsUpdate() const; + /* Graph rebuild */ struct GraphUpdate_Success {}; struct GraphUpdate_NoWorkToDo {}; @@ -213,6 +226,8 @@ public: /// When `getInfo == false, the corresponding error code is returned but without/with empty payloads. GraphUpdateResult UpdateGraph(bool getInfo = true); + /* Serialization */ + enum ReadResult { ReadSuccess, ReadInvalidVersion, diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp index c8657e8..84b4557 100644 --- a/core/src/Model/Workflow/Workflow_Main.cpp +++ b/core/src/Model/Workflow/Workflow_Main.cpp @@ -9,6 +9,8 @@ #include #include +namespace ImNodes = ax::NodeEditor; + WorkflowConnection::WorkflowConnection() : MultiConnections{} , SingleConnection{ WorkflowNode::kInvalidId, -1 } @@ -149,9 +151,8 @@ WorkflowConnection::Direction WorkflowNode::OutputPin::GetSupportedDirection() c return AllowsMultipleConnections ? WorkflowConnection::OneToMany : WorkflowConnection::ManyToOne; } -WorkflowNode::WorkflowNode(Type type, Kind kind) - : mType{ type } - , mKind{ kind } +WorkflowNode::WorkflowNode(Kind kind) + : mKind{ kind } , mDepth{ -1 } { } @@ -167,15 +168,15 @@ size_t WorkflowNode::GetId() const { return mId; } -WorkflowNode::Type WorkflowNode::GetType() const { - return mType; +WorkflowNode::Kind WorkflowNode::GetKind() const { + return mKind; } int WorkflowNode::GetDepth() const { return mDepth; } -WorkflowNode::Kind WorkflowNode::GetKind() const { +WorkflowNode::Type WorkflowNode::GetType() const { if (IsInputNode()) { return InputType; } else if (IsOutputNode()) { @@ -218,12 +219,24 @@ bool WorkflowNode::IsOutputConnected(int nodeId) const { } void WorkflowNode::Draw() { + for (size_t i = 0; i < mInputs.size(); ++i) { + auto& pin = mInputs[i]; + ImNodes::BeginPin(i, ImNodes::PinKind::Input); + // TODO + ImNodes::EndPin(); + } + for (size_t i = 0; i < mOutputs.size(); ++i) { + auto& pin = mOutputs[i]; + ImNodes::BeginPin(i + mInputs.size(), ImNodes::PinKind::Output); + // TODO + ImNodes::EndPin(); + } } void WorkflowNode::DrawDebugInfo() const { ImGui::BeginTooltip(); ImGui::Text("Node kind: %s", FormatKind(mKind)); - ImGui::Text("Node type: %s", FormatType(mType)); + ImGui::Text("Node type: %s", FormatType(GetType())); ImGui::Text("Node ID: %llu", mId); ImGui::Text("Depth: %d", mDepth); DrawExtraDebugInfo(); @@ -333,6 +346,30 @@ void WorkflowNode::SwapOutputPin(int a, int b) { std::swap(pinA, pinB); } +const std::vector& Workflow::GetConnections() const { + return mConnections; +} + +std::vector& Workflow::GetConnections() { + return mConnections; +} + +const std::vector>& Workflow::GetNodes() const { + return mNodes; +} + +std::vector>& Workflow::GetNodes() { + return mNodes; +} + +const std::vector>& Workflow::GetConstants() const { + return mConstants; +} + +std::vector>& Workflow::GetConstants() { + return mConstants; +} + WorkflowConnection* Workflow::GetConnectionById(size_t id) { return &mConnections[id]; } @@ -345,6 +382,14 @@ BaseValue* Workflow::GetConstantById(size_t id) { return mConstants[id].get(); } +const std::vector>& Workflow::GetDepthGroups() const { + return mDepthGroups; +} + +bool Workflow::DoesDepthNeedsUpdate() const { + return mDepthsDirty; +} + void Workflow::AddStep(std::unique_ptr step) { auto [storage, id] = AllocWorkflowStep(); storage = std::move(step); @@ -524,14 +569,6 @@ bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destin return true; } -const std::vector>& Workflow::GetDepthGroups() const { - return mDepthGroups; -} - -bool Workflow::DoesDepthNeedsUpdate() const { - return mDepthsDirty; -} - Workflow::GraphUpdateResult Workflow::UpdateGraph(bool getInfo) { if (!mDepthsDirty) { return GraphUpdate_NoWorkToDo{}; @@ -684,7 +721,7 @@ Workflow::ReadResult Workflow::ReadFrom(std::istream& stream) { uint32_t nKind; stream >> nKind; - autp kind = (BaseValue::Kind)nKind; + auto kind = (BaseValue::Kind)nKind; mConstants[idx] = BaseValue::CreateByKind(kind); mConstants[idx]->ReadFrom(stream); diff --git a/core/src/Model/Workflow/Workflow_RTTI.cpp b/core/src/Model/Workflow/Workflow_RTTI.cpp index 0719372..e3fafdb 100644 --- a/core/src/Model/Workflow/Workflow_RTTI.cpp +++ b/core/src/Model/Workflow/Workflow_RTTI.cpp @@ -29,7 +29,6 @@ const char* WorkflowNode::FormatType(Type type) { case InputType: return "input"; case TransformType: return "transform"; case OutputType: return "output"; - default: UNREACHABLE; } } @@ -46,6 +45,6 @@ std::unique_ptr WorkflowNode::CreateByKind(WorkflowNode::Kind kind case KD_FormInput: return std::make_unique(); case KD_DatabaseRowsInput: return std::make_unique(); - case InvalidKind: return nullptr; + case InvalidKind: UNREACHABLE; } } diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp index 884ab51..4009e5b 100644 --- a/core/src/UI/UI_DatabaseView.cpp +++ b/core/src/UI/UI_DatabaseView.cpp @@ -56,6 +56,8 @@ private: /// index by offsetting by \c mFirstCachedRowId. std::vector mActiveEntries; + /// Number of rows in the table. + int mRowCount; /// Last possible page for the current set table and filter (inclusive). int mLastPage; /// The current page the user is on. @@ -71,6 +73,13 @@ public: void OnProjectChanged(Project* newProject) { mProject = newProject; + auto& stmt = newProject->GetTransactionsModel().GetSales().GetRowCountStatement; + if (stmt.executeStep()) { + mRowCount = stmt.getColumn(0).getInt(); + } else { + // TODO report error + } + mFirstCachedRowId = 0; mLastCachedRowId = 0; @@ -116,6 +125,7 @@ public: auto ls = LocaleStrings::Instance.get(); if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPage == 0)) { + mSelectedEntryRowId = -1; SetPage(mCurrentPage - 1); } @@ -125,6 +135,7 @@ public: ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPage == mLastPage)) { + mSelectedEntryRowId = -1; SetPage(mCurrentPage + 1); } @@ -165,6 +176,9 @@ public: void DrawEntry(const SaleEntry& entry, int rowId) { auto ls = LocaleStrings::Instance.get(); + ImGui::PushID(rowId); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntryRowId == rowId, ImGuiSelectableFlags_SpanAllColumns)) { mSelectedEntryRowId = rowId; @@ -179,6 +193,8 @@ public: } else { ImGui::Text("%s", entry.DeliveryTime.c_str()); } + + ImGui::PopID(); } const SaleEntry& GetEntry(int64_t rowId) const { @@ -197,7 +213,7 @@ private: void UpdateLastPage() { mLastPage = mActiveEntries.empty() - ? 0 // TODO calc page + ? CalcPageForRowId(mRowCount) : CalcPageForRowId(mActiveEntries.back()); } @@ -267,7 +283,10 @@ private: auto t = chrono::system_clock::to_time_t(tp); char data[32]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // C++ doesn't have std::localtime_s std::strftime(data, sizeof(data), "%Y-%m-%d %H:%M:%S", std::localtime(&t)); +#pragma clang diagnostic pop return std::string(data); }; @@ -351,7 +370,6 @@ void UI::DatabaseViewTab() { purchasesView.Draw(); ImGui::EndTabItem(); } - ImGui::EndTabBar(); } } diff --git a/core/src/UI/UI_Items.cpp b/core/src/UI/UI_Items.cpp index 56d6c2a..899db6e 100644 --- a/core/src/UI/UI_Items.cpp +++ b/core/src/UI/UI_Items.cpp @@ -220,27 +220,19 @@ void UI::ItemsTab() { auto ls = LocaleStrings::Instance.get(); auto& uis = UIState::GetInstance(); - constexpr float kAmount = 16.0f; - int id = 0; - if (ImGui::CollapsingHeader(ls->ProductCategoryName.Get())) { - ImGui::PushID(id++); - ImGui::Indent(kAmount); - ItemListEditor(uis.CurrentProject->Products); - ImGui::Unindent(kAmount); - ImGui::PopID(); - } - if (ImGui::CollapsingHeader(ls->FactoryCategoryName.Get())) { - ImGui::PushID(id++); - ImGui::Indent(kAmount); - ItemListEditor(uis.CurrentProject->Factories); - ImGui::Unindent(kAmount); - ImGui::PopID(); - } - if (ImGui::CollapsingHeader(ls->CustomerCategoryName.Get())) { - ImGui::PushID(id++); - ImGui::Indent(kAmount); - ItemListEditor(uis.CurrentProject->Customers); - ImGui::Unindent(kAmount); - ImGui::PopID(); + if (ImGui::BeginTabBar("##ItemViewTabs")) { + if (ImGui::BeginTabItem(ls->ProductCategoryName.Get())) { + ItemListEditor(uis.CurrentProject->Products); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem(ls->FactoryCategoryName.Get())) { + ItemListEditor(uis.CurrentProject->Factories); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem(ls->CustomerCategoryName.Get())) { + ItemListEditor(uis.CurrentProject->Customers); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); } } diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp index 0adfdc2..fe504e2 100644 --- a/core/src/UI/UI_Workflows.cpp +++ b/core/src/UI/UI_Workflows.cpp @@ -9,10 +9,13 @@ #include "Utils/Macros.hpp" #include +#include #include #include #include +namespace ImNodes = ax::NodeEditor; + namespace { class WorkflowCreationMenu { private: @@ -127,13 +130,41 @@ private: class WorkflowUI { private: Workflow* mWorkflow; + ImNodes::EditorContext* mContext; public: + WorkflowUI() { + mContext = ImNodes::CreateEditor(); + } + + ~WorkflowUI() { + ImNodes::DestroyEditor(mContext); + } + void Draw() { + ImNodes::SetCurrentEditor(mContext); + ImNodes::Begin(""); + + for (auto& node : mWorkflow->GetNodes()) { + if (!node) continue; + + ImNodes::BeginNode(node->GetId()); + node->Draw(); + ImNodes::EndNode(); + } + + for (auto& conn : mWorkflow->GetConnections()) { + if (!conn.IsValid()) continue; + + // TODO create link + } + + ImNodes::End(); } }; } // namespace void UI::WorkflowsTab() { + static std::unique_ptr openWorkflow; // TODO } -- cgit v1.2.3-70-g09d2