From 449bcf87893e82f31a3f176aa63ec2077a0c3bf2 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Mon, 27 Jan 2025 02:22:07 -0600 Subject: [PATCH] Squashed 'deps/nostalgia/' changes from 6298ac3a..95256a9a 95256a9a [studio] Make rename file give error message if the file already exists 2286238a [studio] Make rename file accept input upon pressing Enter if text input is focused 13f0bf57 [studio] Make deleting a file close tabs associated with it 8eb1ac21 [studio] Fix not to try moving a parent directory to its child e132f2fd [studio] Make file move do nothing if the file already exists 12f6b22c [nostalgia/gfx/studio/palette] Cleanup 6c858e0c [nostalgia/gfx/studio/tilesheet] UI cleanup c6b58f7c [nostalgia] Update release notes a22aafaf [nostalgia/gfx/studio/palette] Add ability to reorder Palette pages git-subtree-dir: deps/nostalgia git-subtree-split: 95256a9a0dc2d103281055636b099bf1eb317b69 --- release-notes.md | 1 + .../src/studio/paletteeditor/CMakeLists.txt | 16 +------- .../paletteeditor/commands/CMakeLists.txt | 15 +++++++ .../paletteeditor/commands/commands.hpp | 1 + .../commands/movepagecommand.cpp | 40 +++++++++++++++++++ .../commands/movepagecommand.hpp | 35 ++++++++++++++++ .../paletteeditor/paletteeditor-imgui.cpp | 22 ++++++++++ .../tilesheeteditor/tilesheeteditor-imgui.cpp | 7 ++-- .../tilesheeteditor/tilesheeteditor-imgui.hpp | 2 +- src/olympic/studio/applib/src/renamefile.cpp | 27 ++++++++++--- src/olympic/studio/applib/src/renamefile.hpp | 1 + src/olympic/studio/applib/src/studioapp.cpp | 22 ++++++++-- src/olympic/studio/applib/src/studioapp.hpp | 2 + src/olympic/studio/modlib/src/imguiutil.cpp | 2 - src/olympic/studio/modlib/src/project.cpp | 7 ++++ 15 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/CMakeLists.txt create mode 100644 src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.cpp create mode 100644 src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.hpp diff --git a/release-notes.md b/release-notes.md index 7c31d92..e9c37a9 100644 --- a/release-notes.md +++ b/release-notes.md @@ -14,3 +14,4 @@ * Ctrl- keyboard shortcuts for jumping between tabs. * Fix Palette Editor to ignore keyboard input when popups are open. * Palette Editor move color mechanism now uses drag and drop. +* Add ability to reorder Palette pages. diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/CMakeLists.txt b/src/nostalgia/modules/gfx/src/studio/paletteeditor/CMakeLists.txt index cdaf9d3..25e3a79 100644 --- a/src/nostalgia/modules/gfx/src/studio/paletteeditor/CMakeLists.txt +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/CMakeLists.txt @@ -1,18 +1,6 @@ -target_sources( - NostalgiaCore-Studio PRIVATE - commands/addcolorcommand.cpp - commands/addpagecommand.cpp - commands/applycolorallpagescommand.cpp - commands/duplicatepagecommand.cpp - commands/movecolorcommand.cpp - commands/removecolorcommand.cpp - commands/removepagecommand.cpp - commands/renamepagecommand.cpp - commands/updatecolorcommand.cpp - commands/updatecolorinfocommand.cpp -) - target_sources( NostalgiaCore-Studio-ImGui PRIVATE paletteeditor-imgui.cpp ) + +add_subdirectory(commands) \ No newline at end of file diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/CMakeLists.txt b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/CMakeLists.txt new file mode 100644 index 0000000..42c4cd5 --- /dev/null +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/CMakeLists.txt @@ -0,0 +1,15 @@ + +target_sources( + NostalgiaCore-Studio PRIVATE + addcolorcommand.cpp + addpagecommand.cpp + applycolorallpagescommand.cpp + duplicatepagecommand.cpp + movecolorcommand.cpp + movepagecommand.cpp + removecolorcommand.cpp + removepagecommand.cpp + renamepagecommand.cpp + updatecolorcommand.cpp + updatecolorinfocommand.cpp +) diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/commands.hpp b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/commands.hpp index 7ca6ee6..666a0f2 100644 --- a/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/commands.hpp +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/commands.hpp @@ -12,6 +12,7 @@ enum class PaletteEditorCommandId { AddPage, DuplicatePage, RemovePage, + MovePage, AddColor, RemoveColor, UpdateColorInfo, diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.cpp b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.cpp new file mode 100644 index 0000000..86a9c80 --- /dev/null +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include "commands.hpp" + +#include "movepagecommand.hpp" + +namespace nostalgia::gfx { + +MovePageCommand::MovePageCommand( + Palette &pal, + size_t const srcIdx, + size_t const dstIdx) noexcept: + m_pal(pal), + m_srcIdx(srcIdx), + m_dstIdx(dstIdx) {} + +int MovePageCommand::commandId() const noexcept { + return static_cast(PaletteEditorCommandId::MovePage); +} + +ox::Error MovePageCommand::redo() noexcept { + movePage(m_srcIdx, m_dstIdx); + return {}; +} + +ox::Error MovePageCommand::undo() noexcept { + movePage(m_dstIdx, m_srcIdx); + return {}; +} + +void MovePageCommand::movePage( + size_t const srcIdx, size_t const dstIdx) noexcept { + auto page = std::move(m_pal.pages[srcIdx]); + std::ignore = m_pal.pages.erase(srcIdx); + m_pal.pages.emplace(dstIdx, std::move(page)); +} + +} diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.hpp b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.hpp new file mode 100644 index 0000000..0b42161 --- /dev/null +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/commands/movepagecommand.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include + +namespace nostalgia::gfx { + +class MovePageCommand: public studio::UndoCommand { + private: + Palette &m_pal; + std::size_t const m_srcIdx = 0; + std::size_t const m_dstIdx = 0; + + public: + MovePageCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept; + + ~MovePageCommand() noexcept override = default; + + [[nodiscard]] + int commandId() const noexcept override; + + ox::Error redo() noexcept override; + + ox::Error undo() noexcept override; + + private: + void movePage(size_t srcIdx, size_t dstIdx) noexcept; +}; + +} diff --git a/src/nostalgia/modules/gfx/src/studio/paletteeditor/paletteeditor-imgui.cpp b/src/nostalgia/modules/gfx/src/studio/paletteeditor/paletteeditor-imgui.cpp index 28d1275..8a43307 100644 --- a/src/nostalgia/modules/gfx/src/studio/paletteeditor/paletteeditor-imgui.cpp +++ b/src/nostalgia/modules/gfx/src/studio/paletteeditor/paletteeditor-imgui.cpp @@ -11,6 +11,7 @@ #include "commands/applycolorallpagescommand.hpp" #include "commands/duplicatepagecommand.hpp" #include "commands/movecolorcommand.hpp" +#include "commands/movepagecommand.hpp" #include "commands/removecolorcommand.hpp" #include "commands/removepagecommand.hpp" #include "commands/renamepagecommand.hpp" @@ -19,6 +20,7 @@ #include "paletteeditor-imgui.hpp" + namespace nostalgia::gfx { namespace ig = studio::ig; @@ -33,6 +35,16 @@ OX_MODEL_BEGIN(ColorDragDrop) OX_MODEL_FIELD(i) OX_MODEL_END() +struct PageDragDrop { + static constexpr auto TypeName = "nostalgia.gfx.PageDragDrop"; + static constexpr auto TypeVersion = 1; + uint32_t page{}; +}; + +OX_MODEL_BEGIN(PageDragDrop) + OX_MODEL_FIELD(page) +OX_MODEL_END() + void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept { if (!m_show) { return; @@ -235,6 +247,16 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) { m_page = i; } + std::ignore = ig::dragDropSource([this, i] { + ImGui::Text("%s", m_pal.pages[i].name.c_str()); + return ig::setDragDropPayload(PageDragDrop{i}); + }, ImGuiDragDropFlags_SourceAllowNullID); + if (ig::DragDropTarget const d; d) { + auto const [src, err] = ig::getDragDropPayload(); + if (!err) { + std::ignore = pushCommand(m_pal, src.page, i); + } + } } } ImGui::EndTable(); diff --git a/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp b/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp index d9677cc..ba6838a 100644 --- a/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp @@ -214,7 +214,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { ImGui::BeginChild("Controls", {s_palViewWidth - 8, paneSize.y}, true); { auto const controlsSize = ImGui::GetContentRegionAvail(); - ImGui::BeginChild("ToolBox", {s_palViewWidth - 24, 30}, true); + ImGui::BeginChild("ToolBox", {168, 32}, true); { auto const btnSz = ImVec2{45, 14}; if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) { @@ -232,9 +232,10 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { } } ImGui::EndChild(); - ImGui::BeginChild("OperationsBox", {s_palViewWidth - 24, ig::BtnSz.y + 17}, true); + ImGui::SameLine(); + ImGui::BeginChild("OperationsBox", {0, 32}, true); { - auto constexpr btnSz = ImVec2{55, ig::BtnSz.y}; + auto constexpr btnSz = ImVec2{55, 16}; if (ig::PushButton("Flip X", btnSz)) { oxLogError(m_model.flipX()); } diff --git a/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp b/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp index d8e3cac..f338408 100644 --- a/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp @@ -45,7 +45,7 @@ class TileSheetEditorImGui: public studio::Editor { [[nodiscard]] constexpr bool isOpen() const noexcept { return m_show; } }; - static constexpr float s_palViewWidth = 300; + static constexpr float s_palViewWidth = 335; studio::StudioContext &m_sctx; turbine::Context &m_tctx; ox::Vector m_paletteList; diff --git a/src/olympic/studio/applib/src/renamefile.cpp b/src/olympic/studio/applib/src/renamefile.cpp index b4c633c..73a2ff1 100644 --- a/src/olympic/studio/applib/src/renamefile.cpp +++ b/src/olympic/studio/applib/src/renamefile.cpp @@ -41,18 +41,35 @@ void RenameFile::draw(StudioContext &ctx) noexcept { case Stage::Opening: ImGui::OpenPopup(title().c_str()); m_open = true; + m_fileExists = false; m_stage = Stage::Open; [[fallthrough]]; case Stage::Open: setSize({250, 0}); - drawWindow(ctx.tctx, m_open, [this] { + drawWindow(ctx.tctx, m_open, [this, &ctx] { if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } - ig::InputText("Name", m_name); - ImGui::Text("%s%s", m_path.c_str(), m_name.c_str()); - if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) { - moveFile.emit(m_oldPath, m_path + m_name); + m_fileExists = !ig::InputText("Name", m_name) && m_fileExists; + auto const nameInputFocused = ImGui::IsItemFocused(); + if (m_fileExists) { + ImGui::Text("File %s already exists.", m_name.c_str()); + } else { + ImGui::Text("%s%s", m_path.c_str(), m_name.c_str()); + } + bool b{}; + auto const response = ig::PopupControlsOkCancel(b); + if (response == ig::PopupResponse::OK || + (nameInputFocused && ImGui::IsKeyPressed(ImGuiKey_Enter))) { + auto const newPath = m_path + m_name; + if (!ctx.project->exists(newPath)) { + moveFile.emit(m_oldPath, newPath); + close(); + } else { + m_open = true; + m_fileExists = true; + } + } else if (response == ig::PopupResponse::Cancel) { close(); } }); diff --git a/src/olympic/studio/applib/src/renamefile.hpp b/src/olympic/studio/applib/src/renamefile.hpp index 2af0326..4625118 100644 --- a/src/olympic/studio/applib/src/renamefile.hpp +++ b/src/olympic/studio/applib/src/renamefile.hpp @@ -21,6 +21,7 @@ class RenameFile: public Popup { ox::String m_oldPath; ox::String m_path; ox::IString<255> m_name; + bool m_fileExists{}; bool m_open{}; public: diff --git a/src/olympic/studio/applib/src/studioapp.cpp b/src/olympic/studio/applib/src/studioapp.cpp index 3b45c2d..3cbe35a 100644 --- a/src/olympic/studio/applib/src/studioapp.cpp +++ b/src/olympic/studio/applib/src/studioapp.cpp @@ -392,6 +392,18 @@ ox::Error StudioUI::handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR ne return m_projectExplorer.refreshProjectTreeModel(); } +ox::Error StudioUI::handleDeleteFile(ox::StringViewCR path) noexcept { + for (size_t i{}; auto &e : m_editors) { + if (path == e->itemPath()) { + oxLogError(m_editors.erase(i).error); + oxLogError(closeFile(path)); + break; + } + ++i; + } + return m_projectExplorer.refreshProjectTreeModel(); +} + ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept { std::error_code ec; std::filesystem::create_directories(toStdStringView(path), ec); @@ -412,7 +424,7 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept { m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir); m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); - m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); + m_project->fileDeleted.connect(this, &StudioUI::handleDeleteFile); m_project->fileMoved.connect(this, &StudioUI::handleMoveFile); m_openFiles.clear(); m_editors.clear(); @@ -495,11 +507,15 @@ ox::Error StudioUI::queueFileMove(ox::StringParam src, ox::StringParam dst) noex void StudioUI::procFileMoves() noexcept { for (auto const &m : m_queuedMoves) { - oxLogError(m_project->moveItem(m.a, m.b)); + if (!m_project->exists(m.b)) { + oxLogError(m_project->moveItem(m.a, m.b)); + } } m_queuedMoves.clear(); for (auto const &m : m_queuedDirMoves) { - oxLogError(m_project->moveDir(m.a, m.b)); + if (!m_project->exists(m.b)) { + oxLogError(m_project->moveDir(m.a, m.b)); + } } m_queuedDirMoves.clear(); } diff --git a/src/olympic/studio/applib/src/studioapp.hpp b/src/olympic/studio/applib/src/studioapp.hpp index c2e6eb3..6dfc730 100644 --- a/src/olympic/studio/applib/src/studioapp.hpp +++ b/src/olympic/studio/applib/src/studioapp.hpp @@ -104,6 +104,8 @@ class StudioUI: public ox::SignalHandler { ox::Error handleMoveFile(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const&id) noexcept; + ox::Error handleDeleteFile(ox::StringViewCR path) noexcept; + ox::Error createOpenProject(ox::StringViewCR path) noexcept; ox::Error openProjectPath(ox::StringParam path) noexcept; diff --git a/src/olympic/studio/modlib/src/imguiutil.cpp b/src/olympic/studio/modlib/src/imguiutil.cpp index d3af2aa..1254978 100644 --- a/src/olympic/studio/modlib/src/imguiutil.cpp +++ b/src/olympic/studio/modlib/src/imguiutil.cpp @@ -64,13 +64,11 @@ PopupResponse PopupControlsOkCancel( ImGui::Separator(); ImGui::SetCursorPosX(popupWidth - 118); if (ImGui::Button(ok.c_str(), btnSz)) { - ImGui::CloseCurrentPopup(); popupOpen = false; out = PopupResponse::OK; } ImGui::SameLine(); if (ImGui::IsKeyDown(ImGuiKey_Escape) || ImGui::Button(cancel.c_str(), btnSz)) { - ImGui::CloseCurrentPopup(); popupOpen = false; out = PopupResponse::Cancel; } diff --git a/src/olympic/studio/modlib/src/project.cpp b/src/olympic/studio/modlib/src/project.cpp index 1aa311a..609c1d1 100644 --- a/src/olympic/studio/modlib/src/project.cpp +++ b/src/olympic/studio/modlib/src/project.cpp @@ -46,6 +46,10 @@ static ox::Result> listAllRecursive(ox::FileSystem &fs, o return std::move(out); } +[[nodiscard]] +static constexpr bool isParentOf(ox::StringViewCR parent, ox::StringViewCR child) noexcept { + return beginsWith(child, parent); +} Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir): m_kctx(ctx), @@ -99,6 +103,9 @@ ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcep } ox::Error Project::moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept { + if (isParentOf(src, dest)) { + return ox::Error{1, "cannot move parent to a child directory"}; + } OX_REQUIRE(files, listAllRecursive(m_fs, src)); OX_RETURN_ERROR(m_fs.move(src, dest)); fileMoved.emit(src, dest, ox::UUID{});