diff --git a/src/olympic/studio/applib/src/CMakeLists.txt b/src/olympic/studio/applib/src/CMakeLists.txt index 555550ef..5fc2a173 100644 --- a/src/olympic/studio/applib/src/CMakeLists.txt +++ b/src/olympic/studio/applib/src/CMakeLists.txt @@ -6,6 +6,7 @@ add_library( font.cpp popups/about.cpp popups/deleteconfirmation.cpp + popups/fileinfo.cpp popups/makecopy.cpp popups/newdir.cpp popups/newmenu.cpp diff --git a/src/olympic/studio/applib/src/popups/fileinfo.cpp b/src/olympic/studio/applib/src/popups/fileinfo.cpp new file mode 100644 index 00000000..1ffaf206 --- /dev/null +++ b/src/olympic/studio/applib/src/popups/fileinfo.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include "fileinfo.hpp" + +namespace studio { + +FileInfo::FileInfo(Context &sctx) noexcept: + Popup("File Info"), + m_sctx(sctx) {} + +ox::Error FileInfo::open(ox::StringParam filePath) noexcept { + m_filePath = std::move(filePath); + auto &fs = m_sctx.project->romFs(); + auto const fileBuff = fs.read(m_filePath).or_value({}); + auto hdr = keel::readAssetHeader(fileBuff).or_value({}); + m_fileInfo.id = hdr.uuid.toString(); + m_fileInfo.typeName = std::move(hdr.clawHdr.typeName); + m_fileInfo.typeVersion = hdr.clawHdr.typeVersion; + m_fileInfo.format = [&hdr] { + switch (hdr.clawHdr.fmt) { + case ox::ClawFormat::Metal: + return ox::StringLiteral{"Metal Claw"}; + case ox::ClawFormat::Organic: + return ox::StringLiteral{"Organic Claw"}; + default: + return ox::StringLiteral{"Other"}; + } + }(); + m_fileInfo.dataSize = hdr.clawHdr.dataSize; + Popup::open(); + return {}; +} + +void FileInfo::draw(Context &sctx) noexcept { + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + close(); + return; + } + switch (m_stage) { + case Stage::Closed: + break; + case Stage::Opening: + ImGui::OpenPopup(m_title.c_str()); + m_stage = Stage::Open; + [[fallthrough]]; + case Stage::Open: { + constexpr auto modalFlags = + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; + ig::centerNextWindow(sctx.tctx); + auto open = true; + if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) { + drawTable(); + switch (auto const r = ig::PopupControlsOk(m_open, "Close")) { + case ig::PopupResponse::None: + break; + case ig::PopupResponse::OK: + response.emit(r); + close(); + break; + case ig::PopupResponse::Cancel: + break; + } + ImGui::EndPopup(); + } + if (!open) { + m_stage = Stage::Closed; + } + break; + } + } +} + +void FileInfo::drawTable() const noexcept { + ig::IDStackItem const idStackItem{"FileInfo"}; + ImGui::Text("%s", m_filePath.c_str()); + if (ImGui::BeginTable( + "Table", 2, + ImGuiTableFlags_Borders | + ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthFixed, 70); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide); + // id + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("ID:"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", m_fileInfo.id.c_str()); + // typeName + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Type Name:"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", m_fileInfo.typeName.c_str()); + // typeVersion + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Type Version:"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%d", m_fileInfo.typeVersion); + // format + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Format:"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", m_fileInfo.format.c_str()); + // size + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Data Size:"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%lluB", m_fileInfo.dataSize); + ImGui::EndTable(); + } +} + +} diff --git a/src/olympic/studio/applib/src/popups/fileinfo.hpp b/src/olympic/studio/applib/src/popups/fileinfo.hpp new file mode 100644 index 00000000..193f22ba --- /dev/null +++ b/src/olympic/studio/applib/src/popups/fileinfo.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include +#include + +namespace studio { + +class FileInfo: public ig::Popup { + private: + Context &m_sctx; + ox::String m_filePath; + struct { + ox::UUIDStr id; + ox::String typeName; + int typeVersion{}; + ox::StringLiteral format; + size_t dataSize{}; + } m_fileInfo; + + public: + explicit FileInfo(Context &sctx) noexcept; + + ox::Error open(ox::StringParam filePath) noexcept; + + void draw(Context &sctx) noexcept override; + + private: + void drawTable() const noexcept; + +}; + +} \ No newline at end of file diff --git a/src/olympic/studio/applib/src/projectexplorer.cpp b/src/olympic/studio/applib/src/projectexplorer.cpp index 6eb90748..c2890071 100644 --- a/src/olympic/studio/applib/src/projectexplorer.cpp +++ b/src/olympic/studio/applib/src/projectexplorer.cpp @@ -36,6 +36,13 @@ void ProjectExplorer::dirMoved(ox::StringViewCR src, ox::StringViewCR dst) const void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept { if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::MenuItem("Open")) { + fileChosen.emit(path); + } + if (ImGui::MenuItem("Get Info")) { + getInfo.emit(path); + } + ImGui::Separator(); if (ImGui::MenuItem("Delete")) { deleteItem.emit(path); } diff --git a/src/olympic/studio/applib/src/projectexplorer.hpp b/src/olympic/studio/applib/src/projectexplorer.hpp index 74bcaef2..e749880d 100644 --- a/src/olympic/studio/applib/src/projectexplorer.hpp +++ b/src/olympic/studio/applib/src/projectexplorer.hpp @@ -21,6 +21,7 @@ class ProjectExplorer final: public FileExplorer { ox::Signal deleteItem; ox::Signal renameItem; ox::Signal makeCopy; + ox::Signal getInfo; ox::Signal moveDir; ox::Signal moveItem; diff --git a/src/olympic/studio/applib/src/studioui.cpp b/src/olympic/studio/applib/src/studioui.cpp index 3b910457..3f09cbe0 100644 --- a/src/olympic/studio/applib/src/studioui.cpp +++ b/src/olympic/studio/applib/src/studioui.cpp @@ -146,6 +146,7 @@ StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexc m_projectExplorer.addDir.connect(this, &StudioUI::addDir); m_projectExplorer.addItem.connect(this, &StudioUI::addFile); m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile); + m_projectExplorer.getInfo.connect(this, &StudioUI::getFileInfo); m_projectExplorer.renameItem.connect(this, &StudioUI::renameFile); m_projectExplorer.makeCopy.connect(this, &StudioUI::makeCopyDlg); m_projectExplorer.moveDir.connect(this, &StudioUI::queueDirMove); @@ -569,6 +570,10 @@ ox::Error StudioUI::deleteFile(ox::StringViewCR path) noexcept { return {}; } +ox::Error StudioUI::getFileInfo(ox::StringViewCR path) noexcept { + return m_fileInfo.open(path); +} + ox::Error StudioUI::renameFile(ox::StringViewCR path) noexcept { return m_renameFile.openPath(path); } diff --git a/src/olympic/studio/applib/src/studioui.hpp b/src/olympic/studio/applib/src/studioui.hpp index dc758786..618350be 100644 --- a/src/olympic/studio/applib/src/studioui.hpp +++ b/src/olympic/studio/applib/src/studioui.hpp @@ -21,6 +21,7 @@ #include "popups/newmenu.hpp" #include "popups/newproject.hpp" #include "projectexplorer.hpp" +#include "popups/fileinfo.hpp" #include "popups/renamefile.hpp" namespace studio { @@ -48,6 +49,7 @@ class StudioUI final: public ox::SignalHandler { NewMenu m_newMenu{keelCtx(m_tctx)}; AboutPopup m_aboutPopup{m_tctx}; DeleteConfirmation m_deleteConfirmation; + FileInfo m_fileInfo{m_sctx}; NewDir m_newDirDialog; ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"}; ig::QuestionPopup m_closeAppConfirm{ @@ -62,10 +64,11 @@ class StudioUI final: public ox::SignalHandler { ig::QuestionPopup dlg{"Remove From Recents?", "Unable to load project. Remove from recent projects?"}; size_t idx{}; } m_removeRecentProject; - ox::Array const m_widgets { + ox::Array const m_widgets { &m_closeFileConfirm, &m_closeAppConfirm, &m_copyFilePopup, + &m_fileInfo, &m_newMenu, &m_newProject, &m_aboutPopup, @@ -130,6 +133,8 @@ class StudioUI final: public ox::SignalHandler { ox::Error deleteFile(ox::StringViewCR path) noexcept; + ox::Error getFileInfo(ox::StringViewCR path) noexcept; + ox::Error renameFile(ox::StringViewCR path) noexcept; ox::Error handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const &id) noexcept;