From 6854e658a0e24773e6d36a98ab26f9815e624d97 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Wed, 18 May 2022 21:15:11 -0500 Subject: [PATCH] [nostalgia/studio] Fix TileSheetEditor to switch to appropriate subsheet on undo/redo --- src/nostalgia/core/CMakeLists.txt | 1 + src/nostalgia/core/assetmanager.hpp | 86 +++++++++++++++---- .../core/studio/tilesheeteditor-imgui.cpp | 4 +- .../core/studio/tilesheeteditor-imgui.hpp | 4 +- .../core/studio/tilesheeteditormodel.cpp | 79 +++++++++++++---- .../core/studio/tilesheeteditormodel.hpp | 4 +- .../core/studio/tilesheeteditorview.cpp | 5 ++ .../core/studio/tilesheeteditorview.hpp | 2 + src/nostalgia/studio/lib/editor.cpp | 2 +- src/nostalgia/studio/lib/editor.hpp | 2 +- src/nostalgia/studio/lib/undostack.cpp | 15 ++-- src/nostalgia/studio/lib/undostack.hpp | 6 +- 12 files changed, 158 insertions(+), 52 deletions(-) diff --git a/src/nostalgia/core/CMakeLists.txt b/src/nostalgia/core/CMakeLists.txt index 125a3d5c..9f273c2a 100644 --- a/src/nostalgia/core/CMakeLists.txt +++ b/src/nostalgia/core/CMakeLists.txt @@ -18,6 +18,7 @@ if(NOT NOSTALGIA_BUILD_TYPE STREQUAL "GBA") ${OPENGL_gl_LIBRARY} glfw imgui + OxEvent NostalgiaGlUtils ) else() diff --git a/src/nostalgia/core/assetmanager.hpp b/src/nostalgia/core/assetmanager.hpp index 8746c79e..b25d736c 100644 --- a/src/nostalgia/core/assetmanager.hpp +++ b/src/nostalgia/core/assetmanager.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -27,6 +28,8 @@ struct AssetContainer { mutable int m_references = 0; public: + ox::Signal updated; + template explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward(args)...) { } @@ -39,6 +42,14 @@ struct AssetContainer { return &m_obj; } + constexpr void set(T &&val) { + m_obj = std::move(val); + } + + constexpr void set(const T &val) { + m_obj = val; + } + protected: constexpr void incRefs() const noexcept { ++m_references; @@ -55,27 +66,20 @@ struct AssetContainer { }; template -class AssetRef { +class AssetRef: public ox::SignalHandler { private: const AssetContainer *m_ctr = nullptr; public: - explicit constexpr AssetRef(const AssetContainer *c = nullptr) noexcept: m_ctr(c) { - } + ox::Signal updated; - constexpr AssetRef(const AssetRef &h) noexcept { - m_ctr = h.m_ctr; - if (m_ctr) { - m_ctr->incRefs(); - } - } + explicit constexpr AssetRef(const AssetContainer *c = nullptr) noexcept; - constexpr AssetRef(AssetRef &&h) noexcept { - m_ctr = h.m_ctr; - h.m_ctr = nullptr; - } + constexpr AssetRef(const AssetRef &h) noexcept; - ~AssetRef() noexcept { + constexpr AssetRef(AssetRef &&h) noexcept; + + ~AssetRef() noexcept override { if (m_ctr) { m_ctr->decRefs(); } @@ -103,8 +107,10 @@ class AssetRef { } if (m_ctr) { m_ctr->decRefs(); + oxIgnoreError(m_ctr->updated.disconnectObject(this)); } m_ctr = h.m_ctr; + m_ctr->updated.connect(&updated, &ox::Signal::emitCheckError); if (m_ctr) { m_ctr->incRefs(); } @@ -115,7 +121,12 @@ class AssetRef { if (this == &h) { return *this; } + if (m_ctr) { + m_ctr->decRefs(); + oxIgnoreError(m_ctr->updated.disconnectObject(this)); + } m_ctr = h.m_ctr; + m_ctr->updated.connect(this, &AssetRef::emitUpdated); h.m_ctr = nullptr; return *this; } @@ -124,8 +135,36 @@ class AssetRef { return m_ctr; } + private: + constexpr ox::Error emitUpdated() const noexcept { + updated.emit(); + return OxError(0); + } }; +template +constexpr AssetRef::AssetRef(const AssetContainer *c) noexcept: m_ctr(c) { + if (c) { + c->updated.connect(this, &AssetRef::emitUpdated); + } +} + +template +constexpr AssetRef::AssetRef(const AssetRef &h) noexcept { + m_ctr = h.m_ctr; + if (m_ctr) { + m_ctr->updated.connect(this, &AssetRef::emitUpdated); + m_ctr->incRefs(); + } +} + +template +constexpr AssetRef::AssetRef(AssetRef &&h) noexcept { + m_ctr = h.m_ctr; + m_ctr->updated.connect(this, &AssetRef::emitUpdated); + h.m_ctr = nullptr; +} + class AssetManager { private: class AssetTypeManagerBase { @@ -148,7 +187,24 @@ class AssetManager { } ox::Result> setAsset(const ox::String &path, const T &obj) noexcept { - auto &p = m_cache[path] = ox::make_unique>(obj); + auto &p = m_cache[path]; + if (!p) { + p = ox::make_unique>(obj); + } else { + p->set(obj); + p->updated.emit(); + } + return AssetRef(p.get()); + } + + ox::Result> setAsset(const ox::String &path, T &&obj) noexcept { + auto &p = m_cache[path]; + if (!p) { + p = ox::make_unique>(obj); + } else { + p->set(std::move(obj)); + p->updated.emit(); + } return AssetRef(p.get()); } diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp index cbbbb990..20d8e2bb 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -150,7 +150,7 @@ void TileSheetEditorImGui::draw(core::Context*) noexcept { m_subsheetEditor.draw(); } -void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, TileSheet::SubSheetIdx *path) noexcept { +void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, TileSheet::SubSheetIdx *path) { ImGui::TableNextRow(0, 5); using Str = ox::BasicString<100>; auto pathStr = ox::join("##", *path).value; @@ -305,7 +305,7 @@ ox::Error TileSheetEditorImGui::updateActiveSubsheet(const ox::String &name, int return model()->updateSubsheet(model()->activeSubSheetIdx(), name, cols, rows); } -ox::Error TileSheetEditorImGui::markUnsavedChanges(int) noexcept { +ox::Error TileSheetEditorImGui::markUnsavedChanges(const studio::UndoCommand*) noexcept { setUnsavedChanges(true); return OxError(0); } diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp index 83e5834d..96571421 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.hpp @@ -74,7 +74,7 @@ class TileSheetEditorImGui: public studio::BaseEditor { void draw(core::Context*) noexcept override; - void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path) noexcept; + void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path); studio::UndoStack *undoStack() noexcept final; @@ -114,7 +114,7 @@ class TileSheetEditorImGui: public studio::BaseEditor { private: ox::Error updateAfterClicked() noexcept; - ox::Error markUnsavedChanges(int) noexcept; + ox::Error markUnsavedChanges(const studio::UndoCommand*) noexcept; }; diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.cpp b/src/nostalgia/core/studio/tilesheeteditormodel.cpp index bb1bd7cf..56a89121 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.cpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.cpp @@ -73,7 +73,13 @@ constexpr bool operator==(int i, CommandId c) noexcept { return static_cast(c) == i; } -class DrawCommand: public studio::UndoCommand { +class TileSheetCommand: public studio::UndoCommand { + public: + [[nodiscard]] + virtual const TileSheet::SubSheetIdx &subsheetIdx() const noexcept = 0; +}; + +class DrawCommand: public TileSheetCommand { private: struct Change { uint32_t idx = 0; @@ -150,10 +156,15 @@ class DrawCommand: public studio::UndoCommand { return static_cast(CommandId::Draw); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_subSheetIdx; + } + }; template -class CutPasteCommand: public studio::UndoCommand { +class CutPasteCommand: public TileSheetCommand { private: struct Change { uint32_t idx = 0; @@ -202,9 +213,14 @@ class CutPasteCommand: public studio::UndoCommand { return static_cast(CommandId); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_subSheetIdx; + } + }; -class AddSubSheetCommand: public studio::UndoCommand { +class AddSubSheetCommand: public TileSheetCommand { private: TileSheet *m_img = nullptr; TileSheet::SubSheetIdx m_parentIdx; @@ -261,9 +277,14 @@ class AddSubSheetCommand: public studio::UndoCommand { return static_cast(CommandId::AddSubSheet); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_parentIdx; + } + }; -class RmSubSheetCommand: public studio::UndoCommand { +class RmSubSheetCommand: public TileSheetCommand { private: TileSheet *m_img = nullptr; TileSheet::SubSheetIdx m_idx; @@ -296,9 +317,14 @@ class RmSubSheetCommand: public studio::UndoCommand { return static_cast(CommandId::RmSubSheet); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_idx; + } + }; -class UpdateSubSheetCommand: public studio::UndoCommand { +class UpdateSubSheetCommand: public TileSheetCommand { private: TileSheet *m_img = nullptr; TileSheet::SubSheetIdx m_idx; @@ -336,16 +362,23 @@ class UpdateSubSheetCommand: public studio::UndoCommand { return static_cast(CommandId::UpdateSubSheet); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_idx; + } + }; -class PaletteChangeCommand: public studio::UndoCommand { +class PaletteChangeCommand: public TileSheetCommand { private: + TileSheet::SubSheetIdx m_idx; TileSheet *m_img = nullptr; ox::FileAddress m_oldPalette; ox::FileAddress m_newPalette; public: - constexpr PaletteChangeCommand(TileSheet *img, const ox::String &newPalette) noexcept { + constexpr PaletteChangeCommand(const TileSheet::SubSheetIdx &idx, TileSheet *img, const ox::String &newPalette) noexcept { + m_idx = idx; m_img = img; m_oldPalette = m_img->defaultPalette; m_newPalette = newPalette; @@ -364,6 +397,11 @@ class PaletteChangeCommand: public studio::UndoCommand { return static_cast(CommandId::PaletteChange); } + [[nodiscard]] + const TileSheet::SubSheetIdx &subsheetIdx() const noexcept override { + return m_idx; + } + }; @@ -373,7 +411,8 @@ TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) oxRequireT(img, readObj(ctx, path.c_str())); m_img = *img; oxThrowError(readObj(ctx, m_img.defaultPalette).moveTo(&m_pal)); - m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdated); + m_pal.updated.connect(this, &TileSheetEditorModel::markUpdated); + m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId); } void TileSheetEditorModel::cut() { @@ -431,7 +470,7 @@ const ox::FileAddress &TileSheetEditorModel::palPath() const noexcept { } ox::Error TileSheetEditorModel::setPalette(const ox::String &path) noexcept { - pushCommand(new PaletteChangeCommand(&m_img, path)); + pushCommand(new PaletteChangeCommand(activeSubSheetIdx(), &m_img, path)); return OxError(0); } @@ -526,18 +565,12 @@ bool TileSheetEditorModel::updated() const noexcept { return m_updated; } -ox::Error TileSheetEditorModel::markUpdated(int cmdId) noexcept { +ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept { m_updated = true; + const auto cmdId = cmd->commandId(); switch (static_cast(cmdId)) { case CommandId::AddSubSheet: - case CommandId::RmSubSheet: { - // make sure the current active SubSheet is still valid - auto idx = m_img.validateSubSheetIdx(m_activeSubsSheetIdx); - if (idx != m_activeSubsSheetIdx) { - setActiveSubsheet(idx); - } - break; - } + case CommandId::RmSubSheet: case CommandId::Cut: case CommandId::Draw: case CommandId::Paste: @@ -547,6 +580,16 @@ ox::Error TileSheetEditorModel::markUpdated(int cmdId) noexcept { oxReturnError(readObj(m_ctx, m_img.defaultPalette.getPath().value).moveTo(&m_pal)); break; } + auto tsCmd = dynamic_cast(cmd); + auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx()); + if (idx != m_activeSubsSheetIdx) { + setActiveSubsheet(idx); + } + return OxError(0); +} + +ox::Error TileSheetEditorModel::markUpdated() noexcept { + m_updated = true; return OxError(0); } diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.hpp b/src/nostalgia/core/studio/tilesheeteditormodel.hpp index 25adc8cd..06fbb1dc 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.hpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.hpp @@ -94,7 +94,9 @@ class TileSheetEditorModel: public ox::SignalHandler { [[nodiscard]] bool updated() const noexcept; - ox::Error markUpdated(int) noexcept; + ox::Error markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept; + + ox::Error markUpdated() noexcept; void ackUpdate() noexcept; diff --git a/src/nostalgia/core/studio/tilesheeteditorview.cpp b/src/nostalgia/core/studio/tilesheeteditorview.cpp index 9c5308f3..a0b281fa 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.cpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.cpp @@ -79,6 +79,11 @@ bool TileSheetEditorView::updated() const noexcept { return m_updated || m_model.updated(); } +ox::Error TileSheetEditorView::markUpdated() noexcept { + m_updated = true; + return OxError(0); +} + void TileSheetEditorView::ackUpdate() noexcept { m_updated = false; m_model.ackUpdate(); diff --git a/src/nostalgia/core/studio/tilesheeteditorview.hpp b/src/nostalgia/core/studio/tilesheeteditorview.hpp index 38695513..5efee963 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.hpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.hpp @@ -100,6 +100,8 @@ class TileSheetEditorView: public ox::SignalHandler { [[nodiscard]] bool updated() const noexcept; + ox::Error markUpdated() noexcept; + void ackUpdate() noexcept; private: diff --git a/src/nostalgia/studio/lib/editor.cpp b/src/nostalgia/studio/lib/editor.cpp index 69807ab3..3742fa55 100644 --- a/src/nostalgia/studio/lib/editor.cpp +++ b/src/nostalgia/studio/lib/editor.cpp @@ -93,7 +93,7 @@ Editor::Editor() noexcept { m_undoStack.changeTriggered.connect(this, &Editor::markUnsavedChanges); } -ox::Error Editor::markUnsavedChanges(int) noexcept { +ox::Error Editor::markUnsavedChanges(const UndoCommand*) noexcept { setUnsavedChanges(true); return OxError(0); } diff --git a/src/nostalgia/studio/lib/editor.hpp b/src/nostalgia/studio/lib/editor.hpp index 4422b28f..eeb95439 100644 --- a/src/nostalgia/studio/lib/editor.hpp +++ b/src/nostalgia/studio/lib/editor.hpp @@ -123,7 +123,7 @@ class Editor: public studio::BaseEditor { } private: - ox::Error markUnsavedChanges(int) noexcept; + ox::Error markUnsavedChanges(const UndoCommand*) noexcept; }; diff --git a/src/nostalgia/studio/lib/undostack.cpp b/src/nostalgia/studio/lib/undostack.cpp index dd736ae7..17c71c61 100644 --- a/src/nostalgia/studio/lib/undostack.cpp +++ b/src/nostalgia/studio/lib/undostack.cpp @@ -14,10 +14,9 @@ void UndoStack::push(UndoCommand *cmd) noexcept { for (const auto i = m_stackIdx; i < m_stack.size();) { oxIgnoreError(m_stack.erase(i)); } - auto cmdId = cmd->commandId(); cmd->redo(); - redoTriggered.emit(cmdId); - changeTriggered.emit(cmdId); + redoTriggered.emit(cmd); + changeTriggered.emit(cmd); if (!m_stack.size() || !m_stack.back().value->mergeWith(cmd)) { m_stack.emplace_back(cmd); ++m_stackIdx; @@ -29,20 +28,18 @@ void UndoStack::push(UndoCommand *cmd) noexcept { void UndoStack::redo() noexcept { if (m_stackIdx < m_stack.size()) { auto &c = m_stack[m_stackIdx++]; - auto cmdId = c->commandId(); c->redo(); - redoTriggered.emit(cmdId); - changeTriggered.emit(cmdId); + redoTriggered.emit(c.get()); + changeTriggered.emit(c.get()); } } void UndoStack::undo() noexcept { if (m_stackIdx) { auto &c = m_stack[--m_stackIdx]; - auto cmdId = c->commandId(); c->undo(); - undoTriggered.emit(cmdId); - changeTriggered.emit(cmdId); + undoTriggered.emit(c.get()); + changeTriggered.emit(c.get()); } } diff --git a/src/nostalgia/studio/lib/undostack.hpp b/src/nostalgia/studio/lib/undostack.hpp index b46f0e24..9de48142 100644 --- a/src/nostalgia/studio/lib/undostack.hpp +++ b/src/nostalgia/studio/lib/undostack.hpp @@ -43,9 +43,9 @@ class UndoStack { return m_stackIdx; } - ox::Signal redoTriggered; - ox::Signal undoTriggered; - ox::Signal changeTriggered; + ox::Signal redoTriggered; + ox::Signal undoTriggered; + ox::Signal changeTriggered; }; }