aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/locale/zh_CN.json1
-rw-r--r--core/src/Model/GlobalStates.cpp10
-rw-r--r--core/src/Model/GlobalStates.hpp4
-rw-r--r--core/src/Model/Project.cpp106
-rw-r--r--core/src/Model/Project.hpp17
-rw-r--r--core/src/Model/Workflow/Workflow.hpp16
-rw-r--r--core/src/Model/Workflow/Workflow_Main.cpp14
-rw-r--r--core/src/Model/Workflow/fwd.hpp11
-rw-r--r--core/src/UI/Localization.hpp1
-rw-r--r--core/src/UI/UI_DatabaseView.cpp8
-rw-r--r--core/src/UI/UI_Workflows.cpp191
11 files changed, 311 insertions, 68 deletions
diff --git a/core/locale/zh_CN.json b/core/locale/zh_CN.json
index 31f8827..e5df7ee 100644
--- a/core/locale/zh_CN.json
+++ b/core/locale/zh_CN.json
@@ -4,6 +4,7 @@
"Generic.Add": "\uf067 新建",
"Generic.Edit": "\uf044 编辑",
"Generic.Delete": "\uf1f8 删除",
+ "Generic.Rename": "\uf246 重命名",
"Generic.Disconnect": "\uf127 断开连接",
"Generic.Close": "\uf00d 关闭",
"Generic.Dialog.Confirm": "确定",
diff --git a/core/src/Model/GlobalStates.cpp b/core/src/Model/GlobalStates.cpp
index 4004f4a..a9b6806 100644
--- a/core/src/Model/GlobalStates.cpp
+++ b/core/src/Model/GlobalStates.cpp
@@ -45,8 +45,8 @@ void GlobalStates::Init(std::filesystem::path userDataDir)
auto utf8String = path.string();
globalStateInstance->mRecentProjects.push_back(RecentProject{
- .path = std::move(path),
- .cachedUtf8String = std::move(utf8String),
+ .Path = std::move(path),
+ .CachedUtf8String = std::move(utf8String),
});
}
}
@@ -85,8 +85,8 @@ void GlobalStates::ClearRecentProjects()
void GlobalStates::AddRecentProject(const Project& project)
{
mRecentProjects.push_back(RecentProject{
- .path = project.GetPath(),
- .cachedUtf8String = project.GetPath().string(),
+ .Path = project.GetPath(),
+ .CachedUtf8String = project.GetPath().string(),
});
MarkDirty();
}
@@ -94,7 +94,7 @@ void GlobalStates::AddRecentProject(const Project& project)
void GlobalStates::MoveProjectToTop(const Project& project)
{
for (auto it = mRecentProjects.begin(); it != mRecentProjects.end(); ++it) {
- if (it->path == project.GetPath()) {
+ if (it->Path == project.GetPath()) {
std::rotate(it, it + 1, mRecentProjects.end());
MarkDirty();
return;
diff --git a/core/src/Model/GlobalStates.hpp b/core/src/Model/GlobalStates.hpp
index d88f752..6970642 100644
--- a/core/src/Model/GlobalStates.hpp
+++ b/core/src/Model/GlobalStates.hpp
@@ -19,8 +19,8 @@ public:
struct RecentProject
{
- std::filesystem::path path;
- std::string cachedUtf8String;
+ std::filesystem::path Path;
+ std::string CachedUtf8String;
};
public:
diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp
index 74e7142..2d7c82a 100644
--- a/core/src/Model/Project.cpp
+++ b/core/src/Model/Project.cpp
@@ -1,5 +1,8 @@
#include "Project.hpp"
+#include "Model/Workflow/Workflow.hpp"
+#include "Utils/Macros.hpp"
+
#include <json/reader.h>
#include <json/value.h>
#include <json/writer.h>
@@ -25,7 +28,7 @@ void ReadItemList(ItemList<T>& list, const fs::path& filePath)
Project::Project(const fs::path& rootPath)
: mRootPath{ rootPath }
, mRootPathString{ mRootPath.string() }
- , mDb(*this)
+ , Database(*this)
{
// TODO better diagnostic
const char* kInvalidFormatErr = "Failed to load project: invalid format.";
@@ -59,13 +62,30 @@ Project::Project(const fs::path& rootPath)
ReadItemList(Products, itemsDir / "products.json");
ReadItemList(Factories, itemsDir / "factories.json");
ReadItemList(Customers, itemsDir / "customers.json");
+
+ auto workflowsDir = mRootPath / "workflows";
+ fs::create_directories(workflowsDir);
+
+ for (auto& entry : fs::directory_iterator(workflowsDir)) {
+ if (!entry.is_regular_file()) continue;
+ auto& path = entry.path();
+ if (path.extension() != ".cplt-workflow") continue;
+
+ auto name = path.stem().string();
+ auto [it, DISCARD] = mWorkflows.insert(name, WorkflowInfo{});
+ auto& info = it.value();
+
+ info.Name = std::move(name);
+ info.PathStringCache = path.string();
+ info.Path = path;
+ }
}
Project::Project(std::filesystem::path rootPath, std::string name)
: mRootPath{ std::move(rootPath) }
, mRootPathString{ mRootPath.string() }
, mName{ std::move(name) }
- , mDb(*this)
+ , Database(*this)
{
}
@@ -89,14 +109,68 @@ void Project::SetName(std::string name)
mName = std::move(name);
}
-const TransactionModel& Project::GetTransactionsModel() const
+const decltype(Project::mWorkflows)& Project::GetWorkflows() const
+{
+ return mWorkflows;
+}
+
+std::unique_ptr<Workflow> Project::LoadWorkflow(std::string_view name)
+{
+ auto iter = mWorkflows.find(name);
+ if (iter == mWorkflows.end()) {
+ return iter.value().LoadFromDisk();
+ } else {
+ return nullptr;
+ }
+}
+
+std::unique_ptr<Workflow> Project::CreateWorkflow(std::string_view name)
{
- return mDb;
+ if (mWorkflows.find(name) != mWorkflows.end()) {
+ // Workflow with name already exists
+ return nullptr;
+ }
+
+ auto workflow = std::make_unique<Workflow>();
+ auto [it, DISCARD] = mWorkflows.insert(name, WorkflowInfo{});
+ auto& info = it.value();
+
+ info.Name = name;
+ info.Path = GetWorkflowPath(name);
+
+ return workflow;
}
-TransactionModel& Project::GetTransactionsModel()
+bool Project::RemoveWorkflow(std::string_view name)
{
- return mDb;
+ auto iter = mWorkflows.find(name);
+ if (iter == mWorkflows.end()) {
+ return false;
+ }
+ auto& info = iter.value();
+
+ fs::remove(info.Path);
+ mWorkflows.erase(iter);
+
+ return true;
+}
+
+bool Project::RenameWorkflow(std::string_view name, std::string_view newName)
+{
+ auto iter = mWorkflows.find(name);
+ if (iter == mWorkflows.end()) return false;
+
+ auto info = std::move(iter.value());
+
+ auto& oldPath = info.Path;
+ auto newPath = GetWorkflowPath(newName);
+ fs::rename(oldPath, newPath);
+ info.Path = std::move(newPath);
+
+ mWorkflows.insert(newName, std::move(info));
+ mWorkflows.erase(iter);
+
+ return true;
}
Json::Value Project::Serialize()
@@ -127,3 +201,23 @@ void Project::WriteToDisk()
WriteItemList(Factories, itemsDir / "factories.json");
WriteItemList(Customers, itemsDir / "customers.json");
}
+
+std::filesystem::path Project::GetDatabasesDirectory() const
+{
+ return mRootPath / "databases";
+}
+
+std::filesystem::path Project::GetItemsDirectory() const
+{
+ return mRootPath / "items";
+}
+
+std::filesystem::path Project::GetWorkflowsDirectory() const
+{
+ return mRootPath / "workflows";
+}
+
+std::filesystem::path Project::GetWorkflowPath(std::string_view name) const
+{
+ return (mRootPath / "workflows" / name).concat(".cplt-workflow");
+}
diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp
index 998befb..5f26532 100644
--- a/core/src/Model/Project.hpp
+++ b/core/src/Model/Project.hpp
@@ -4,8 +4,10 @@
#include "Model/TransactionsModel.hpp"
#include <json/forwards.h>
+#include <tsl/array_map.h>
#include <filesystem>
#include <string>
+#include <string_view>
class Project
{
@@ -13,12 +15,13 @@ public:
ItemList<ProductItem> Products;
ItemList<FactoryItem> Factories;
ItemList<CustomerItem> Customers;
+ TransactionModel Database;
private:
+ tsl::array_map<char, WorkflowInfo> mWorkflows;
std::filesystem::path mRootPath;
std::string mRootPathString;
std::string mName;
- TransactionModel mDb;
public:
/// Load the project from a directory containing the cplt_project.json file.
@@ -32,11 +35,19 @@ public:
const std::filesystem::path& GetPath() const;
const std::string& GetPathString() const;
+ std::filesystem::path GetDatabasesDirectory() const;
+ std::filesystem::path GetItemsDirectory() const;
+ std::filesystem::path GetWorkflowsDirectory() const;
+ std::filesystem::path GetWorkflowPath(std::string_view name) const;
+
const std::string& GetName() const;
void SetName(std::string name);
- const TransactionModel& GetTransactionsModel() const;
- TransactionModel& GetTransactionsModel();
+ const decltype(mWorkflows)& GetWorkflows() const;
+ std::unique_ptr<Workflow> LoadWorkflow(std::string_view name);
+ std::unique_ptr<Workflow> CreateWorkflow(std::string_view name);
+ bool RemoveWorkflow(std::string_view name);
+ bool RenameWorkflow(std::string_view name, std::string_view newName);
Json::Value Serialize();
void WriteToDisk();
diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp
index 0aabcdc..99e4e90 100644
--- a/core/src/Model/Workflow/Workflow.hpp
+++ b/core/src/Model/Workflow/Workflow.hpp
@@ -7,6 +7,7 @@
#include <imgui_node_editor.h>
#include <cstddef>
#include <cstdint>
+#include <filesystem>
#include <iosfwd>
#include <limits>
#include <memory>
@@ -135,14 +136,14 @@ public:
void DisconnectInput(uint32_t pinId);
void DrawInputPinDebugInfo(uint32_t pinId) const;
- const InputPin& GetInputPin(uint32_t pinId)const ;
+ const InputPin& GetInputPin(uint32_t pinId) const;
ImNodes::PinId GetInputPinUniqueId(uint32_t pinId) const;
void ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId);
void DisconnectOutput(uint32_t pinId);
- void DrawOutputPinDebugInfo(uint32_t pinId)const;
- const OutputPin& GetOutputPin(uint32_t pinId)const ;
+ void DrawOutputPinDebugInfo(uint32_t pinId) const;
+ const OutputPin& GetOutputPin(uint32_t pinId) const;
ImNodes::PinId GetOutputPinUniqueId(uint32_t pinId) const;
virtual void Evaluate(WorkflowEvaluationContext& ctx) = 0;
@@ -170,6 +171,15 @@ protected:
void OnDetach();
};
+struct WorkflowInfo
+{
+ std::string Name;
+ std::string PathStringCache = Path.string();
+ std::filesystem::path Path;
+
+ std::unique_ptr<Workflow> LoadFromDisk() const;
+};
+
class Workflow
{
private:
diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp
index c9ae328..bfe007c 100644
--- a/core/src/Model/Workflow/Workflow_Main.cpp
+++ b/core/src/Model/Workflow/Workflow_Main.cpp
@@ -4,6 +4,7 @@
#include <imgui_node_editor.h>
#include <tsl/robin_set.h>
#include <cassert>
+#include <fstream>
#include <iostream>
#include <queue>
#include <utility>
@@ -319,6 +320,19 @@ void WorkflowNode::OnDetach()
{
}
+std::unique_ptr<Workflow> WorkflowInfo::LoadFromDisk() const
+{
+ std::ifstream ifs(this->Path);
+ if (!ifs) return nullptr;
+
+ auto workflow = std::make_unique<Workflow>();
+ if (workflow->ReadFrom(ifs) == Workflow::RR_Success) {
+ return workflow;
+ }
+
+ return nullptr;
+}
+
const std::vector<WorkflowConnection>& Workflow::GetConnections() const
{
return mConnections;
diff --git a/core/src/Model/Workflow/fwd.hpp b/core/src/Model/Workflow/fwd.hpp
index 2323a91..b541e52 100644
--- a/core/src/Model/Workflow/fwd.hpp
+++ b/core/src/Model/Workflow/fwd.hpp
@@ -3,12 +3,19 @@
#include "Model/Workflow/Nodes/fwd.hpp"
#include "Model/Workflow/Values/fwd.hpp"
+// Evaluation.hpp
+class WorkflowEvaluationError;
+class WorkflowEvaluationContext;
+
+// SavedWorkflow.hpp
+class SavedWorkflowCache;
+class SavedWorkflow;
+
// Value.hpp
class BaseValue;
// Workflow.hpp
class WorkflowConnection;
class WorkflowNode;
+struct WorkflowInfo;
class Workflow;
-class WorkflowEvaluationError;
-class WorkflowEvaluationContext;
diff --git a/core/src/UI/Localization.hpp b/core/src/UI/Localization.hpp
index b421d24..c470fa7 100644
--- a/core/src/UI/Localization.hpp
+++ b/core/src/UI/Localization.hpp
@@ -19,6 +19,7 @@ public:
BasicTranslation Add{ "Generic.Add"sv };
BasicTranslation Edit{ "Generic.Edit"sv };
BasicTranslation Delete{ "Generic.Delete"sv };
+ BasicTranslation Rename{ "Generic.Rename"sv };
BasicTranslation Disconnect{ "Generic.Disconnect"sv };
BasicTranslation Close{ "Generic.Close"sv };
BasicTranslation DialogConfirm{ "Generic.Dialog.Confirm"sv };
diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp
index fb7fdfe..73ea657 100644
--- a/core/src/UI/UI_DatabaseView.cpp
+++ b/core/src/UI/UI_DatabaseView.cpp
@@ -468,7 +468,7 @@ private:
case DeliveryDirection::WarehouseToCustomer: outgoingFlag = true; break;
}
- auto& stmt = mProject->GetTransactionsModel().GetDeliveries().FilterByTypeAndId;
+ auto& stmt = mProject->Database.GetDeliveries().FilterByTypeAndId;
// clang-format off
DEFER { stmt.reset(); };
// clang-format on
@@ -482,7 +482,7 @@ private:
int arrivalTimeCol = stmt.getColumnIndex("ArrivalTime");
while (stmt.executeStep()) {
auto items = LoadItems(
- mProject->GetTransactionsModel().GetDeliveries().GetItems,
+ mProject->Database.GetDeliveries().GetItems,
stmt.getColumn(rowIdCol).getInt64());
auto summary = CreateItemsSummary(items);
@@ -609,7 +609,7 @@ public:
#pragma ide diagnostic ignored "HidingNonVirtualFunction"
void OnProjectChanged(Project* newProject)
{
- auto& table = newProject->GetTransactionsModel().GetSales();
+ auto& table = newProject->Database.GetSales();
Statements.GetRowCount = &table.GetRowCount;
Statements.GetRows = &table.GetRows;
Statements.GetItems = &table.GetItems;
@@ -634,7 +634,7 @@ public:
#pragma ide diagnostic ignored "HidingNonVirtualFunction"
void OnProjectChanged(Project* newProject)
{
- auto& table = newProject->GetTransactionsModel().GetPurchases();
+ auto& table = newProject->Database.GetPurchases();
Statements.GetRowCount = &table.GetRowCount;
Statements.GetRows = &table.GetRows;
Statements.GetItems = &table.GetItems;
diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp
index a375ec0..feebe89 100644
--- a/core/src/UI/UI_Workflows.cpp
+++ b/core/src/UI/UI_Workflows.cpp
@@ -1,15 +1,18 @@
#include "UI.hpp"
+#include "Model/Project.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 "UI/States.hpp"
#include "Utils/Macros.hpp"
#include <imgui.h>
#include <imgui_node_editor.h>
+#include <imgui_stdlib.h>
#include <memory>
#include <span>
#include <vector>
@@ -19,12 +22,12 @@ namespace ImNodes = ax::NodeEditor;
namespace {
enum class WorkflowCategory
{
- NumericCategory,
- TextCategory,
- DocumentsCategory,
- UserInputCategory,
- SystemInputCategory,
- OutputCategory,
+ Numeric,
+ Text,
+ Documents,
+ UserInput,
+ SystemInput,
+ Output,
};
class WorkflowDatabase
@@ -42,12 +45,6 @@ public:
private:
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;
@@ -55,57 +52,64 @@ private:
int mOutputOffset;
public:
- 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());
+ // clang-format off
+ std::span<Candidate> GetNumericNodes() { return { &mCandidates[0], (size_t)(mTextOffset - 0) }; };
+ std::span<Candidate> GetTextNodes() { return { &mCandidates[mTextOffset], (size_t)(mDocumentOffset - mTextOffset) }; };
+ std::span<Candidate> GetDocumentNodes() { return { &mCandidates[mDocumentOffset], (size_t)(mUserInputOffset - mDocumentOffset) }; };
+ std::span<Candidate> GetUserInputNodes() { return { &mCandidates[mUserInputOffset], (size_t)(mSystemInputNodes - mUserInputOffset) }; };
+ std::span<Candidate> GetSystemInputNodes() { return { &mCandidates[mSystemInputNodes], (size_t)(mOutputOffset - mSystemInputNodes) }; };
+ std::span<Candidate> GetOutputNodes() { return { &mCandidates[mOutputOffset], (size_t)(mCandidates.size() - mOutputOffset) }; };
+ // clang-format on
-#undef SUB_RANGE_ACCESS
+public:
+ static WorkflowDatabase& GetInstance()
+ {
+ static WorkflowDatabase database;
+ return database;
+ }
public:
- void SetupCandidates()
+ WorkflowDatabase()
{
// 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 = WorkflowCategory::NumericCategory,
+ .Category = WorkflowCategory::Numeric,
});
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); },
.Name = "Subtract",
- .Category = WorkflowCategory::NumericCategory,
+ .Category = WorkflowCategory::Numeric,
});
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); },
.Name = "Multiply",
- .Category = WorkflowCategory::NumericCategory,
+ .Category = WorkflowCategory::Numeric,
});
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); },
.Name = "Divide",
- .Category = WorkflowCategory::NumericCategory,
+ .Category = WorkflowCategory::Numeric,
});
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericExpressionNode>(); },
.Name = "Evaluate expression",
- .Category = WorkflowCategory::NumericCategory,
+ .Category = WorkflowCategory::Numeric,
});
mTextOffset = mCandidates.size();
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<TextFormatterNode>(); },
.Name = "Fill template text",
- .Category = WorkflowCategory::TextCategory,
+ .Category = WorkflowCategory::Text,
});
mDocumentOffset = mCandidates.size();
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DocumentTemplateExpansionNode>(); },
.Name = "Document template",
- .Category = WorkflowCategory::DocumentsCategory,
+ .Category = WorkflowCategory::Documents,
});
/* Inputs */
@@ -114,13 +118,13 @@ public:
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<FormInputNode>(); },
.Name = "Input: form",
- .Category = WorkflowCategory::UserInputCategory,
+ .Category = WorkflowCategory::UserInput,
});
mCandidates.push_back(Candidate{
.Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DatabaseRowsInputNode>(); },
.Name = "Input: database rows",
- .Category = WorkflowCategory::UserInputCategory,
+ .Category = WorkflowCategory::UserInput,
});
mSystemInputNodes = mCandidates.size();
@@ -134,19 +138,21 @@ public:
class WorkflowUI
{
private:
- Workflow* mWorkflow;
+ std::unique_ptr<Workflow> mWorkflow;
+ WorkflowDatabase* mWorkflowDb;
+
ImNodes::EditorContext* mContext;
- WorkflowDatabase mWorkflowDatabase;
ImNodes::NodeId mContextMenuNodeId = 0;
ImNodes::PinId mContextMenuPinId = 0;
ImNodes::LinkId mContextMenuLinkId = 0;
public:
- WorkflowUI()
+ WorkflowUI(std::unique_ptr<Workflow> workflow)
+ : mWorkflow{ std::move(workflow) }
{
+ mWorkflowDb = &WorkflowDatabase::GetInstance();
mContext = ImNodes::CreateEditor();
- mWorkflowDatabase.SetupCandidates();
}
~WorkflowUI()
@@ -333,12 +339,12 @@ public:
}
};
- DisplayCandidatesCategory("Numeric nodes", mWorkflowDatabase.GetNumericNodes());
- DisplayCandidatesCategory("Text nodes", mWorkflowDatabase.GetTextNodes());
- DisplayCandidatesCategory("Document nodes", mWorkflowDatabase.GetDocumentNodes());
- DisplayCandidatesCategory("User input nodes", mWorkflowDatabase.GetUserInputNodes());
- DisplayCandidatesCategory("System input nodes", mWorkflowDatabase.GetSystemInputNodes());
- DisplayCandidatesCategory("Output nodes", mWorkflowDatabase.GetOutputNodes());
+ DisplayCandidatesCategory("Numeric nodes", mWorkflowDb->GetNumericNodes());
+ DisplayCandidatesCategory("Text nodes", mWorkflowDb->GetTextNodes());
+ DisplayCandidatesCategory("Document nodes", mWorkflowDb->GetDocumentNodes());
+ DisplayCandidatesCategory("User input nodes", mWorkflowDb->GetUserInputNodes());
+ DisplayCandidatesCategory("System input nodes", mWorkflowDb->GetSystemInputNodes());
+ DisplayCandidatesCategory("Output nodes", mWorkflowDb->GetOutputNodes());
ImGui::EndPopup();
}
@@ -352,31 +358,130 @@ public:
ImNodes::End();
}
};
+
+struct DrawWorkflowList_State
+{
+ const WorkflowInfo* SelectedWorkflow = nullptr;
+};
+
+void DrawWorkflowList(DrawWorkflowList_State& state)
+{
+ auto& uis = UIState::GetInstance();
+ auto& workflows = uis.CurrentProject->GetWorkflows();
+
+ // TODO sort the list
+ for (auto& info : workflows) {
+ if (ImGui::Selectable(info.Name.c_str(), state.SelectedWorkflow == &info)) {
+ state.SelectedWorkflow = &info;
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("Path: %s", info.PathStringCache.c_str());
+ ImGui::EndTooltip();
+ }
+ }
+}
} // namespace
void UI::WorkflowsTab()
{
auto ls = LocaleStrings::Instance.get();
+ auto& uis = UIState::GetInstance();
+ bool openedDummy = true;
static std::unique_ptr<WorkflowUI> openWorkflow;
+ static DrawWorkflowList_State state;
+ // Toolbar item: close
if (ImGui::Button(ls->Close.Get(), openWorkflow == nullptr)) {
openWorkflow = nullptr;
}
+
+ // Toolbar item: open...
ImGui::SameLine();
if (ImGui::Button(ls->OpenWorkflow.Get())) {
ImGui::OpenPopup(ls->OpenWorkflowDialogTitle.Get());
}
- if (ImGui::BeginPopupModal(ls->OpenWorkflowDialogTitle.Get())) {
- // TODO
+ if (ImGui::BeginPopupModal(ls->OpenWorkflowDialogTitle.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
+ DrawWorkflowList(state);
+
+ if (state.SelectedWorkflow) {
+ auto workflow = state.SelectedWorkflow->LoadFromDisk();
+ openWorkflow = std::make_unique<WorkflowUI>(std::move(workflow));
+ }
+
ImGui::EndPopup();
}
+
+ // Toolbar item: manage...
ImGui::SameLine();
if (ImGui::Button(ls->ManageWorkflows.Get())) {
ImGui::OpenPopup(ls->ManageWorkflowsDialogTitle.Get());
}
- if (ImGui::BeginPopupModal(ls->ManageWorkflowsDialogTitle.Get())) {
- // TODO
+ if (ImGui::BeginPopupModal(ls->ManageWorkflowsDialogTitle.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
+ DrawWorkflowList(state);
+
+ enum class NameSelectionError
+ {
+ None,
+ Duplicated,
+ Empty,
+ };
+ static std::string newName;
+ static NameSelectionError newNameError;
+ if (ImGui::Button(ls->Rename.Get(), state.SelectedWorkflow == nullptr)) {
+ ImGui::OpenPopup("Rename workflow");
+ newName.clear();
+ }
+ if (ImGui::BeginPopupModal("Rename workflow")) {
+ if (ImGui::InputText("New name", &newName)) {
+ if (newName.empty()) {
+ newNameError = NameSelectionError::Empty;
+ }
+
+ auto& workflows = uis.CurrentProject->GetWorkflows();
+ if (workflows.find(newName) != workflows.end()) {
+ newNameError = NameSelectionError::Duplicated;
+ }
+ }
+
+ if (ImGui::Button(ls->DialogConfirm.Get(), newName.empty())) {
+ auto& project = uis.CurrentProject;
+ project->RenameWorkflow(state.SelectedWorkflow->Name, newName);
+ state.SelectedWorkflow = &project->GetWorkflows()[newName];
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+
+ switch (newNameError) {
+ case NameSelectionError::None: break;
+ case NameSelectionError::Duplicated:
+ ImGui::ErrorMessage("Duplicate workflow name");
+ break;
+ case NameSelectionError::Empty:
+ ImGui::ErrorMessage("Workflow name cannot be empty");
+ break;
+ }
+
+ ImGui::EndPopup();
+ }
+
+ if (ImGui::Button(ls->Delete.Get(), state.SelectedWorkflow == nullptr)) {
+ ImGui::OpenPopup("Delete confirmation");
+ }
+ if (ImGui::BeginPopupModal("Delete confirmation")) {
+ if (ImGui::Button(ls->DialogConfirm.Get())) {
+ uis.CurrentProject->RemoveWorkflow(state.SelectedWorkflow->Name);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
+
ImGui::EndPopup();
}