From 52533c8c44dc2884c63a75e47de09379bba58aa9 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 6 Sep 2024 21:35:09 -0500 Subject: [PATCH] [nostalgia/core/studio] Add Palette page names to editor --- .../paletteeditor/paletteeditor-imgui.cpp | 41 ++++++++++++++++--- .../paletteeditor/paletteeditor-imgui.hpp | 19 +++++++++ .../studio/paletteeditor/paletteeditor.cpp | 20 +++++++++ .../studio/paletteeditor/paletteeditor.hpp | 21 ++++++++++ src/olympic/studio/applib/src/aboutpopup.cpp | 9 ++-- .../studio/modlib/include/studio/context.hpp | 2 +- .../modlib/include/studio/imguiutil.hpp | 27 +++++++++--- src/olympic/studio/modlib/src/imguiutil.cpp | 34 ++++++--------- 8 files changed, 136 insertions(+), 37 deletions(-) diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp index 1d62bf6a..7a4882a6 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp @@ -4,8 +4,6 @@ #include -#include - #include #include @@ -17,6 +15,27 @@ namespace nostalgia::core { namespace ig = studio::ig; + +void PaletteEditorImGui::PageRename::draw(turbine::Context &tctx) noexcept { + if (!m_show) { + return; + } + if (ig::BeginPopup(tctx, "Rename Page", m_show)) { + ig::InputText("Name", m_name); + switch (ig::PopupControlsOkCancel(m_show)) { + case ig::PopupResponse::OK: + inputSubmitted.emit(m_name); + [[fallthrough]]; + case ig::PopupResponse::Cancel: + close(); + default: + break; + } + ImGui::EndPopup(); + } +} + + PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringParam path): Editor(std::move(path)), m_sctx(sctx), @@ -26,6 +45,7 @@ PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringPa throw OxException(1, "PaletteEditorImGui: invalid Palette object"); } undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand); + m_pageRename.inputSubmitted.connect(this, &PaletteEditorImGui::renamePage); } void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) { @@ -49,7 +69,7 @@ void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) { void PaletteEditorImGui::draw(studio::StudioContext&) noexcept { auto const paneSize = ImGui::GetContentRegionAvail(); { - ImGui::BeginChild("Pages", {250, paneSize.y}, true); + ImGui::BeginChild("Pages", {280, paneSize.y}, true); drawPagesEditor(); ImGui::EndChild(); } @@ -59,6 +79,7 @@ void PaletteEditorImGui::draw(studio::StudioContext&) noexcept { drawColorsEditor(); ImGui::EndChild(); } + m_pageRename.draw(m_tctx); } ox::Error PaletteEditorImGui::saveItem() noexcept { @@ -166,7 +187,7 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { constexpr auto tableFlags = ImGuiTableFlags_RowBg; auto const paneSz = ImGui::GetContentRegionAvail(); constexpr auto toolbarHeight = 40; - auto const btnSz = ImVec2{paneSz.x / 3 - 5.5f, 24}; + auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24}; if (ImGui::Button("Add", btnSz)) { std::ignore = pushCommand(m_pal, 0u, m_pal.pages.size()); m_page = m_pal.pages.size() - 1; @@ -177,9 +198,13 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { m_page = std::min(m_page, m_pal.pages.size() - 1); } ImGui::SameLine(); - if (ImGui::Button("Duplicate", btnSz)) { + if (ImGui::Button("Clone", btnSz)) { std::ignore = pushCommand(m_pal, m_page, m_pal.pages.size()); } + ImGui::SameLine(); + if (ImGui::Button("Rename", btnSz)) { + m_pageRename.show(m_pal.pages[m_page].name); + } ImGui::BeginTable( "PageSelect", 2, @@ -187,11 +212,13 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { {paneSz.x, paneSz.y - (toolbarHeight + 5)}); { ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60); + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 200); ImGui::TableHeadersRow(); for (auto i = 0u; i < m_pal.pages.size(); ++i) { ig::IDStackItem const idStackItem(static_cast(i)); ImGui::TableNextRow(); drawColumn(i + 1); + drawColumnLeftAlign(m_pal.pages[i].name); ImGui::SameLine(); if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) { m_page = i; @@ -232,6 +259,10 @@ void PaletteEditorImGui::drawColorEditor() noexcept { } } +ox::Error PaletteEditorImGui::renamePage(ox::StringView name) noexcept { + return pushCommand(m_pal, m_page, name); +} + ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept { if (dynamic_cast(cmd)) { m_page = ox::min(m_page, m_pal.pages.size() - 1); diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp index 0289f059..feefa082 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp @@ -14,6 +14,23 @@ namespace nostalgia::core { class PaletteEditorImGui: public studio::Editor { private: + class PageRename { + private: + ox::IString<50> m_name; + bool m_show = false; + public: + ox::Signal inputSubmitted; + constexpr void show(ox::StringView const&name) noexcept { + m_show = true; + m_name = name; + } + constexpr void close() noexcept { + m_show = false; + } + [[nodiscard]] + constexpr bool isOpen() const noexcept { return m_show; } + void draw(turbine::Context &tctx) noexcept; + } m_pageRename; studio::StudioContext &m_sctx; turbine::Context &m_tctx; Palette m_pal; @@ -45,6 +62,8 @@ class PaletteEditorImGui: public studio::Editor { void drawColorEditor() noexcept; + ox::Error renamePage(ox::StringView name) noexcept; + ox::Error handleCommand(studio::UndoCommand const*) noexcept; }; diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp index b42d5261..854bc289 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp @@ -47,6 +47,26 @@ ox::Error ApplyColorAllPagesCommand::undo() noexcept { } +RenamePageCommand::RenamePageCommand(Palette &pal, size_t const page, ox::StringParam name) noexcept: + m_pal(pal), + m_page(page), + m_name{std::move(name)} {} + +int RenamePageCommand::commandId() const noexcept { + return static_cast(PaletteEditorCommandId::RenamePage); +} + +ox::Error RenamePageCommand::redo() noexcept { + std::swap(m_pal.pages[m_page].name, m_name); + return {}; +} + +ox::Error RenamePageCommand::undo() noexcept { + std::swap(m_pal.pages[m_page].name, m_name); + return {}; +} + + DuplicatePageCommand::DuplicatePageCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept: m_pal(pal), m_dstIdx(dstIdx) { diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp index 3e161449..60efb601 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp @@ -13,6 +13,7 @@ namespace nostalgia::core { enum class PaletteEditorCommandId { ApplyColorAllPages, + RenamePage, DuplicatePage, RemovePage, AddColor, @@ -43,6 +44,26 @@ class ApplyColorAllPagesCommand: public studio::UndoCommand { ox::Error undo() noexcept final; }; +class RenamePageCommand: public studio::UndoCommand { + private: + Palette &m_pal; + size_t m_page = 0; + ox::String m_name; + + public: + RenamePageCommand(Palette &pal, size_t page, ox::StringParam name) noexcept; + + ~RenamePageCommand() noexcept override = default; + + [[nodiscard]] + int commandId() const noexcept final; + + ox::Error redo() noexcept final; + + ox::Error undo() noexcept final; + +}; + class DuplicatePageCommand: public studio::UndoCommand { private: Palette &m_pal; diff --git a/src/olympic/studio/applib/src/aboutpopup.cpp b/src/olympic/studio/applib/src/aboutpopup.cpp index 6bbd2c7e..0a5ca956 100644 --- a/src/olympic/studio/applib/src/aboutpopup.cpp +++ b/src/olympic/studio/applib/src/aboutpopup.cpp @@ -29,7 +29,7 @@ bool AboutPopup::isOpen() const noexcept { return m_stage == Stage::Open; } -void AboutPopup::draw(studio::StudioContext &sctx) noexcept { +void AboutPopup::draw(StudioContext &sctx) noexcept { switch (m_stage) { case Stage::Closed: break; @@ -40,15 +40,14 @@ void AboutPopup::draw(studio::StudioContext &sctx) noexcept { case Stage::Open: { constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - ImGui::SetNextWindowSize(ImVec2(215, 90)); - studio::ig::centerNextWindow(sctx.tctx); + ig::centerNextWindow(sctx.tctx); auto open = true; if (ImGui::BeginPopupModal("About", &open, modalFlags)) { ImGui::Text("%s", m_text.c_str()); ImGui::NewLine(); - ImGui::Dummy(ImVec2(148.0f, 0.0f)); + ImGui::Dummy({148.0f, 0.0f}); ImGui::SameLine(); - if (ImGui::Button("Close")) { + if (ig::PushButton("Close")) { ImGui::CloseCurrentPopup(); open = false; } diff --git a/src/olympic/studio/modlib/include/studio/context.hpp b/src/olympic/studio/modlib/include/studio/context.hpp index 21cb042c..16ca8d13 100644 --- a/src/olympic/studio/modlib/include/studio/context.hpp +++ b/src/olympic/studio/modlib/include/studio/context.hpp @@ -18,7 +18,7 @@ struct StudioContext { StudioUI &ui; Project *project = nullptr; turbine::Context &tctx; - inline StudioContext(StudioUI &pUi, turbine::Context &pTctx) noexcept: + StudioContext(StudioUI &pUi, turbine::Context &pTctx) noexcept: ui(pUi), tctx(pTctx) {} }; diff --git a/src/olympic/studio/modlib/include/studio/imguiutil.hpp b/src/olympic/studio/modlib/include/studio/imguiutil.hpp index bab5e7d8..e0053901 100644 --- a/src/olympic/studio/modlib/include/studio/imguiutil.hpp +++ b/src/olympic/studio/modlib/include/studio/imguiutil.hpp @@ -45,10 +45,10 @@ class DragDropSource { private: bool const m_active{}; public: - inline DragDropSource() noexcept: + DragDropSource() noexcept: m_active(ImGui::BeginDragDropSource()) { } - inline ~DragDropSource() noexcept { + ~DragDropSource() noexcept { if (m_active) { ImGui::EndDragDropSource(); } @@ -58,7 +58,7 @@ class DragDropSource { } }; -inline auto dragDropSource(auto const&cb) noexcept { +auto dragDropSource(auto const&cb) noexcept { if constexpr(ox::is_same_v) { if (ig::DragDropSource const tgt; tgt) [[unlikely]] { return cb(); @@ -75,10 +75,10 @@ class DragDropTarget { private: bool const m_active{}; public: - inline DragDropTarget() noexcept: + DragDropTarget() noexcept: m_active(ImGui::BeginDragDropTarget()) { } - inline ~DragDropTarget() noexcept { + ~DragDropTarget() noexcept { if (m_active) { ImGui::EndDragDropTarget(); } @@ -128,6 +128,21 @@ void centerNextWindow(turbine::Context &ctx) noexcept; bool PushButton(ox::CStringView lbl, ImVec2 const&btnSz = BtnSz) noexcept; +template +bool InputText( + ox::CStringView label, + ox::IString &text, + ImGuiInputTextFlags const flags = 0, + ImGuiInputTextCallback const callback = nullptr, + void *user_data = nullptr) noexcept { + auto const out = ImGui::InputText( + label.c_str(), text.data(), StrCap + 1, flags, callback, user_data); + if (out) { + std::ignore = text.unsafeResize(ox::strlen(text.c_str())); + } + return out; +} + enum class PopupResponse { None, OK, @@ -136,6 +151,8 @@ enum class PopupResponse { PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen); +PopupResponse PopupControlsOkCancel(bool &popupOpen); + [[nodiscard]] bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, ImVec2 const&sz = {285, 0}); diff --git a/src/olympic/studio/modlib/src/imguiutil.cpp b/src/olympic/studio/modlib/src/imguiutil.cpp index 0af15b97..72c98988 100644 --- a/src/olympic/studio/modlib/src/imguiutil.cpp +++ b/src/olympic/studio/modlib/src/imguiutil.cpp @@ -73,9 +73,13 @@ PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) { return out; } +PopupResponse PopupControlsOkCancel(bool &popupOpen) { + return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen); +} + bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, ImVec2 const&sz) { constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - ig::centerNextWindow(ctx); + centerNextWindow(ctx); ImGui::OpenPopup(popupName.c_str()); ImGui::SetNextWindowSize(sz); return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags); @@ -122,23 +126,11 @@ bool ComboBox( bool FileComboBox( ox::CStringView lbl, - studio::StudioContext &sctx, + StudioContext &sctx, ox::StringView fileExt, size_t &selectedIdx) noexcept { auto const&list = sctx.project->fileList(fileExt); - bool out{}; - auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : ""; - if (ImGui::BeginCombo(lbl.c_str(), first, 0)) { - for (auto i = 0u; i < list.size(); ++i) { - const auto selected = (selectedIdx == i); - if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) { - selectedIdx = i; - out = true; - } - } - ImGui::EndCombo(); - } - return out; + return ComboBox(lbl, list, selectedIdx); } bool ListBox( @@ -164,14 +156,14 @@ bool ListBox( } bool ListBox(ox::CStringView name, ox::SpanView const&list, size_t &selIdx) noexcept { - return ig::ListBox(name, [list](size_t i) -> ox::CStringView { + return ListBox(name, [list](size_t i) -> ox::CStringView { return list[i]; }, list.size(), selIdx); } FilePicker::FilePicker( - studio::StudioContext &sctx, + StudioContext &sctx, ox::String title, ox::String fileExt, ImVec2 const&size) noexcept: @@ -186,12 +178,12 @@ void FilePicker::draw() noexcept { return; } auto constexpr popupSz = ImVec2{450.f, 0}; - ig::IDStackItem const idStackItem(m_title.c_str()); - if (ig::BeginPopup(m_sctx.tctx, m_title.c_str(), m_show, popupSz)) { + IDStackItem const idStackItem(m_title); + if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) { auto const&list = m_sctx.project->fileList(m_fileExt); size_t selIdx{}; - ig::ComboBox(m_title.c_str(), list, selIdx); - if (ig::PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) { + ComboBox(m_title, list, selIdx); + if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) { filePicked.emit(list[selIdx]); } ImGui::EndPopup();