summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-05-30 23:00:41 -0700
committerrtk0c <[email protected]>2021-05-30 23:00:41 -0700
commitc90f78df080a9891930ee346b0ad87498ba5b697 (patch)
treec4f4c475086337e25cbb985625423591c47310e1 /core/src
parent088da97531935a61870ecada10f06b9b9a8255d1 (diff)
Initial work on templates UI
Diffstat (limited to 'core/src')
-rw-r--r--core/src/Entrypoint/main.cpp6
-rw-r--r--core/src/Model/Project.cpp134
-rw-r--r--core/src/Model/Project.hpp22
-rw-r--r--core/src/Model/Template/TableTemplate.cpp63
-rw-r--r--core/src/Model/Template/TableTemplate.hpp12
-rw-r--r--core/src/Model/Template/Template.hpp45
-rw-r--r--core/src/Model/Template/Template_Main.cpp5
-rw-r--r--core/src/Model/Template/Template_RTTI.cpp21
-rw-r--r--core/src/Model/Template/fwd.hpp4
-rw-r--r--core/src/Model/Workflow/Workflow.hpp2
-rw-r--r--core/src/Model/fwd.hpp2
-rw-r--r--core/src/UI/Localization.hpp2
-rw-r--r--core/src/UI/UI.hpp1
-rw-r--r--core/src/UI/UI_MainWindow.cpp13
-rw-r--r--core/src/UI/UI_Templates.cpp289
-rw-r--r--core/src/UI/UI_Workflows.cpp12
16 files changed, 560 insertions, 73 deletions
diff --git a/core/src/Entrypoint/main.cpp b/core/src/Entrypoint/main.cpp
index 2d0238e..152a1c8 100644
--- a/core/src/Entrypoint/main.cpp
+++ b/core/src/Entrypoint/main.cpp
@@ -156,11 +156,7 @@ int main(int argc, char* argv[])
GlobalStates::Init();
} else {
fs::path path(dataDirOption);
- if (fs::exists(path)) {
- GlobalStates::Init(std::move(path));
- } else {
- GlobalStates::Init();
- }
+ GlobalStates::Init(std::move(path));
}
DEFER
{
diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp
index 2d7c82a..3cb3cb4 100644
--- a/core/src/Model/Project.cpp
+++ b/core/src/Model/Project.cpp
@@ -25,19 +25,19 @@ void ReadItemList(ItemList<T>& list, const fs::path& filePath)
}
}
-Project::Project(const fs::path& rootPath)
- : mRootPath{ rootPath }
+Project::Project(fs::path rootPath)
+ : mRootPath{ std::move(rootPath) }
, mRootPathString{ mRootPath.string() }
, Database(*this)
{
// TODO better diagnostic
const char* kInvalidFormatErr = "Failed to load project: invalid format.";
- std::ifstream ifs(rootPath / "cplt_project.json");
+ std::ifstream ifs(mRootPath / "cplt_project.json");
if (!ifs) {
std::string message;
message += "Failed to load project file at '";
- message += rootPath.string();
+ message += mRootPath.string();
message += "'.";
throw std::runtime_error(message);
}
@@ -81,7 +81,7 @@ Project::Project(const fs::path& rootPath)
}
}
-Project::Project(std::filesystem::path rootPath, std::string name)
+Project::Project(fs::path rootPath, std::string name)
: mRootPath{ std::move(rootPath) }
, mRootPathString{ mRootPath.string() }
, mName{ std::move(name) }
@@ -99,6 +99,36 @@ const std::string& Project::GetPathString() const
return mRootPathString;
}
+fs::path Project::GetDatabasesDirectory() const
+{
+ return mRootPath / "databases";
+}
+
+fs::path Project::GetItemsDirectory() const
+{
+ return mRootPath / "items";
+}
+
+fs::path Project::GetWorkflowsDirectory() const
+{
+ return mRootPath / "workflows";
+}
+
+fs::path Project::GetWorkflowPath(std::string_view name) const
+{
+ return (mRootPath / "workflows" / name).concat(".cplt-workflow");
+}
+
+fs::path Project::GetTemplatesDirectory() const
+{
+ return mRootPath / "templates";
+}
+
+fs::path Project::GetTemplatePath(std::string_view name) const
+{
+ return (mRootPath / "templates" / name).concat(".cplt-template");
+}
+
const std::string& Project::GetName() const
{
return mName;
@@ -109,7 +139,7 @@ void Project::SetName(std::string name)
mName = std::move(name);
}
-const decltype(Project::mWorkflows)& Project::GetWorkflows() const
+const tsl::array_map<char, WorkflowInfo>& Project::GetWorkflows() const
{
return mWorkflows;
}
@@ -132,11 +162,12 @@ std::unique_ptr<Workflow> Project::CreateWorkflow(std::string_view name)
}
auto workflow = std::make_unique<Workflow>();
- auto [it, DISCARD] = mWorkflows.insert(name, WorkflowInfo{});
- auto& info = it.value();
-
- info.Name = name;
- info.Path = GetWorkflowPath(name);
+ auto [it, DISCARD] = mWorkflows.insert(
+ name,
+ WorkflowInfo{
+ .Path = GetWorkflowPath(name),
+ .Name = std::string(name),
+ });
return workflow;
}
@@ -155,9 +186,9 @@ bool Project::RemoveWorkflow(std::string_view name)
return true;
}
-bool Project::RenameWorkflow(std::string_view name, std::string_view newName)
+bool Project::RenameWorkflow(std::string_view oldName, std::string_view newName)
{
- auto iter = mWorkflows.find(name);
+ auto iter = mWorkflows.find(oldName);
if (iter == mWorkflows.end()) return false;
auto info = std::move(iter.value());
@@ -173,6 +204,63 @@ bool Project::RenameWorkflow(std::string_view name, std::string_view newName)
return true;
}
+const tsl::array_map<char, TemplateInfo>& Project::GetTemplates() const
+{
+ return mTemplates;
+}
+
+std::unique_ptr<Template> Project::LoadTemplate(std::string_view name)
+{
+ auto iter = mTemplates.find(name);
+ if (iter == mTemplates.end()) {
+ return iter.value().LoadFromDisk();
+ } else {
+ return nullptr;
+ }
+}
+
+TemplateInfo* Project::InsertTemplate(std::string_view name, TemplateInfo info)
+{
+ auto [it, inserted] = mTemplates.insert(name, info);
+ if (inserted) {
+ return &it.value();
+ } else {
+ return nullptr;
+ }
+}
+
+bool Project::RemoveTemplate(std::string_view name)
+{
+ auto iter = mTemplates.find(name);
+ if (iter == mTemplates.end()) {
+ return false;
+ }
+ auto& info = iter.value();
+
+ fs::remove(info.Path);
+ mTemplates.erase(iter);
+
+ return true;
+}
+
+bool Project::RenameTemplate(std::string_view oldName, std::string_view newName)
+{
+ auto iter = mTemplates.find(oldName);
+ if (iter == mTemplates.end()) return false;
+
+ auto info = std::move(iter.value());
+
+ auto& oldPath = info.Path;
+ auto newPath = GetTemplatePath(newName);
+ fs::rename(oldPath, newPath);
+ info.Path = std::move(newPath);
+
+ mTemplates.insert(newName, std::move(info));
+ mTemplates.erase(iter);
+
+ return true;
+}
+
Json::Value Project::Serialize()
{
Json::Value root(Json::objectValue);
@@ -201,23 +289,3 @@ 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 5f26532..bce58c2 100644
--- a/core/src/Model/Project.hpp
+++ b/core/src/Model/Project.hpp
@@ -1,7 +1,9 @@
#pragma once
#include "Model/Items.hpp"
+#include "Model/Template/Template.hpp"
#include "Model/TransactionsModel.hpp"
+#include "Model/Workflow/Workflow.hpp"
#include <json/forwards.h>
#include <tsl/array_map.h>
@@ -15,17 +17,21 @@ public:
ItemList<ProductItem> Products;
ItemList<FactoryItem> Factories;
ItemList<CustomerItem> Customers;
- TransactionModel Database;
private:
tsl::array_map<char, WorkflowInfo> mWorkflows;
+ tsl::array_map<char, TemplateInfo> mTemplates;
std::filesystem::path mRootPath;
std::string mRootPathString;
std::string mName;
+ // This is put after the private fields, so that when TransactionModel's constructor runs, all of them will be initialized
+public:
+ TransactionModel Database;
+
public:
/// Load the project from a directory containing the cplt_project.json file.
- Project(const std::filesystem::path& rootPath);
+ Project(std::filesystem::path rootPath);
/// Create a project with the given name in the given path. Note that the path should be a directory that will contain the project files once created.
/// This function assumes the given directory will exist and is empty.
@@ -39,15 +45,23 @@ public:
std::filesystem::path GetItemsDirectory() const;
std::filesystem::path GetWorkflowsDirectory() const;
std::filesystem::path GetWorkflowPath(std::string_view name) const;
+ std::filesystem::path GetTemplatesDirectory() const;
+ std::filesystem::path GetTemplatePath(std::string_view name) const;
const std::string& GetName() const;
void SetName(std::string name);
- const decltype(mWorkflows)& GetWorkflows() const;
+ const tsl::array_map<char, WorkflowInfo>& 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);
+ bool RenameWorkflow(std::string_view oldName, std::string_view newName);
+
+ const tsl::array_map<char, TemplateInfo>& GetTemplates() const;
+ std::unique_ptr<Template> LoadTemplate(std::string_view name);
+ bool InsertTemplate(std::string_view name, TemplateInfo info);
+ bool RemoveTemplate(std::string_view name);
+ bool RenameTemplate(std::string_view oldName, std::string_view newName);
Json::Value Serialize();
void WriteToDisk();
diff --git a/core/src/Model/Template/TableTemplate.cpp b/core/src/Model/Template/TableTemplate.cpp
index 258bb37..0ad1cca 100644
--- a/core/src/Model/Template/TableTemplate.cpp
+++ b/core/src/Model/Template/TableTemplate.cpp
@@ -5,6 +5,7 @@
bool TableCell::IsDataHoldingCell() const
{
+ return IsPrimaryCell() || !IsMergedCell();
}
bool TableCell::IsPrimaryCell() const
@@ -58,12 +59,12 @@ const TableTemplate& TableInstanciationParameters::GetTable() const
int TableTemplate::GetTableWidth() const
{
- return mTableWidth;
+ return mColumnWidths.size();
}
int TableTemplate::GetTableHeight() const
{
- return mTableHeight;
+ return mRowHeights.size();
}
void TableTemplate::Resize(int newWidth, int newHeight)
@@ -73,10 +74,13 @@ void TableTemplate::Resize(int newWidth, int newHeight)
std::vector<TableCell> cells;
cells.reserve(newWidth * newHeight);
- int yEnd = std::min(mTableHeight, newHeight);
- int xEnd = std::min(mTableWidth, newWidth);
+ int tableWidth = GetTableWidth();
+ int tableHeight = GetTableHeight();
+
+ int yEnd = std::min(tableHeight, newHeight);
+ int xEnd = std::min(tableWidth, newWidth);
for (int y = 0; y < yEnd; ++y) {
- if (y >= mTableHeight) {
+ if (y >= tableHeight) {
for (int x = 0; x < xEnd; ++x) {
cells.push_back(TableCell{});
}
@@ -84,7 +88,7 @@ void TableTemplate::Resize(int newWidth, int newHeight)
}
for (int x = 0; x < xEnd; ++x) {
- if (x >= mTableWidth) {
+ if (x >= tableWidth) {
cells.push_back(TableCell{});
} else {
auto& cell = GetCell({ x, y });
@@ -96,9 +100,30 @@ void TableTemplate::Resize(int newWidth, int newHeight)
mCells = std::move(cells);
}
+int TableTemplate::GetRowHeight(int row) const
+{
+ return mRowHeights[row];
+}
+
+void TableTemplate::SetRowHeight(int row, int height)
+{
+ mRowHeights[row] = height;
+}
+
+int TableTemplate::GetColumnWidth(int column) const
+{
+ return mColumnWidths[column];
+}
+
+void TableTemplate::SetColumnWidth(int column, int width)
+{
+ mColumnWidths[column] = width;
+}
+
const TableCell& TableTemplate::GetCell(Vec2i pos) const
{
- return mCells[pos.y * mTableWidth + pos.x];
+ int tableWidth = GetTableWidth();
+ return mCells[pos.y * tableWidth + pos.x];
}
TableCell& TableTemplate::GetCell(Vec2i pos)
@@ -194,7 +219,7 @@ lxw_worksheet* TableTemplate::InstanciateToExcelWorksheet(lxw_workbook* workbook
// Not enough space to fit in this array group, update (or insert) the appropriate amount of generated rows
int row = i;
int count = param.size();
- generatedRanges.insert(row, count);
+ generatedRanges.try_emplace(row, count);
}
auto GetOffset = [&](int y) -> int {
@@ -217,27 +242,33 @@ lxw_worksheet* TableTemplate::InstanciateToExcelWorksheet(lxw_workbook* workbook
}
};
- // Write/instanciate all array groups
+ // Write/instantiate all array groups
for (size_t i = 0; i < mArrayGroups.size(); ++i) {
auto& groupInfo = mArrayGroups[i];
auto& groupParams = params.ArrayGroups[i];
int rowCellCount = groupInfo.GetCount();
- int row = groupInfo.Row + GetOffset(groupInfo.Row);
+ int rowCount = groupParams.size();
+ int baseRowIdx = groupInfo.Row + GetOffset(groupInfo.Row);
// For each row that would be generated
- for (auto& rowCells : groupParams) {
+ for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx) {
+ auto& row = groupParams[rowIdx];
+
// For each cell in the row
- for (int i = 0; i < rowCellCount; ++i) {
- // TODO spport merged cells in array groups
- worksheet_write_string(worksheet, row + i, col, rowCells[i].c_str(), nullptr);
+ for (int rowCellIdx = 0; rowCellIdx < rowCellCount; ++rowCellIdx) {
+ // TODO support merged cells in array groups
+ worksheet_write_string(worksheet, baseRowIdx + rowIdx, rowCellIdx, row[rowCellIdx].c_str(), nullptr);
}
}
}
+ int tableWidth = GetTableWidth();
+ int tableHeight = GetTableHeight();
+
// Write all regular and singular parameter cells
- for (int y = 0; y < mTableHeight; ++y) {
- for (int x = 0; x < mTableWidth; ++x) {
+ for (int y = 0; y < tableHeight; ++y) {
+ for (int x = 0; x < tableWidth; ++x) {
auto& cell = GetCell({ x, y });
if (!cell.IsDataHoldingCell()) {
diff --git a/core/src/Model/Template/TableTemplate.hpp b/core/src/Model/Template/TableTemplate.hpp
index be043a9..688192a 100644
--- a/core/src/Model/Template/TableTemplate.hpp
+++ b/core/src/Model/Template/TableTemplate.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "Model/Template/Template.hpp"
#include "Utils/Vector.hpp"
#include "Utils/VectorHash.hpp"
#include "cplt_fwd.hpp"
@@ -127,19 +128,24 @@ public:
/// parametric rows/columns, and grids are also supported.
///
/// This current supports exporting to xlsx files.
-class TableTemplate
+class TableTemplate : public Template
{
private:
std::vector<TableCell> mCells;
std::vector<TableCellArrayGroup> mArrayGroups;
- int mTableWidth;
- int mTableHeight;
+ std::vector<int> mRowHeights;
+ std::vector<int> mColumnWidths;
public:
int GetTableWidth() const;
int GetTableHeight() const;
void Resize(int newWidth, int newHeight);
+ int GetRowHeight(int row) const;
+ void SetRowHeight(int row, int height);
+ int GetColumnWidth(int column) const;
+ void SetColumnWidth(int column, int width);
+
const TableCell& GetCell(Vec2i pos) const;
TableCell& GetCell(Vec2i pos);
diff --git a/core/src/Model/Template/Template.hpp b/core/src/Model/Template/Template.hpp
new file mode 100644
index 0000000..0901a1b
--- /dev/null
+++ b/core/src/Model/Template/Template.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "cplt_fwd.hpp"
+
+#include <filesystem>
+#include <iosfwd>
+#include <memory>
+#include <string>
+
+class Template
+{
+public:
+ enum Kind
+ {
+ KD_Table,
+
+ InvalidKind,
+ KindCount = InvalidKind,
+ };
+
+public:
+ static const char* FormatKind(Kind kind);
+ static std::unique_ptr<Template> CreateByKind(Kind kind);
+
+ virtual ~Template() = default;
+
+ enum class ReadResult
+ {
+ RR_Success,
+ RR_InvalidFormat,
+ };
+ ReadResult ReadFrom(std::istream& stream) = 0;
+ void WriteTo(std::ostream& stream) const = 0;
+};
+
+class TemplateInfo
+{
+public:
+ std::filesystem::path Path;
+ std::string Name;
+ std::string PathStringCache = Path.string();
+ Template::Kind Kind;
+
+ std::unique_ptr<Template> LoadFromDisk() const;
+};
diff --git a/core/src/Model/Template/Template_Main.cpp b/core/src/Model/Template/Template_Main.cpp
new file mode 100644
index 0000000..eeb6871
--- /dev/null
+++ b/core/src/Model/Template/Template_Main.cpp
@@ -0,0 +1,5 @@
+#include "Template.hpp"
+
+std::unique_ptr<Template> TemplateInfo::LoadFromDisk() const
+{
+}
diff --git a/core/src/Model/Template/Template_RTTI.cpp b/core/src/Model/Template/Template_RTTI.cpp
new file mode 100644
index 0000000..042aaec
--- /dev/null
+++ b/core/src/Model/Template/Template_RTTI.cpp
@@ -0,0 +1,21 @@
+#include "Template.hpp"
+
+#include "Model/Template/TableTemplate.hpp"
+
+inline const char* Template::FormatKind(Kind kind)
+{
+ switch (kind) {
+ case KD_Table: return "Table template";
+
+ case InvalidKind: return "<invalid kind>";
+ }
+}
+
+inline std::unique_ptr<Template> Template::CreateByKind(Kind kind)
+{
+ switch (kind) {
+ case KD_Table: return std::make_unique<TableTemplate>();
+
+ case InvalidKind: return nullptr;
+ }
+}
diff --git a/core/src/Model/Template/fwd.hpp b/core/src/Model/Template/fwd.hpp
index 2f41f22..6bc7349 100644
--- a/core/src/Model/Template/fwd.hpp
+++ b/core/src/Model/Template/fwd.hpp
@@ -5,3 +5,7 @@ class TableCell;
class TableCellArrayGroup;
class TableInstanciationParameters;
class TableTemplate;
+
+// Template.hpp
+class Template;
+class TemplateInfo;
diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp
index 99e4e90..ded9bfb 100644
--- a/core/src/Model/Workflow/Workflow.hpp
+++ b/core/src/Model/Workflow/Workflow.hpp
@@ -173,9 +173,9 @@ protected:
struct WorkflowInfo
{
+ std::filesystem::path Path;
std::string Name;
std::string PathStringCache = Path.string();
- std::filesystem::path Path;
std::unique_ptr<Workflow> LoadFromDisk() const;
};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index 6fa29c5..09b0c75 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -3,6 +3,8 @@
#include "Model/Template/fwd.hpp"
#include "Model/Workflow/fwd.hpp"
+// Assets.hpp
+
// Filter.hpp
class TableRowsFilter;
diff --git a/core/src/UI/Localization.hpp b/core/src/UI/Localization.hpp
index c470fa7..43ee23b 100644
--- a/core/src/UI/Localization.hpp
+++ b/core/src/UI/Localization.hpp
@@ -32,6 +32,7 @@ public:
BasicTranslation DatabaseViewTab{ "MainWindow.Tab.DatabaseView"sv };
BasicTranslation ItemsTab{ "MainWindow.Tab.Items"sv };
BasicTranslation WorkflowsTab{ "MainWindow.Tab.Workflows"sv };
+ BasicTranslation TemplatesTab{ "MainWindow.Tab.Templates"sv };
/* Project tab */
@@ -54,7 +55,6 @@ public:
BasicTranslation InvalidProjectFormat{ "Project.InvalidProjectFormat"sv };
- BasicTranslation CloseActiveProject{ "ActiveProject.Close"sv };
BasicTranslation OpenActiveProjectInFileSystem{ "ActiveProject.OpenInFilesystem"sv };
BasicTranslation ActiveProjectName{ "ActiveProject.Info.Name"sv };
BasicTranslation ActiveProjectPath{ "ActiveProject.Info.Path"sv };
diff --git a/core/src/UI/UI.hpp b/core/src/UI/UI.hpp
index cce0e00..dfce713 100644
--- a/core/src/UI/UI.hpp
+++ b/core/src/UI/UI.hpp
@@ -41,5 +41,6 @@ void SettingsTab();
void DatabaseViewTab();
void ItemsTab();
void WorkflowsTab();
+void TemplatesTab();
} // namespace UI
diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp
index b1bee7d..1838aec 100644
--- a/core/src/UI/UI_MainWindow.cpp
+++ b/core/src/UI/UI_MainWindow.cpp
@@ -21,7 +21,7 @@ void ProjectTab_Normal()
auto& gs = GlobalStates::GetInstance();
auto& uis = UIState::GetInstance();
- if (ImGui::Button(ls->CloseActiveProject.Get())) {
+ if (ImGui::Button(ls->Close.Get())) {
uis.CloseCurrentProject();
return;
}
@@ -93,9 +93,9 @@ void ProjectTab_NoProject()
uis.SetCurrentProject(std::make_unique<Project>(std::move(dirPath), std::move(projectName)));
// Dialog just got closed, reset states
- projectName = "";
- dirName = "";
- dirPath = fs::path{};
+ projectName.clear();
+ dirName.clear();
+ dirPath.clear();
dirNameIsValid = false;
}
@@ -231,6 +231,11 @@ void UI::MainWindow()
ImGui::EndTabItem();
}
+ if (ImGui::BeginTabItem(ls->TemplatesTab.Get())) {
+ UI::TemplatesTab();
+ ImGui::EndTabItem();
+ }
+
endTab:
ImGui::EndTabBar();
}
diff --git a/core/src/UI/UI_Templates.cpp b/core/src/UI/UI_Templates.cpp
new file mode 100644
index 0000000..628f3f7
--- /dev/null
+++ b/core/src/UI/UI_Templates.cpp
@@ -0,0 +1,289 @@
+#include "UI.hpp"
+
+#include "Model/Project.hpp"
+#include "Model/Template/TableTemplate.hpp"
+#include "Model/Template/Template.hpp"
+#include "UI/Localization.hpp"
+#include "UI/States.hpp"
+
+#include <imgui.h>
+#include <imgui_extra_math.h>
+#include <imgui_stdlib.h>
+#include <fstream>
+#include <iostream>
+#include <utility>
+
+namespace {
+class TemplateUI
+{
+public:
+ virtual ~TemplateUI() = default;
+ virtual void Draw() = 0;
+};
+
+class TableTemplateUI : public TemplateUI
+{
+private:
+ std::unique_ptr<TableTemplate> mTable;
+
+public:
+ TableTemplateUI(std::unique_ptr<TableTemplate> table)
+ : mTable{ std::move(table) }
+ {
+ }
+
+ virtual void Draw() override
+ {
+ ImGui::Columns(2);
+
+ DrawInspector();
+ ImGui::NextColumn();
+
+ DrawTable();
+ ImGui::NextColumn();
+
+ ImGui::Columns(1);
+ }
+
+private:
+ void DrawInspector()
+ {
+ }
+
+ void DrawTable()
+ {
+ constexpr int kCellSpacing = 20;
+
+ int colCount = mTable->GetTableWidth();
+ int rowCount = mTable->GetTableHeight();
+ float x = 0.0f;
+ float y = 0.0f;
+ for (int rowIdx = 0; rowIdx < rowCount; ++rowIdx) {
+ int rowHeight = mTable->GetRowHeight(rowIdx);
+
+ for (int colIdx = 0; colIdx < colCount; ++colIdx) {
+ int colWidth = mTable->GetColumnWidth(colIdx);
+
+ ImRect rect{
+ ImVec2(x, y),
+ ImVec2(colWidth, rowHeight),
+ };
+
+ // TODO
+
+ x += colWidth;
+ x += kCellSpacing;
+ }
+
+ y += rowHeight;
+ y += kCellSpacing;
+ }
+ }
+};
+
+struct DrawTemplateList_State
+{
+ // Internal data
+
+ // Items that are intended for user usage
+ const TemplateInfo* SelectedTemplate = nullptr;
+};
+
+void DrawTemplateList(DrawTemplateList_State& state)
+{
+ auto& uis = UIState::GetInstance();
+ auto& templates = uis.CurrentProject->GetTemplates();
+
+ for (auto& info : templates) {
+ if (ImGui::Selectable(info.Name.c_str(), state.SelectedTemplate == &info)) {
+ state.SelectedTemplate = &info;
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("Path: %s", info.PathStringCache.c_str());
+ ImGui::EndTooltip();
+ }
+ }
+}
+} // namespace
+
+void UI::TemplatesTab()
+{
+ auto ls = LocaleStrings::Instance.get();
+ auto& uis = UIState::GetInstance();
+
+ bool openedDummy = true;
+ static std::unique_ptr<TemplateUI> openTemplate;
+ static DrawTemplateList_State state;
+
+ // Toolbar item: close
+ if (ImGui::Button(ls->Close.Get(), openTemplate == nullptr)) {
+ openTemplate = nullptr;
+ }
+
+ // Toolbar item: open...
+ ImGui::SameLine();
+ if (ImGui::Button("Open...")) {
+ ImGui::OpenPopup("Open template");
+ }
+ if (ImGui::BeginPopupModal("Open template", &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
+ DrawTemplateList(state);
+
+ if (state.SelectedTemplate) {
+ // TODO
+ }
+
+ ImGui::EndPopup();
+ }
+
+ // Toolbar item: manage...
+ ImGui::SameLine();
+ if (ImGui::Button("Manage...")) {
+ ImGui::OpenPopup("Manage templates");
+ }
+ if (ImGui::BeginPopupModal("Manage templates", &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
+ DrawTemplateList(state);
+
+ enum class NameSelectionError
+ {
+ None,
+ Duplicated,
+ Empty,
+ };
+
+ static std::string newName;
+ static NameSelectionError newNameError;
+ auto ValidateNewName = [&]() -> void {
+ if (newName.empty()) {
+ newNameError = NameSelectionError::Empty;
+ }
+
+ auto& templates = uis.CurrentProject->GetTemplates();
+ if (templates.find(newName) != templates.end()) {
+ newNameError = NameSelectionError::Duplicated;
+ }
+ };
+ auto ShowNewNameErrors = [&]() -> void {
+ switch (newNameError) {
+ case NameSelectionError::None: break;
+ case NameSelectionError::Duplicated:
+ ImGui::ErrorMessage("Duplicate template name");
+ break;
+ case NameSelectionError::Empty:
+ ImGui::ErrorMessage("Template name cannot be empty");
+ break;
+ }
+ };
+
+ static Template::Kind newKind = Template::InvalidKind;
+ auto ShowNewKindErrors = [&]() -> void {
+ if (newKind == Template::InvalidKind) {
+ ImGui::ErrorMessage("Must select a valid template type");
+ }
+ };
+
+ auto IsInputValid = [&]() -> bool {
+ return newNameError == NameSelectionError::None && newKind != Template::InvalidKind;
+ };
+
+ if (ImGui::Button(ls->Add.Get())) {
+ ImGui::OpenPopup("Create template");
+ }
+ if (ImGui::BeginPopupModal("Create template")) {
+ if (ImGui::InputText("Name", &newName)) {
+ ValidateNewName();
+ }
+
+ if (ImGui::BeginCombo("Type", Template::FormatKind(newKind))) {
+ for (int i = 0; i < Template::KindCount; ++i) {
+ auto kind = static_cast<Template::Kind>(i);
+ if (ImGui::Selectable(Template::FormatKind(kind), newKind == kind)) {
+ newKind = kind;
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (ImGui::Button(ls->DialogConfirm.Get(), IsInputValid())) {
+ auto& project = *uis.CurrentProject;
+
+ auto& info = *project.InsertTemplate(
+ newName,
+ TemplateInfo{
+ .Path = project.GetTemplatePath(newName),
+ .Name = newName, // Don't std::move here because evaluation order of `newName` (as parameter of InsertTemplate()) and this is unspecified
+ .Kind = newKind,
+ });
+
+ auto tmpl = Template::CreateByKind(newKind);
+
+ std::ofstream ofs(info.Path);
+ if (ofs) {
+ tmpl->WriteTo(ofs);
+ } else {
+ // Writing to disk here isn't necessary for downstream operations to function successfully;
+ // if the user makes any changes and don't save, those changes will not persist anyways
+ // if the user doesn't make any changes, it doesn't matter that the empty template file isn't created yet
+ }
+
+ openTemplate = std::make_unique<TableTemplateUI>(std::move(tmpl));
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+
+ ShowNewNameErrors();
+ ShowNewKindErrors();
+
+ ImGui::EndPopup();
+ }
+
+ if (ImGui::Button(ls->Rename.Get(), state.SelectedTemplate == nullptr)) {
+ ImGui::OpenPopup("Rename template");
+ newName.clear();
+ }
+ if (ImGui::BeginPopupModal("Rename template")) {
+ if (ImGui::InputText("New name", &newName)) {
+ ValidateNewName();
+ }
+
+ if (ImGui::Button(ls->DialogConfirm.Get(), IsInputValid())) {
+ auto& project = *uis.CurrentProject;
+
+ project.RenameTemplate(
+ state.SelectedTemplate->Name,
+ newName);
+
+ // We mutated the map, the pointer may be invalid now
+ state.SelectedTemplate = &project.GetTemplates().at(newName);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+
+ ShowNewNameErrors();
+
+ ImGui::EndPopup();
+ }
+
+ if (ImGui::Button(ls->Delete.Get(), state.SelectedTemplate == nullptr)) {
+ ImGui::OpenPopup("Delete confirmation");
+ }
+ if (ImGui::BeginPopupModal("Delete confirmation")) {
+ assert(state.SelectedTemplate != nullptr);
+
+ if (ImGui::Button(ls->DialogConfirm.Get())) {
+ uis.CurrentProject->RemoveTemplate(state.SelectedTemplate->Name);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button(ls->DialogCancel.Get())) {
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
+
+ ImGui::EndPopup();
+ }
+}
diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp
index feebe89..ac023c9 100644
--- a/core/src/UI/UI_Workflows.cpp
+++ b/core/src/UI/UI_Workflows.cpp
@@ -359,12 +359,12 @@ public:
}
};
-struct DrawWorkflowList_State
+struct DrawTemplateList_State
{
const WorkflowInfo* SelectedWorkflow = nullptr;
};
-void DrawWorkflowList(DrawWorkflowList_State& state)
+void DrawTemplateList(DrawTemplateList_State& state)
{
auto& uis = UIState::GetInstance();
auto& workflows = uis.CurrentProject->GetWorkflows();
@@ -390,7 +390,7 @@ void UI::WorkflowsTab()
bool openedDummy = true;
static std::unique_ptr<WorkflowUI> openWorkflow;
- static DrawWorkflowList_State state;
+ static DrawTemplateList_State state;
// Toolbar item: close
if (ImGui::Button(ls->Close.Get(), openWorkflow == nullptr)) {
@@ -403,7 +403,7 @@ void UI::WorkflowsTab()
ImGui::OpenPopup(ls->OpenWorkflowDialogTitle.Get());
}
if (ImGui::BeginPopupModal(ls->OpenWorkflowDialogTitle.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
- DrawWorkflowList(state);
+ DrawTemplateList(state);
if (state.SelectedWorkflow) {
auto workflow = state.SelectedWorkflow->LoadFromDisk();
@@ -419,7 +419,7 @@ void UI::WorkflowsTab()
ImGui::OpenPopup(ls->ManageWorkflowsDialogTitle.Get());
}
if (ImGui::BeginPopupModal(ls->ManageWorkflowsDialogTitle.Get(), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) {
- DrawWorkflowList(state);
+ DrawTemplateList(state);
enum class NameSelectionError
{
@@ -448,7 +448,7 @@ void UI::WorkflowsTab()
if (ImGui::Button(ls->DialogConfirm.Get(), newName.empty())) {
auto& project = uis.CurrentProject;
project->RenameWorkflow(state.SelectedWorkflow->Name, newName);
- state.SelectedWorkflow = &project->GetWorkflows()[newName];
+ state.SelectedWorkflow = &project->GetWorkflows().at(newName);
}
ImGui::SameLine();
if (ImGui::Button(ls->DialogCancel.Get())) {