aboutsummaryrefslogtreecommitdiff
path: root/core/src/UI
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/UI
parent088da97531935a61870ecada10f06b9b9a8255d1 (diff)
Initial work on templates UI
Diffstat (limited to 'core/src/UI')
-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
5 files changed, 306 insertions, 11 deletions
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())) {