From c90f78df080a9891930ee346b0ad87498ba5b697 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Sun, 30 May 2021 23:00:41 -0700 Subject: Initial work on templates UI --- core/src/UI/UI_Templates.cpp | 289 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 core/src/UI/UI_Templates.cpp (limited to 'core/src/UI/UI_Templates.cpp') 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 +#include +#include +#include +#include +#include + +namespace { +class TemplateUI +{ +public: + virtual ~TemplateUI() = default; + virtual void Draw() = 0; +}; + +class TableTemplateUI : public TemplateUI +{ +private: + std::unique_ptr mTable; + +public: + TableTemplateUI(std::unique_ptr 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 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(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(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(); + } +} -- cgit v1.2.3-70-g09d2