From 7df978605fc916b58b34976fca9b74ed96976ec3 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 11 Mar 2022 20:41:36 -0600 Subject: [PATCH] [nostalgia/core] Add Fill command to tilesheet editor and make ClipboardObject more efficient --- src/nostalgia/core/clipboard.hpp | 28 +- src/nostalgia/core/context.hpp | 21 +- .../core/studio/tilesheeteditor-imgui.cpp | 12 +- .../core/studio/tilesheeteditormodel.cpp | 358 +++++++++++++++++- .../core/studio/tilesheeteditormodel.hpp | 301 +-------------- .../core/studio/tilesheeteditorview.cpp | 6 +- .../core/studio/tilesheeteditorview.hpp | 2 + 7 files changed, 391 insertions(+), 337 deletions(-) diff --git a/src/nostalgia/core/clipboard.hpp b/src/nostalgia/core/clipboard.hpp index 36a4fd5e..af32d7dd 100644 --- a/src/nostalgia/core/clipboard.hpp +++ b/src/nostalgia/core/clipboard.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "context.hpp" @@ -15,32 +16,21 @@ ox::String getClipboardText(class Context *ctx) noexcept; void setClipboardText(class Context *ctx, const ox::String &text) noexcept; -void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] auto *obj) noexcept { +template +void setClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] ox::UniquePtr obj) noexcept { #ifndef OX_BARE_METAL - auto [buff, err] = ox::writeClaw(obj); - if (err) { - oxLogError(err); - return; - } - ctx->clipboard = std::move(buff); -#endif -} - -ox::Error getClipboardObject([[maybe_unused]] class Context *ctx, [[maybe_unused]] auto *obj) noexcept { -#ifndef OX_BARE_METAL - return ox::readClaw(ctx->clipboard).moveTo(obj); -#else - return OxError(0); + ctx->clipboard = std::move(obj); #endif } template -ox::Result getClipboardObject([[maybe_unused]] class Context *ctx) noexcept { +ox::Result getClipboardObject([[maybe_unused]] class Context *ctx) noexcept { #ifndef OX_BARE_METAL - return ox::readClaw(ctx->clipboard); -#else - return OxError(0); + if (ctx->clipboard && ctx->clipboard->typeMatch(T::TypeName, T::TypeVersion)) { + return static_cast(ctx->clipboard.get()); + } #endif + return OxError(1); } } diff --git a/src/nostalgia/core/context.hpp b/src/nostalgia/core/context.hpp index 7931ce3b..279ecff9 100644 --- a/src/nostalgia/core/context.hpp +++ b/src/nostalgia/core/context.hpp @@ -17,6 +17,25 @@ class Size; namespace nostalgia::core { +class BaseClipboardObject { + public: + virtual ~BaseClipboardObject() = default; + + virtual ox::String typeId() const noexcept = 0; + + constexpr auto typeMatch(auto name, auto version) const noexcept { + auto inId = ox::sfmt("{};{}", name, version); + return typeId() == inId; + } +}; + +template +class ClipboardObject: public BaseClipboardObject { + ox::String typeId() const noexcept final { + return ox::sfmt("{};{}", T::TypeName, T::TypeVersion); + } +}; + class Context; class Drawer; @@ -59,7 +78,7 @@ class Context { #ifndef OX_BARE_METAL AssetManager assetManager; int uninterruptedRefreshes = 0; - ox::Buffer clipboard; + ox::UniquePtr clipboard; #endif protected: #ifndef OX_BARE_METAL diff --git a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp index 6d4db468..d7986c68 100644 --- a/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor-imgui.cpp @@ -56,7 +56,7 @@ void TileSheetEditorImGui::draw(core::Context*) noexcept { { ImGui::BeginChild("ToolBox", ImVec2(m_palViewWidth - 24, 30), true); { - const auto btnSz = ImVec2(40, 14); + const auto btnSz = ImVec2(45, 14); if (ImGui::Selectable("Draw", m_tool == Tool::Draw, 0, btnSz)) { m_tool = Tool::Draw; model()->clearSelection(); @@ -65,6 +65,11 @@ void TileSheetEditorImGui::draw(core::Context*) noexcept { if (ImGui::Selectable("Select", m_tool == Tool::Select, 0, btnSz)) { m_tool = Tool::Select; } + ImGui::SameLine(); + if (ImGui::Selectable("Fill", m_tool == Tool::Fill, 0, btnSz)) { + m_tool = Tool::Fill; + model()->clearSelection(); + } } ImGui::EndChild(); const auto ySize = paneSize.y - 36; @@ -212,10 +217,13 @@ void TileSheetEditorImGui::drawTileSheet(const geo::Vec2 &fbSize) noexcept { m_tileSheetEditor.clickDraw(fbSize, clickPos); break; case Tool::Fill: - case Tool::None: + m_tileSheetEditor.clickFill(fbSize, clickPos); + break; case Tool::Select: m_tileSheetEditor.clickSelect(fbSize, clickPos); break; + case Tool::None: + break; } } } diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.cpp b/src/nostalgia/core/studio/tilesheeteditormodel.cpp index 2fcf2f24..2951d793 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.cpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.cpp @@ -8,19 +8,320 @@ #include #include #include +#include #include "tilesheeteditormodel.hpp" namespace nostalgia::core { -void TileSheetClipboard::addPixel(const geo::Point &pt, uint16_t colorIdx) noexcept { - m_pixels.emplace_back(colorIdx, pt); +class TileSheetClipboard: public ClipboardObject { + public: + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; + static constexpr auto TypeVersion = 1; + + oxModelFriend(TileSheetClipboard); + + struct Pixel { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard.Pixel"; + static constexpr auto TypeVersion = 1; + uint16_t colorIdx = 0; + geo::Point pt; + }; + protected: + ox::Vector m_pixels; + + public: + constexpr void addPixel(const geo::Point &pt, uint16_t colorIdx) noexcept { + m_pixels.emplace_back(colorIdx, pt); + } + + constexpr const ox::Vector &pixels() const noexcept { + return m_pixels; + } +}; + +oxModelBegin(TileSheetClipboard::Pixel) + oxModelField(colorIdx) + oxModelField(pt) +oxModelEnd() + +oxModelBegin(TileSheetClipboard) + oxModelFieldRename(pixels, m_pixels) +oxModelEnd() + + +// Command IDs to use with QUndoCommand::id() +enum class CommandId { + Draw = 1, + AddSubSheet = 2, + RmSubSheet = 3, + UpdateSubSheet = 4, + Paste = 5, +}; + +constexpr bool operator==(CommandId c, int i) noexcept { + return static_cast(c) == i; } -void TileSheetClipboard::clear() noexcept { - m_pixels.clear(); +constexpr bool operator==(int i, CommandId c) noexcept { + return static_cast(c) == i; } +class DrawCommand: public studio::UndoCommand { + private: + struct Change { + uint32_t idx = 0; + uint16_t oldPalIdx = 0; + }; + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_subSheetIdx; + ox::Vector m_changes; + int m_palIdx = 0; + + public: + constexpr DrawCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, std::size_t idx, int palIdx) noexcept { + m_img = img; + auto &subsheet = m_img->getSubSheet(subSheetIdx); + m_subSheetIdx = subSheetIdx; + m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); + m_palIdx = palIdx; + } + + constexpr DrawCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, ox::Vector idxList, int palIdx) noexcept { + m_img = img; + auto &subsheet = m_img->getSubSheet(subSheetIdx); + m_subSheetIdx = subSheetIdx; + for (const auto idx : idxList) { + m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); + } + m_palIdx = palIdx; + } + + constexpr auto append(std::size_t idx) noexcept { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + if (m_changes.back().value.idx != idx && subsheet.getPixel(m_img->bpp, idx) != m_palIdx) { + // duplicate entries are bad + auto existing = std::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) { + return c.idx == idx; + }); + if (existing == m_changes.cend()) { + m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); + subsheet.setPixel(m_img->bpp, idx, m_palIdx); + return true; + } + } + return false; + } + + constexpr auto append(ox::Vector idxList) noexcept { + auto out = false; + for (auto idx : idxList) { + out = append(idx) || out; + } + return out; + } + + void redo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + for (const auto &c : m_changes) { + subsheet.setPixel(m_img->bpp, c.idx, m_palIdx); + } + } + + void undo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + for (const auto &c : m_changes) { + subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx); + } + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::Draw); + } + +}; + +class PasteCommand: public studio::UndoCommand { + private: + struct Change { + uint32_t idx = 0; + uint16_t newPalIdx = 0; + uint16_t oldPalIdx = 0; + }; + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_subSheetIdx; + ox::Vector m_changes; + + public: + constexpr PasteCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, const geo::Point &dstStart, const geo::Point &dstEnd, const TileSheetClipboard &cb) noexcept { + m_img = img; + m_subSheetIdx = subSheetIdx; + const auto &subsheet = m_img->getSubSheet(subSheetIdx); + for (const auto &p : cb.pixels()) { + const auto dstPt = p.pt + dstStart; + if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) { + const auto idx = subsheet.idx(dstPt); + m_changes.emplace_back(idx, p.colorIdx, subsheet.getPixel(m_img->bpp, idx)); + } + } + } + + void redo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + for (const auto &c : m_changes) { + subsheet.setPixel(m_img->bpp, c.idx, c.newPalIdx); + } + } + + void undo() noexcept final { + auto &subsheet = m_img->getSubSheet(m_subSheetIdx); + for (const auto &c : m_changes) { + subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx); + } + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::Paste); + } + +}; + +class AddSubSheetCommand: public studio::UndoCommand { + private: + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_parentIdx; + ox::Vector m_addedSheets; + + public: + constexpr AddSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &parentIdx) noexcept { + m_img = img; + m_parentIdx = parentIdx; + auto &parent = m_img->getSubSheet(m_parentIdx); + if (parent.subsheets.size()) { + auto idx = m_parentIdx; + idx.emplace_back(parent.subsheets.size()); + m_addedSheets.push_back(idx); + } else { + auto idx = m_parentIdx; + idx.emplace_back(0); + m_addedSheets.push_back(idx); + idx.back().value = 1; + m_addedSheets.push_back(idx); + } + } + + void redo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + if (m_addedSheets.size() < 2) { + auto i = parent.subsheets.size(); + parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i).c_str(), 1, 1); + } else { + parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels)); + parent.rows = 0; + parent.columns = 0; + parent.subsheets.emplace_back("Subsheet 1", 1, 1); + } + } + + void undo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + if (parent.subsheets.size() == 2) { + auto s = parent.subsheets[0]; + parent.rows = s.rows; + parent.columns = s.columns; + parent.pixels = std::move(s.pixels); + parent.subsheets.clear(); + } else { + for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { + oxLogError(m_img->rmSubSheet(*idx)); + } + } + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::AddSubSheet); + } + +}; + +class RmSubSheetCommand: public studio::UndoCommand { + private: + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_idx; + TileSheet::SubSheetIdx m_parentIdx; + TileSheet::SubSheet m_sheet; + + public: + constexpr RmSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx) noexcept { + m_img = img; + m_idx = idx; + m_parentIdx = idx; + m_parentIdx.pop_back(); + auto &parent = m_img->getSubSheet(m_parentIdx); + m_sheet = parent.subsheets[idx.back().value]; + } + + void redo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + oxLogError(parent.subsheets.erase(m_idx.back().value).error); + } + + void undo() noexcept final { + auto &parent = m_img->getSubSheet(m_parentIdx); + auto i = m_idx.back().value; + parent.subsheets.insert(i, m_sheet); + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::RmSubSheet); + } + +}; + +class UpdateSubSheetCommand: public studio::UndoCommand { + private: + TileSheet *m_img = nullptr; + TileSheet::SubSheetIdx m_idx; + TileSheet::SubSheet m_sheet; + ox::String m_newName; + int m_newCols = 0; + int m_newRows = 0; + + public: + constexpr UpdateSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx, + const ox::String &name, int cols, int rows) noexcept { + m_img = img; + m_idx = idx; + m_sheet = img->getSubSheet(idx); + m_newName = name; + m_newCols = cols; + m_newRows = rows; + } + + void redo() noexcept final { + auto &sheet = m_img->getSubSheet(m_idx); + sheet.name = m_newName; + sheet.columns = m_newCols; + sheet.rows = m_newRows; + oxLogError(sheet.setPixelCount(m_img->bpp, static_cast(PixelsPerTile * m_newCols * m_newRows))); + } + + void undo() noexcept final { + auto &sheet = m_img->getSubSheet(m_idx); + sheet = m_sheet; + } + + [[nodiscard]] + int commandId() const noexcept final { + return static_cast(CommandId::UpdateSubSheet); + } + +}; + TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) { m_ctx = ctx; @@ -32,7 +333,7 @@ TileSheetEditorModel::TileSheetEditorModel(Context *ctx, const ox::String &path) } void TileSheetEditorModel::cut() { - TileSheetClipboard cb; + auto cb = ox::make_unique();; for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { auto pt = geo::Point(x, y); @@ -42,14 +343,14 @@ void TileSheetEditorModel::cut() { s->setPixel(m_img.bpp, idx, 0); pt.x -= m_selectionBounds.x; pt.y -= m_selectionBounds.y; - cb.addPixel(pt, c); + cb->addPixel(pt, c); } } - setClipboardObject(m_ctx, &cb); + setClipboardObject(m_ctx, std::move(cb)); } void TileSheetEditorModel::copy() { - TileSheetClipboard cb; + auto cb = ox::make_unique();; for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { auto pt = geo::Point(x, y); @@ -58,10 +359,10 @@ void TileSheetEditorModel::copy() { const auto c = s->getPixel(m_img.bpp, idx); pt.x -= m_selectionBounds.x; pt.y -= m_selectionBounds.y; - cb.addPixel(pt, c); + cb->addPixel(pt, c); } } - setClipboardObject(m_ctx, &cb); + setClipboardObject(m_ctx, std::move(cb)); } void TileSheetEditorModel::paste() { @@ -74,12 +375,12 @@ void TileSheetEditorModel::paste() { const auto s = activeSubSheet(); const auto pt1 = m_selectionOrigin == geo::Point(-1, -1) ? geo::Point(0, 0) : m_selectionOrigin; const auto pt2 = geo::Point(s->columns * TileWidth, s->rows * TileHeight); - pushCommand(new PasteCommand(&m_img, m_activeSubsSheetIdx, pt1, pt2, cb)); + pushCommand(new PasteCommand(&m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); } void TileSheetEditorModel::drawCommand(const geo::Point &pt, std::size_t palIdx) noexcept { const auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); - const auto idx = ptToIdx(pt, activeSubSheet.columns); + const auto idx = activeSubSheet.idx(pt); if (idx >= activeSubSheet.pixelCnt(m_img.bpp)) { return; } @@ -112,6 +413,31 @@ void TileSheetEditorModel::setActiveSubsheet(const TileSheet::SubSheetIdx &idx) this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); } +void TileSheetEditorModel::fill(const geo::Point &pt, int palIdx) noexcept { + const auto s = m_img.getSubSheet(m_activeSubsSheetIdx); + // build idx list + ox::Array updateMap = {}; + const auto oldColor = s.getPixel(m_img.bpp, pt); + if (oldColor == palIdx) { + return; + } + getFillPixels(updateMap.data(), pt, oldColor); + ox::Vector idxList; + auto i = s.idx(pt) / PixelsPerTile * PixelsPerTile; + for (auto u : updateMap) { + if (u) { + idxList.emplace_back(i); + } + ++i; + } + // do updates to sheet + if (m_ongoingDrawCommand) { + m_updated = m_updated || m_ongoingDrawCommand->append(idxList); + } else if (s.getPixel(m_img.bpp, pt) != palIdx) { + pushCommand(new DrawCommand(&m_img, m_activeSubsSheetIdx, idxList, palIdx)); + } +} + void TileSheetEditorModel::select(const geo::Point &pt) noexcept { if (!m_selectionOngoing) { m_selectionOrigin = pt; @@ -193,16 +519,16 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, const geo::Point &pt, int const auto tile = tileIdx(pt); // mark pixels to update pixels[idx % PixelsPerTile] = true; - if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && activeSubSheet.pixels[leftIdx] == oldColor) { + if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && activeSubSheet.getPixel(m_img.bpp, leftIdx) == oldColor) { getFillPixels(pixels, leftPt, oldColor); } - if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && activeSubSheet.pixels[rightIdx] == oldColor) { + if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && activeSubSheet.getPixel(m_img.bpp, rightIdx) == oldColor) { getFillPixels(pixels, rightPt, oldColor); } - if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && activeSubSheet.pixels[topIdx] == oldColor) { + if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && activeSubSheet.getPixel(m_img.bpp, topIdx) == oldColor) { getFillPixels(pixels, topPt, oldColor); } - if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && activeSubSheet.pixels[bottomIdx] == oldColor) { + if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && activeSubSheet.getPixel(m_img.bpp, bottomIdx) == oldColor) { getFillPixels(pixels, bottomPt, oldColor); } } diff --git a/src/nostalgia/core/studio/tilesheeteditormodel.hpp b/src/nostalgia/core/studio/tilesheeteditormodel.hpp index 493f47d3..4fa732c0 100644 --- a/src/nostalgia/core/studio/tilesheeteditormodel.hpp +++ b/src/nostalgia/core/studio/tilesheeteditormodel.hpp @@ -15,301 +15,6 @@ namespace nostalgia::core { -class TileSheetClipboard { - public: - static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; - static constexpr auto TypeVersion = 1; - - oxModelFriend(TileSheetClipboard); - - struct Pixel { - static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard.Pixel"; - static constexpr auto TypeVersion = 1; - uint16_t colorIdx = 0; - geo::Point pt; - }; - protected: - ox::Vector m_pixels; - - public: - void addPixel(const geo::Point &pt, uint16_t colorIdx) noexcept; - - [[nodiscard]] - bool empty() const; - - void pastePixels(const geo::Point &pt, ox::Vector *tgt, int tgtColumns) const; - - void clear() noexcept; - - constexpr const ox::Vector &pixels() const noexcept { - return m_pixels; - } - -}; - -oxModelBegin(TileSheetClipboard::Pixel) - oxModelField(colorIdx) - oxModelField(pt) -oxModelEnd() - -oxModelBegin(TileSheetClipboard) - oxModelFieldRename(pixels, m_pixels) -oxModelEnd() - -// Command IDs to use with QUndoCommand::id() -enum class CommandId { - Draw = 1, - AddSubSheet = 2, - RmSubSheet = 3, - UpdateSubSheet = 4, - Paste = 5, -}; - -constexpr bool operator==(CommandId c, int i) noexcept { - return static_cast(c) == i; -} - -constexpr bool operator==(int i, CommandId c) noexcept { - return static_cast(c) == i; -} - -struct DrawCommand: public studio::UndoCommand { - private: - struct Change { - uint32_t idx = 0; - uint16_t oldPalIdx = 0; - }; - TileSheet *m_img = nullptr; - TileSheet::SubSheetIdx m_subSheetIdx; - ox::Vector m_changes; - int m_palIdx = 0; - - public: - constexpr DrawCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, std::size_t idx, int palIdx) noexcept { - m_img = img; - auto &subsheet = m_img->getSubSheet(subSheetIdx); - m_subSheetIdx = subSheetIdx; - m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); - m_palIdx = palIdx; - } - - constexpr auto append(std::size_t idx) noexcept { - auto &subsheet = m_img->getSubSheet(m_subSheetIdx); - if (m_changes.back().value.idx != idx && subsheet.getPixel(m_img->bpp, idx) != m_palIdx) { - // duplicate entries are bad - auto existing = std::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) { - return c.idx == idx; - }); - if (existing == m_changes.cend()) { - m_changes.emplace_back(idx, subsheet.getPixel(m_img->bpp, idx)); - subsheet.setPixel(m_img->bpp, idx, m_palIdx); - return true; - } - } - return false; - } - - void redo() noexcept final { - auto &subsheet = m_img->getSubSheet(m_subSheetIdx); - for (const auto &c : m_changes) { - subsheet.setPixel(m_img->bpp, c.idx, m_palIdx); - } - } - - void undo() noexcept final { - auto &subsheet = m_img->getSubSheet(m_subSheetIdx); - for (const auto &c : m_changes) { - subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx); - } - } - - [[nodiscard]] - int commandId() const noexcept final { - return static_cast(CommandId::Draw); - } - -}; - -struct PasteCommand: public studio::UndoCommand { - private: - struct Change { - uint32_t idx = 0; - uint16_t newPalIdx = 0; - uint16_t oldPalIdx = 0; - }; - TileSheet *m_img = nullptr; - TileSheet::SubSheetIdx m_subSheetIdx; - ox::Vector m_changes; - - public: - constexpr PasteCommand(TileSheet *img, const TileSheet::SubSheetIdx &subSheetIdx, const geo::Point &dstStart, const geo::Point &dstEnd, const TileSheetClipboard &cb) noexcept { - m_img = img; - m_subSheetIdx = subSheetIdx; - const auto &subsheet = m_img->getSubSheet(subSheetIdx); - for (const auto &p : cb.pixels()) { - const auto dstPt = p.pt + dstStart; - if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) { - const auto idx = subsheet.idx(dstPt); - m_changes.emplace_back(idx, p.colorIdx, subsheet.getPixel(m_img->bpp, idx)); - } - } - } - - void redo() noexcept final { - auto &subsheet = m_img->getSubSheet(m_subSheetIdx); - for (const auto &c : m_changes) { - subsheet.setPixel(m_img->bpp, c.idx, c.newPalIdx); - } - } - - void undo() noexcept final { - auto &subsheet = m_img->getSubSheet(m_subSheetIdx); - for (const auto &c : m_changes) { - subsheet.setPixel(m_img->bpp, c.idx, c.oldPalIdx); - } - } - - [[nodiscard]] - int commandId() const noexcept final { - return static_cast(CommandId::Paste); - } - -}; - -struct AddSubSheetCommand: public studio::UndoCommand { - private: - TileSheet *m_img = nullptr; - TileSheet::SubSheetIdx m_parentIdx; - ox::Vector m_addedSheets; - - public: - constexpr AddSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &parentIdx) noexcept { - m_img = img; - m_parentIdx = parentIdx; - auto &parent = m_img->getSubSheet(m_parentIdx); - if (parent.subsheets.size()) { - auto idx = m_parentIdx; - idx.emplace_back(parent.subsheets.size()); - m_addedSheets.push_back(idx); - } else { - auto idx = m_parentIdx; - idx.emplace_back(0); - m_addedSheets.push_back(idx); - idx.back().value = 1; - m_addedSheets.push_back(idx); - } - } - - void redo() noexcept final { - auto &parent = m_img->getSubSheet(m_parentIdx); - if (m_addedSheets.size() < 2) { - auto i = parent.subsheets.size(); - parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i).c_str(), 1, 1); - } else { - parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels)); - parent.rows = 0; - parent.columns = 0; - parent.subsheets.emplace_back("Subsheet 1", 1, 1); - } - } - - void undo() noexcept final { - auto &parent = m_img->getSubSheet(m_parentIdx); - if (parent.subsheets.size() == 2) { - auto s = parent.subsheets[0]; - parent.rows = s.rows; - parent.columns = s.columns; - parent.pixels = std::move(s.pixels); - parent.subsheets.clear(); - } else { - for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { - oxLogError(m_img->rmSubSheet(*idx)); - } - } - } - - [[nodiscard]] - int commandId() const noexcept final { - return static_cast(CommandId::AddSubSheet); - } - -}; - -struct RmSubSheetCommand: public studio::UndoCommand { - private: - TileSheet *m_img = nullptr; - TileSheet::SubSheetIdx m_idx; - TileSheet::SubSheetIdx m_parentIdx; - TileSheet::SubSheet m_sheet; - - public: - constexpr RmSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx) noexcept { - m_img = img; - m_idx = idx; - m_parentIdx = idx; - m_parentIdx.pop_back(); - auto &parent = m_img->getSubSheet(m_parentIdx); - m_sheet = parent.subsheets[idx.back().value]; - } - - void redo() noexcept final { - auto &parent = m_img->getSubSheet(m_parentIdx); - oxLogError(parent.subsheets.erase(m_idx.back().value).error); - } - - void undo() noexcept final { - auto &parent = m_img->getSubSheet(m_parentIdx); - auto i = m_idx.back().value; - parent.subsheets.insert(i, m_sheet); - } - - [[nodiscard]] - int commandId() const noexcept final { - return static_cast(CommandId::RmSubSheet); - } - -}; - -struct UpdateSubSheetCommand: public studio::UndoCommand { - private: - TileSheet *m_img = nullptr; - TileSheet::SubSheetIdx m_idx; - TileSheet::SubSheet m_sheet; - ox::String m_newName; - int m_newCols = 0; - int m_newRows = 0; - - public: - constexpr UpdateSubSheetCommand(TileSheet *img, const TileSheet::SubSheetIdx &idx, - const ox::String &name, int cols, int rows) noexcept { - m_img = img; - m_idx = idx; - m_sheet = img->getSubSheet(idx); - m_newName = name; - m_newCols = cols; - m_newRows = rows; - } - - void redo() noexcept final { - auto &sheet = m_img->getSubSheet(m_idx); - sheet.name = m_newName; - sheet.columns = m_newCols; - sheet.rows = m_newRows; - oxLogError(sheet.setPixelCount(m_img->bpp, static_cast(PixelsPerTile * m_newCols * m_newRows))); - } - - void undo() noexcept final { - auto &sheet = m_img->getSubSheet(m_idx); - sheet = m_sheet; - } - - [[nodiscard]] - int commandId() const noexcept final { - return static_cast(CommandId::UpdateSubSheet); - } - -}; - class TileSheetEditorModel: public ox::SignalHandler { public: @@ -320,7 +25,7 @@ class TileSheetEditorModel: public ox::SignalHandler { TileSheet::SubSheetIdx m_activeSubsSheetIdx; AssetRef m_pal; studio::UndoStack m_undoStack; - DrawCommand *m_ongoingDrawCommand = nullptr; + class DrawCommand *m_ongoingDrawCommand = nullptr; bool m_updated = false; Context *m_ctx = nullptr; ox::String m_path; @@ -374,6 +79,8 @@ class TileSheetEditorModel: public ox::SignalHandler { return m_activeSubsSheetIdx; } + void fill(const geo::Point &pt, int palIdx) noexcept; + void select(const geo::Point &pt) noexcept; void completeSelection() noexcept; @@ -394,8 +101,6 @@ class TileSheetEditorModel: public ox::SignalHandler { bool pixelSelected(std::size_t idx) const noexcept; protected: - void saveItem(); - void getFillPixels(bool *pixels, const geo::Point &pt, int oldColor) const noexcept; private: diff --git a/src/nostalgia/core/studio/tilesheeteditorview.cpp b/src/nostalgia/core/studio/tilesheeteditorview.cpp index 8bb2edb8..cc55e7b3 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.cpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.cpp @@ -53,7 +53,6 @@ void TileSheetEditorView::scrollH(const geo::Vec2 &paneSz, float wheelh) noexcep void TileSheetEditorView::clickDraw(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { const auto pt = clickPoint(paneSize, clickPos); m_model.drawCommand(pt, m_palIdx); - m_model.clearSelection(); } void TileSheetEditorView::clickSelect(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { @@ -61,6 +60,11 @@ void TileSheetEditorView::clickSelect(const geo::Vec2 &paneSize, const geo::Vec2 m_model.select(pt); } +void TileSheetEditorView::clickFill(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept { + const auto pt = clickPoint(paneSize, clickPos); + m_model.fill(pt, m_palIdx); +} + void TileSheetEditorView::releaseMouseButton() noexcept { m_model.endDrawCommand(); m_model.completeSelection(); diff --git a/src/nostalgia/core/studio/tilesheeteditorview.hpp b/src/nostalgia/core/studio/tilesheeteditorview.hpp index f54d798a..38695513 100644 --- a/src/nostalgia/core/studio/tilesheeteditorview.hpp +++ b/src/nostalgia/core/studio/tilesheeteditorview.hpp @@ -59,6 +59,8 @@ class TileSheetEditorView: public ox::SignalHandler { void clickSelect(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept; + void clickFill(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept; + void releaseMouseButton() noexcept; void scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept;