diff options
Diffstat (limited to 'core/src/UI')
-rw-r--r-- | core/src/UI/Localization.hpp | 2 | ||||
-rw-r--r-- | core/src/UI/UI.hpp | 1 | ||||
-rw-r--r-- | core/src/UI/UI_MainWindow.cpp | 13 | ||||
-rw-r--r-- | core/src/UI/UI_Templates.cpp | 289 | ||||
-rw-r--r-- | core/src/UI/UI_Workflows.cpp | 12 |
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())) { |