From d7ee2efaca226fc478e3f0c78abdbe86a887f17a Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 7 Jun 2021 12:19:51 -0700 Subject: Complete asset loading/saving and UI management logic --- conanfile.txt | 1 + core/src/Model/Assets.cpp | 116 +++++++++++++++----------- core/src/Model/Assets.hpp | 97 +++++++++++---------- core/src/Model/Project.hpp | 4 +- core/src/Model/Template/Template.hpp | 19 +++-- core/src/Model/Template/Template_Main.cpp | 62 +++++++++++--- core/src/Model/Template/fwd.hpp | 2 +- core/src/Model/Workflow/Workflow.hpp | 18 ++-- core/src/Model/Workflow/Workflow_Main.cpp | 134 ++++++++++++++++++------------ core/src/Model/Workflow/fwd.hpp | 2 +- core/src/Model/fwd.hpp | 3 - core/src/UI/UI_Templates.cpp | 41 ++++----- core/src/UI/UI_Workflows.cpp | 54 ++++-------- 13 files changed, 310 insertions(+), 243 deletions(-) diff --git a/conanfile.txt b/conanfile.txt index ed63b1b..7ba98c6 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -2,6 +2,7 @@ argparse/2.1 sqlitecpp/3.1.1 jsoncpp/1.9.4 +stduuid/1.0 portable-file-dialogs/0.1.0 libxlsxwriter/1.0.0 glfw/3.3.2 diff --git a/core/src/Model/Assets.cpp b/core/src/Model/Assets.cpp index e4eee54..a1e9730 100644 --- a/core/src/Model/Assets.cpp +++ b/core/src/Model/Assets.cpp @@ -4,54 +4,16 @@ #include #include +using namespace std::literals::string_view_literals; namespace fs = std::filesystem; Asset::Asset() { } -std::unique_ptr AssetCategory::CreateEmptyUnique(const SavedAsset& diskForm) const -{ - return std::unique_ptr(CreateEmpty(diskForm)); -} - -std::unique_ptr AssetCategory::LoadUnique(const SavedAsset& diskForm) const -{ - return std::unique_ptr(Load(diskForm)); -} - -void AssetCategory::DiscoverFilesByExtension(const std::function& callback, const std::filesystem::path& containerDir, std::string_view extension) -{ - for (auto entry : fs::directory_iterator(containerDir)) { - if (!entry.is_regular_file()) continue; - - // If the caller provided an extension to match against, and it doesn't equal to current file extension, skip - if (!extension.empty() && - entry.path().extension() != extension) - { - continue; - } - - callback(SavedAsset{ - .Path = entry.path(), - .Name = entry.path().stem().string(), - }); - } -} - -void AssetCategory::DiscoverFilesByHeader(const std::function& callback, const std::filesystem::__cxx11::path& containerDir, const std::function& validater) -{ - // TODO -} - -AssetList::AssetList(const AssetCategory& loader) - : mLoader{ &loader } -{ -} - void AssetList::Reload() { - mLoader->DiscoverFiles([this](SavedAsset asset) -> void { + DiscoverFiles([this](SavedAsset asset) -> void { Create(std::move(asset)); }); } @@ -71,8 +33,12 @@ const SavedAsset& AssetList::Create(SavedAsset asset) auto [iter, DISCARD] = mAssets.insert(asset.Name, SavedAsset{}); auto& savedAsset = iter.value(); - mLoader->CreateEmpty(asset); savedAsset = std::move(asset); + if (savedAsset.Uuid.is_nil()) { + savedAsset.Uuid = uuids::uuid_random_generator{}(); + } + + SaveEmptyInstance(savedAsset); return savedAsset; } @@ -80,24 +46,51 @@ const SavedAsset& AssetList::Create(SavedAsset asset) std::unique_ptr AssetList::CreateAndLoad(SavedAsset assetIn) { auto& savedAsset = Create(std::move(assetIn)); - auto asset = std::unique_ptr(mLoader->CreateEmpty(savedAsset)); + auto asset = std::unique_ptr(CreateEmptyInstance(savedAsset)); return asset; } -std::unique_ptr AssetList::LoadFromDisk(std::string_view name) const +std::unique_ptr AssetList::Load(std::string_view name) const { if (auto savedAsset = FindByName(name)) { - auto asset = mLoader->LoadUnique(*savedAsset); + auto asset = Load(*savedAsset); return asset; + } else { + return nullptr; } } -bool AssetList::Rename(std::string_view oldName, std::string_view newName) +std::unique_ptr AssetList::Load(const SavedAsset& asset) const +{ + return std::unique_ptr(LoadImpl(asset)); +} + +const SavedAsset* AssetList::Rename(std::string_view oldName, std::string_view newName) { + auto iter = mAssets.find(oldName); + if (iter == mAssets.end()) return nullptr; + + auto info = std::move(iter.value()); + info.Name = newName; + + auto [newIter, DISCARD] = mAssets.insert(newName, std::move(info)); + mAssets.erase(iter); + + return &newIter.value(); } bool AssetList::Remove(std::string_view name) { + auto iter = mAssets.find(name); + if (iter == mAssets.end()) { + return false; + } + auto& asset = iter.value(); + + fs::remove(RetrievePathFromAsset(asset)); + mAssets.erase(iter); + + return true; } int AssetList::GetCacheSizeLimit() const @@ -110,17 +103,42 @@ void AssetList::SetCacheSizeLimit(int limit) mCacheSizeLimit = limit; } -void AssetList::DrawBigIcons(DrawState& state) +void AssetList::DrawBigIcons(ListState& state) { // TODO } -void AssetList::DrawDetails(DrawState& state) +void AssetList::DrawDetails(ListState& state) { - mLoader->SetupDetailsTable("AssetDetailsTable"); + SetupDetailsTable("AssetDetailsTable"); for (auto& asset : mAssets) { - mLoader->DrawDetailsTableRow(asset); + DrawDetailsTableRow(asset); ImGui::TableNextRow(); } ImGui::EndTable(); } + +void AssetList::DiscoverFilesByExtension(const std::function& callback, const fs::path& containerDir, std::string_view extension) const +{ + for (auto entry : fs::directory_iterator(containerDir)) { + if (!entry.is_regular_file()) continue; + + // If the caller provided an extension to match against, and it doesn't equal to current file extension, skip + if (!extension.empty() && + entry.path().extension() != extension) + { + continue; + } + + callback(SavedAsset{ + .Name = RetrieveNameFromFile(entry.path()), + .Uuid = RetrieveUuidFromFile(entry.path()), + // TODO load payload + }); + } +} + +void AssetList::DiscoverFilesByHeader(const std::function& callback, const fs::path& containerDir, const std::function& validater) const +{ + // TODO +} diff --git a/core/src/Model/Assets.hpp b/core/src/Model/Assets.hpp index bc5219d..3401e42 100644 --- a/core/src/Model/Assets.hpp +++ b/core/src/Model/Assets.hpp @@ -1,18 +1,22 @@ #pragma once #include +#include #include #include #include #include /// A structure representing a ready-to-be-loaded asset, locating on the disk. +/// Each asset should be identified by a unique uuid within the asset category (i.e. a workflow and a template can share the same uuid), +/// generated on insertion to an asset list if not given by the caller. struct SavedAsset { - std::filesystem::path Path; std::string Name; - /// `Path`'s string form, encoded in UTF-8. - std::string PathString = Path.string(); + /// UUID of this asset. This field is generated as a random UUID v4 upon insertion into an AssetList, if not already provided by the caller (indicated by !is_nil()). + uuids::uuid Uuid; + /// Extra data to be used by the AssetList/Asset implementation. + uint64_t Payload; }; class Asset @@ -22,40 +26,15 @@ public: virtual ~Asset() = default; }; -class AssetCategory -{ -public: - virtual ~AssetCategory() = default; - - virtual void DiscoverFiles(const std::function& callback) const = 0; - - virtual Asset* CreateEmpty(const SavedAsset& diskForm) const = 0; - std::unique_ptr CreateEmptyUnique(const SavedAsset& diskForm) const; - virtual Asset* Load(const SavedAsset& diskForm) const = 0; - std::unique_ptr LoadUnique(const SavedAsset& diskForm) const; - - /// This should call ImGui::BeginTable() along with other accessories such as setting up the header row. - virtual void SetupDetailsTable(const char* tableId) const = 0; - virtual void DrawBigIcon(const SavedAsset& asset) const = 0; - virtual void DrawDetailsTableRow(const SavedAsset& asset) const = 0; - -protected: - /* Helper loader functions */ - - static void DiscoverFilesByExtension(const std::function& callback, const std::filesystem::path& containerDir, std::string_view extension); - static void DiscoverFilesByHeader(const std::function& callback, const std::filesystem::path& containerDir, const std::function& validater); -}; - class AssetList { private: - const AssetCategory* mLoader; tsl::array_map mAssets; tsl::array_map> mCache; int mCacheSizeLimit = 0; public: - AssetList(const AssetCategory& loader); + virtual ~AssetList() = default; // TODO support file watches void Reload(); @@ -63,42 +42,62 @@ public: const SavedAsset* FindByName(std::string_view name) const; const SavedAsset& Create(SavedAsset asset); std::unique_ptr CreateAndLoad(SavedAsset asset); - std::unique_ptr LoadFromDisk(std::string_view name) const; - bool Rename(std::string_view oldName, std::string_view newName); + /// Load the asset on disk by its name. + std::unique_ptr Load(std::string_view name) const; + /// Load the asset on disk by a reference to its SavedAsset instance. This function assumes that the given SavedAsset + /// is stored in AssetList, otherwise the behavior is undefined. + std::unique_ptr Load(const SavedAsset& asset) const; + const SavedAsset* Rename(std::string_view oldName, std::string_view newName); bool Remove(std::string_view name); int GetCacheSizeLimit() const; void SetCacheSizeLimit(int limit); - struct DrawState + struct ListState { const SavedAsset* SelectedAsset = nullptr; }; - void DrawBigIcons(DrawState& state); - void DrawDetails(DrawState& state); + void DrawBigIcons(ListState& state); + void DrawDetails(ListState& state); + +protected: + virtual void DiscoverFiles(const std::function& callback) const = 0; + + // Helper + void DiscoverFilesByExtension(const std::function& callback, const std::filesystem::path& containerDir, std::string_view extension) const; + void DiscoverFilesByHeader(const std::function& callback, const std::filesystem::path& containerDir, const std::function& validater) const; + + virtual void SaveEmptyInstance(const SavedAsset& asset) const = 0; + virtual Asset* CreateEmptyInstance(const SavedAsset& asset) const = 0; + + virtual Asset* LoadImpl(const SavedAsset& asset) const = 0; + + virtual std::string RetrieveNameFromFile(const std::filesystem::path& file) const = 0; + virtual uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const = 0; + virtual std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const = 0; + + /// This should call ImGui::BeginTable() along with other accessories such as setting up the header row. + virtual void SetupDetailsTable(const char* tableId) const = 0; + virtual void DrawBigIcon(const SavedAsset& asset) const = 0; + virtual void DrawDetailsTableRow(const SavedAsset& asset) const = 0; }; -template -class TypedAssetList : public AssetList +template +class AssetListTyped : public AssetList { public: - using Asset = TAsset; - using AssetType = typename TAsset::CategoryType; - -public: - // Import constructor - using AssetList::AssetList; - - Asset* FindByName(std::string_view name) const + std::unique_ptr CreateAndLoad(SavedAsset asset) { - return static_cast(AssetList::FindByName(name)); + return std::unique_ptr(static_cast(AssetList::CreateAndLoad(asset).release())); } - std::unique_ptr Create(std::string_view name) + + std::unique_ptr Load(std::string_view name) const { - return std::unique_ptr(static_cast(AssetList::Create(name).release())); + return std::unique_ptr(static_cast(AssetList::Load(name).release())); } - std::unique_ptr LoadFromDisk(std::string_view name) const + + std::unique_ptr Load(const SavedAsset& asset) const { - return std::unique_ptr(static_cast(AssetList::LoadFromDisk(name))); + return std::unique_ptr(static_cast(AssetList::Load(asset).release())); } }; diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp index fea148d..8cf3483 100644 --- a/core/src/Model/Project.hpp +++ b/core/src/Model/Project.hpp @@ -15,8 +15,8 @@ class Project { public: - TypedAssetList Workflows; - TypedAssetList