#include "Assets.hpp" #include "UI/UI.hpp" #include "Utils/I18n.hpp" #include "Utils/IO/DataStream.hpp" #include "Utils/IO/StringIntegration.hpp" #include "Utils/IO/UuidIntegration.hpp" #include #include #include #include #include #include using namespace std::literals::string_view_literals; namespace fs = std::filesystem; template void OperateStreamForSavedAsset(TSavedAsset& cell, TStream& proxy) { proxy.template ObjectAdapted(cell.Name); proxy.template ObjectAdapted(cell.Uuid); proxy.Value(cell.Payload); } void SavedAsset::ReadFromDataStream(InputDataStream& stream) { ::OperateStreamForSavedAsset(*this, stream); } void SavedAsset::WriteToDataStream(OutputDataStream& stream) const { ::OperateStreamForSavedAsset(*this, stream); } Asset::Asset() = default; class AssetList::Private { public: Project* ConnectedProject; tsl::array_map Assets; tsl::array_map> Cache; int CacheSizeLimit = 0; struct { std::string NewName; NameSelectionError NewNameError = NameSelectionError::Empty; void ShowErrors() const { switch (NewNameError) { case NameSelectionError::None: break; case NameSelectionError::Duplicated: ImGui::ErrorMessage(I18N_TEXT("Duplicate name", L10N_DUPLICATE_NAME_ERROR)); break; case NameSelectionError::Empty: ImGui::ErrorMessage(I18N_TEXT("Name cannot be empty", L10N_EMPTY_NAME_ERROR)); break; } } bool HasErrors() const { return NewNameError != NameSelectionError::None; } void Validate(const AssetList& self) { if (NewName.empty()) { NewNameError = NameSelectionError::Empty; return; } if (self.FindByName(NewName)) { NewNameError = NameSelectionError::Duplicated; return; } NewNameError = NameSelectionError::None; } } PopupPrivateState; }; 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 AssetList::~AssetList() { } Project& AssetList::GetConnectedProject() const { return *mPrivate->ConnectedProject; } void AssetList::Reload() { DiscoverFiles([this](SavedAsset asset) -> void { Create(std::move(asset)); }); } int AssetList::GetCount() const { return mPrivate->Assets.size(); } const tsl::array_map& AssetList::GetAssets() const { return mPrivate->Assets; } const SavedAsset* AssetList::FindByName(std::string_view name) const { auto iter = mPrivate->Assets.find(name); if (iter != mPrivate->Assets.end()) { return &iter.value(); } else { return nullptr; } } const SavedAsset& AssetList::Create(SavedAsset asset) { auto [iter, DISCARD] = mPrivate->Assets.insert(asset.Name, SavedAsset{}); auto& savedAsset = iter.value(); savedAsset = std::move(asset); if (savedAsset.Uuid.is_nil()) { savedAsset.Uuid = uuids::uuid_random_generator{}(); } SaveInstance(savedAsset, nullptr); return savedAsset; } std::unique_ptr AssetList::CreateAndLoad(SavedAsset assetIn) { auto& savedAsset = Create(std::move(assetIn)); auto asset = std::unique_ptr(CreateInstance(savedAsset)); return asset; } std::unique_ptr AssetList::Load(std::string_view name) const { if (auto savedAsset = FindByName(name)) { auto asset = Load(*savedAsset); return asset; } else { return nullptr; } } std::unique_ptr AssetList::Load(const SavedAsset& asset) const { return std::unique_ptr(LoadInstance(asset)); } const SavedAsset* AssetList::Rename(std::string_view oldName, std::string_view newName) { auto iter = mPrivate->Assets.find(oldName); if (iter == mPrivate->Assets.end()) return nullptr; auto info = std::move(iter.value()); info.Name = newName; RenameInstanceOnDisk(info, oldName); mPrivate->Assets.erase(iter); auto [newIter, DISCARD] = mPrivate->Assets.insert(newName, std::move(info)); return &newIter.value(); } bool AssetList::Remove(std::string_view name) { auto iter = mPrivate->Assets.find(name); if (iter == mPrivate->Assets.end()) { return false; } auto& asset = iter.value(); fs::remove(RetrievePathFromAsset(asset)); mPrivate->Assets.erase(iter); return true; } int AssetList::GetCacheSizeLimit() const { return mPrivate->CacheSizeLimit; } void AssetList::SetCacheSizeLimit(int limit) { mPrivate->CacheSizeLimit = limit; } void AssetList::DisplayIconsList(ListState& state) { // TODO } void AssetList::DisplayDetailsList(ListState& state) { // Note: stub function remained here in case any state processing needs to be done before issuing to implementers DisplayDetailsTable(state); } void AssetList::DisplayControls(ListState& state) { auto& ps = mPrivate->PopupPrivateState; bool openedDummy = true; if (ImGui::Button(ICON_FA_PLUS " " I18N_TEXT("Add", L10N_ADD))) { ImGui::OpenPopup(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE)); } if (ImGui::BeginPopupModal(I18N_TEXT("Add asset wizard", L10N_ADD_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { DisplayAssetCreator(state); ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_I_CURSOR " " I18N_TEXT("Rename", L10N_RENAME), state.SelectedAsset == nullptr)) { ImGui::OpenPopup(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE)); } if (ImGui::BeginPopupModal(I18N_TEXT("Rename asset wizard", L10N_RENAME_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::InputText(I18N_TEXT("Name", L10N_NAME), &ps.NewName)) { ps.Validate(*this); } if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM), ps.HasErrors())) { ImGui::CloseCurrentPopup(); auto movedAsset = Rename(state.SelectedAsset->Name, ps.NewName); // Update the selected pointer to the new location (we mutated the map, the pointer may be invalid now) state.SelectedAsset = movedAsset; ps = {}; } ImGui::SameLine(); if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { ImGui::CloseCurrentPopup(); } ps.ShowErrors(); ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_TRASH " " I18N_TEXT("Delete", L10N_DELETE), state.SelectedAsset == nullptr)) { ImGui::OpenPopup(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE)); } if (ImGui::BeginPopupModal(I18N_TEXT("Delete asset", L10N_DELETE_ASSET_DIALOG_TITLE), &openedDummy, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::Button(I18N_TEXT("Confirm", L10N_CONFIRM))) { ImGui::CloseCurrentPopup(); auto& assetName = state.SelectedAsset->Name; Remove(assetName); state.SelectedAsset = nullptr; } ImGui::SameLine(); if (ImGui::Button(I18N_TEXT("Cancel", L10N_CANCEL))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } 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 }