From bdee9dd0c92865e0cec2f4bbf170959df282a930 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Fri, 11 Jun 2021 22:19:23 -0700 Subject: More UI polishing and fix asset saving/reloading --- core/src/Model/Assets.cpp | 9 +++- core/src/Model/Assets.hpp | 9 ++-- core/src/Model/Project.cpp | 24 +++++++-- core/src/Model/Project.hpp | 14 +++--- core/src/Model/Template/TableTemplate.cpp | 10 ++-- core/src/Model/Template/Template.hpp | 4 ++ core/src/Model/Template/Template_Main.cpp | 14 ++---- core/src/Model/Workflow/Workflow.hpp | 4 ++ core/src/Model/Workflow/Workflow_Main.cpp | 14 ++---- core/src/UI/UI_DatabaseView.cpp | 10 ++-- core/src/UI/UI_Templates.cpp | 83 +++++++++++++++++++++++++------ core/src/Utils/Color.hpp | 10 ++++ 12 files changed, 147 insertions(+), 58 deletions(-) diff --git a/core/src/Model/Assets.cpp b/core/src/Model/Assets.cpp index 64e1f22..374995d 100644 --- a/core/src/Model/Assets.cpp +++ b/core/src/Model/Assets.cpp @@ -21,6 +21,7 @@ Asset::Asset() class AssetList::Private { public: + Project* ConnectedProject; tsl::array_map Assets; tsl::array_map> Cache; int CacheSizeLimit = 0; @@ -65,9 +66,10 @@ public: } PopupPrivateState; }; -AssetList::AssetList() +AssetList::AssetList(Project& project) : mPrivate{ std::make_unique() } { + mPrivate->ConnectedProject = &project; } // Write an empty destructor here so std::unique_ptr's destructor can see AssetList::Private's implementation @@ -75,6 +77,11 @@ AssetList::~AssetList() { } +Project& AssetList::GetConnectedProject() const +{ + return *mPrivate->ConnectedProject; +} + void AssetList::Reload() { DiscoverFiles([this](SavedAsset asset) -> void { diff --git a/core/src/Model/Assets.hpp b/core/src/Model/Assets.hpp index adde97b..9fd781f 100644 --- a/core/src/Model/Assets.hpp +++ b/core/src/Model/Assets.hpp @@ -1,8 +1,7 @@ #pragma once #include "Utils/UUID.hpp" - -#include "Assets.hpp" +#include "cplt_fwd.hpp" #include #include @@ -42,9 +41,11 @@ private: std::unique_ptr mPrivate; public: - AssetList(); + AssetList(Project& project); virtual ~AssetList(); + Project& GetConnectedProject() const; + // TODO support file watches void Reload(); @@ -99,6 +100,8 @@ template class AssetListTyped : public AssetList { public: + using AssetList::AssetList; + std::unique_ptr CreateAndLoad(SavedAsset asset) { return std::unique_ptr(static_cast(AssetList::CreateAndLoad(asset).release())); diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp index 9f41d3a..523ee9b 100644 --- a/core/src/Model/Project.cpp +++ b/core/src/Model/Project.cpp @@ -14,7 +14,7 @@ namespace fs = std::filesystem; template -void ReadItemList(ItemList& list, const fs::path& filePath) +static void ReadItemList(ItemList& list, const fs::path& filePath) { std::ifstream ifs(filePath); if (ifs) { @@ -25,9 +25,19 @@ void ReadItemList(ItemList& list, const fs::path& filePath) } } +static void CreateProjectSubfolders(const Project& project) +{ + fs::create_directory(project.GetDatabasesDirectory()); + fs::create_directory(project.GetItemsDirectory()); + fs::create_directory(project.GetWorkflowsDirectory()); + fs::create_directory(project.GetTemplatesDirectory()); +} + Project::Project(fs::path rootPath) : mRootPath{ std::move(rootPath) } , mRootPathString{ mRootPath.string() } + , Workflows(*this) + , Templates(*this) , Database(*this) { // TODO better diagnostic @@ -58,18 +68,26 @@ Project::Project(fs::path rootPath) } } + CreateProjectSubfolders(*this); + auto itemsDir = mRootPath / "items"; ReadItemList(Products, itemsDir / "products.json"); ReadItemList(Factories, itemsDir / "factories.json"); ReadItemList(Customers, itemsDir / "customers.json"); + + Workflows.Reload(); + Templates.Reload(); } Project::Project(fs::path rootPath, std::string name) : mRootPath{ std::move(rootPath) } , mRootPathString{ mRootPath.string() } , mName{ std::move(name) } + , Workflows(*this) + , Templates(*this) , Database(*this) { + CreateProjectSubfolders(*this); } const fs::path& Project::GetPath() const @@ -143,9 +161,7 @@ void Project::WriteToDisk() std::ofstream ofs(mRootPath / "cplt_project.json"); ofs << this->Serialize(); - auto itemsDir = mRootPath / "items"; - fs::create_directories(itemsDir); - + auto itemsDir = GetItemsDirectory(); WriteItemList(Products, itemsDir / "products.json"); WriteItemList(Factories, itemsDir / "factories.json"); WriteItemList(Customers, itemsDir / "customers.json"); diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp index 8cf3483..17d9acb 100644 --- a/core/src/Model/Project.hpp +++ b/core/src/Model/Project.hpp @@ -14,24 +14,24 @@ class Project { -public: - WorkflowAssetList Workflows; - TemplateAssetList Templates; - ItemList Products; - ItemList Factories; - ItemList Customers; - private: std::filesystem::path mRootPath; std::string mRootPathString; std::string mName; + // (Exception to style guidelines) // This is put after the private fields, so that when XxxDatabase's constructor runs, all of them will be initialized public: + WorkflowAssetList Workflows; + TemplateAssetList Templates; + ItemList Products; + ItemList Factories; + ItemList Customers; MainDatabase Database; public: /// Load the project from a directory containing the cplt_project.json file. + /// This only loads the main project file, the caller needs to 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. diff --git a/core/src/Model/Template/TableTemplate.cpp b/core/src/Model/Template/TableTemplate.cpp index 28a4d6e..f2524a0 100644 --- a/core/src/Model/Template/TableTemplate.cpp +++ b/core/src/Model/Template/TableTemplate.cpp @@ -88,17 +88,15 @@ void TableTemplate::Resize(int newWidth, int newHeight) 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) { + for (int y = 0; y < newHeight; ++y) { if (y >= tableHeight) { - for (int x = 0; x < xEnd; ++x) { + for (int x = 0; x < newWidth; ++x) { cells.push_back(TableCell{}); } continue; } - for (int x = 0; x < xEnd; ++x) { + for (int x = 0; x < newWidth; ++x) { if (x >= tableWidth) { cells.push_back(TableCell{}); } else { @@ -109,6 +107,8 @@ void TableTemplate::Resize(int newWidth, int newHeight) } mCells = std::move(cells); + mColumnWidths.resize(newWidth); + mRowHeights.resize(newHeight); } int TableTemplate::GetRowHeight(int row) const diff --git a/core/src/Model/Template/Template.hpp b/core/src/Model/Template/Template.hpp index 7cfbb8b..7d43130 100644 --- a/core/src/Model/Template/Template.hpp +++ b/core/src/Model/Template/Template.hpp @@ -52,6 +52,10 @@ private: NameSelectionError mACNewNameError = NameSelectionError::Empty; Template::Kind mACNewKind = Template::InvalidKind; +public: + // Inherit constructors + using AssetListTyped::AssetListTyped; + protected: virtual void DiscoverFiles(const std::function& callback) const override; diff --git a/core/src/Model/Template/Template_Main.cpp b/core/src/Model/Template/Template_Main.cpp index 7dd5f87..8b659cf 100644 --- a/core/src/Model/Template/Template_Main.cpp +++ b/core/src/Model/Template/Template_Main.cpp @@ -25,8 +25,8 @@ Template::Kind Template::GetKind() const void TemplateAssetList::DiscoverFiles(const std::function& callback) const { - auto& gs = GlobalStates::GetInstance(); - DiscoverFilesByExtension(callback, gs.GetCurrentProject()->GetTemplatesDirectory(), ".cplt-template"sv); + auto dir = GetConnectedProject().GetTemplatesDirectory(); + DiscoverFilesByExtension(callback, dir, ".cplt-template"sv); } std::string TemplateAssetList::RetrieveNameFromFile(const fs::path& file) const @@ -46,12 +46,8 @@ uuids::uuid TemplateAssetList::RetrieveUuidFromFile(const fs::path& file) const fs::path TemplateAssetList::RetrievePathFromAsset(const SavedAsset& asset) const { - auto uuid = uuids::uuid_random_generator{}(); - auto fileName = uuids::to_string(uuid); - fileName.append(".cplt-template"); - - auto& gs = GlobalStates::GetInstance(); - return gs.GetCurrentProject()->GetTemplatePath(fileName); + auto fileName = uuids::to_string(asset.Uuid); + return GetConnectedProject().GetTemplatePath(fileName); } void TemplateAssetList::SaveEmptyInstance(const SavedAsset& asset) const @@ -183,7 +179,7 @@ void TemplateAssetList::DrawBigIcon(ListState& state, const SavedAsset& asset) c void TemplateAssetList::DrawDetailsTableRow(ListState& state, const SavedAsset& asset) const { ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns)) { + if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { state.SelectedAsset = &asset; } diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp index 3dc6f38..9c809bf 100644 --- a/core/src/Model/Workflow/Workflow.hpp +++ b/core/src/Model/Workflow/Workflow.hpp @@ -282,6 +282,10 @@ private: std::string mACNewName; NameSelectionError mACNewNameError = NameSelectionError::Empty; +public: + // Inherit constructors + using AssetListTyped::AssetListTyped; + public: virtual void DiscoverFiles(const std::function& callback) const override; diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp index adf944e..77b64d3 100644 --- a/core/src/Model/Workflow/Workflow_Main.cpp +++ b/core/src/Model/Workflow/Workflow_Main.cpp @@ -746,8 +746,8 @@ std::pair&, uint32_t> Workflow::AllocWorkflowStep( void WorkflowAssetList::DiscoverFiles(const std::function& callback) const { - auto& gs = GlobalStates::GetInstance(); - DiscoverFilesByExtension(callback, gs.GetCurrentProject()->GetWorkflowsDirectory(), ".cplt-workflow"sv); + auto dir = GetConnectedProject().GetWorkflowsDirectory(); + DiscoverFilesByExtension(callback, dir, ".cplt-workflow"sv); } std::string WorkflowAssetList::RetrieveNameFromFile(const fs::path& file) const @@ -767,12 +767,8 @@ uuids::uuid WorkflowAssetList::RetrieveUuidFromFile(const fs::path& file) const fs::path WorkflowAssetList::RetrievePathFromAsset(const SavedAsset& asset) const { - auto uuid = uuids::uuid_random_generator{}(); - auto fileName = uuids::to_string(uuid); - fileName.append(".cplt-workflow"); - - auto& gs = GlobalStates::GetInstance(); - return gs.GetCurrentProject()->GetTemplatePath(fileName); + auto fileName = uuids::to_string(asset.Uuid); + return GetConnectedProject().GetTemplatePath(fileName); } void WorkflowAssetList::SaveEmptyInstance(const SavedAsset& asset) const @@ -875,7 +871,7 @@ void WorkflowAssetList::DrawBigIcon(ListState& state, const SavedAsset& asset) c void WorkflowAssetList::DrawDetailsTableRow(ListState& state, const SavedAsset& asset) const { ImGui::TableNextColumn(); - if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns)) { + if (ImGui::Selectable(asset.Name.c_str(), state.SelectedAsset == &asset, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups)) { state.SelectedAsset = &asset; } } diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp index caf81d8..40f29ca 100644 --- a/core/src/UI/UI_DatabaseView.cpp +++ b/core/src/UI/UI_DatabaseView.cpp @@ -224,6 +224,11 @@ public: SetPage(mCurrentPageNumber + 1); } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { + // TODO + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_EDIT " " I18N_TEXT("Edit", L10N_EDIT), mSelectRow == -1)) { ImGui::OpenPopup(mEditDialogTitle); @@ -235,11 +240,6 @@ public: ImGui::EndPopup(); } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { - // TODO - } - ImGui::SameLine(); if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), mSelectRow == -1)) { // TODO diff --git a/core/src/UI/UI_Templates.cpp b/core/src/UI/UI_Templates.cpp index e08510a..7d0251a 100644 --- a/core/src/UI/UI_Templates.cpp +++ b/core/src/UI/UI_Templates.cpp @@ -30,17 +30,27 @@ class TableTemplateUI : public TemplateUI private: std::unique_ptr mTable; - TableCell* mSelectedCell = nullptr; + Vec2i mSelectionTL; + Vec2i mSelectionBR; + + bool mDirty = false; + bool mFirstDraw = true; public: TableTemplateUI(std::unique_ptr table) : mTable{ std::move(table) } { + // TODO debug code + mTable->Resize(6, 2); } virtual void Draw() override { ImGui::Columns(2); + if (mFirstDraw) { + mFirstDraw = false; + ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.15f); + } DrawInspector(); ImGui::NextColumn(); @@ -55,36 +65,43 @@ private: void DrawInspector() { if (ImGui::BeginTabBar("Inspector")) { - if (ImGui::BeginTabItem("Table")) { - DrawTableInspector(); - ImGui::EndTabItem(); - } if (ImGui::BeginTabItem("Cell")) { DrawCellInspector(); ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Table")) { + DrawTableInspector(); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } } - void DrawTableInspector() - { - // TODO - } - void DrawCellInspector() { - if (mSelectedCell) { - + if (IsSelected()) { + if (mSelectionTL == mSelectionBR) { + auto& selectCell = mTable->GetCell(mSelectionTL); + // TODO + } else { + // TODO + } } else { ImGui::Text("Select a cell to edit"); } } + void DrawTableInspector() + { + // TODO + } + void DrawTable() { constexpr int kCellSpacing = 20; + ImGui::BeginChild("TableTemplate"); + int colCount = mTable->GetTableWidth(); int rowCount = mTable->GetTableHeight(); float x = 0.0f; @@ -95,12 +112,28 @@ private: for (int colIdx = 0; colIdx < colCount; ++colIdx) { int colWidth = mTable->GetColumnWidth(colIdx); + int i = rowIdx * colCount + colIdx; + ImGuiID id = ImGui::GetCurrentWindow()->GetID(i); + + auto p = ImGui::GetCursorPos(); + ImGui::GetWindowDrawList()->AddRectFilled( + ImVec2(p.x + x, p.y + y), + ImVec2(p.x + x + colWidth, p.y + y + rowHeight), + RgbaColor(170, 204, 244).AsImU32()); + ImRect rect{ ImVec2(x, y), ImVec2(colWidth, rowHeight), }; - - // TODO + if (ImGui::ButtonBehavior(rect, id, nullptr, nullptr)) { + if (ImGui::GetIO().KeyShift && IsSelected()) { + // Select range + mSelectionBR = { colIdx, rowIdx }; + } else { + // Select a single cell + SelectCell({ colIdx, rowIdx }); + } + } x += colWidth; x += kCellSpacing; @@ -109,6 +142,25 @@ private: y += rowHeight; y += kCellSpacing; } + + ImGui::EndChild(); + } + + bool IsSelected() const + { + return mSelectionTL.x != -1; + } + + void ClearSelection() + { + mSelectionTL = { -1, -1 }; + mSelectionBR = { -1, -1 }; + } + + void SelectCell(Vec2i cell) + { + mSelectionTL = cell; + mSelectionBR = cell; } }; @@ -152,7 +204,8 @@ void UI::TemplatesTab() ImGui::OpenPopup(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE)); } if (ImGui::BeginPopupModal(I18N_TEXT("Open asset", L10N_ASSET_OPEN_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::Button(I18N_TEXT("Open", L10N_OPEN), state.SelectedAsset == nullptr)) { + if (ImGui::Button(ICON_FA_FOLDER_OPEN " " I18N_TEXT("Open", L10N_OPEN), state.SelectedAsset == nullptr)) { + ImGui::CloseCurrentPopup(); auto kind = static_cast(state.SelectedAsset->Payload); openTemplate = TemplateUI::CreateByKind(kind); } diff --git a/core/src/Utils/Color.hpp b/core/src/Utils/Color.hpp index 46435c3..19227e0 100644 --- a/core/src/Utils/Color.hpp +++ b/core/src/Utils/Color.hpp @@ -115,6 +115,16 @@ public: return ImColor{ v.x, v.y, v.z, v.w }; } + ImU32 AsImU32() const + { + ImU32 res; + res |= r << IM_COL32_R_SHIFT; + res |= g << IM_COL32_G_SHIFT; + res |= b << IM_COL32_B_SHIFT; + res |= a << IM_COL32_A_SHIFT; + return res; + } + constexpr void SetVec(const Vec4f& vec) noexcept { r = (uint8_t)(vec.x * 255.0f); -- cgit v1.2.3-70-g09d2