Merge commit 'ab760b064fd6a302bad13274e0e02b2b2c957b67'
This commit is contained in:
@@ -414,6 +414,10 @@ void setPixel(TileSheet::SubSheet &ss, ox::Point const&pt, uint8_t palIdx) noexc
|
||||
|
||||
ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t cnt) noexcept;
|
||||
|
||||
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
|
||||
|
||||
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept;
|
||||
|
||||
/**
|
||||
* Gets a count of the pixels in this sheet, and not that of its children.
|
||||
* @return a count of the pixels in this sheet
|
||||
|
@@ -9,9 +9,10 @@
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
MoveColorCommand::MoveColorCommand(
|
||||
Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept:
|
||||
Palette &pal,
|
||||
size_t const srcIdx,
|
||||
size_t const dstIdx) noexcept:
|
||||
m_pal(pal),
|
||||
m_page(page),
|
||||
m_srcIdx(srcIdx),
|
||||
m_dstIdx(dstIdx) {}
|
||||
|
||||
@@ -29,10 +30,18 @@ ox::Error MoveColorCommand::undo() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void MoveColorCommand::moveColor(size_t srcIdx, size_t dstIdx) noexcept {
|
||||
auto const c = color(m_pal, m_page, srcIdx);
|
||||
std::ignore = colors(m_pal, m_page).erase(srcIdx);
|
||||
colors(m_pal, m_page).emplace(dstIdx, c);
|
||||
void MoveColorCommand::moveColor(
|
||||
size_t const srcIdx, size_t const dstIdx) noexcept {
|
||||
for (size_t page{}; page < m_pal.pages.size(); ++page) {
|
||||
auto const c = color(m_pal, page, srcIdx);
|
||||
std::ignore = colors(m_pal, page).erase(srcIdx);
|
||||
colors(m_pal, page).emplace(dstIdx, c);
|
||||
}
|
||||
{
|
||||
auto name = std::move(m_pal.colorNames[srcIdx]);
|
||||
std::ignore = m_pal.colorNames.erase(srcIdx);
|
||||
m_pal.colorNames.emplace(dstIdx, std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,12 +13,11 @@ namespace nostalgia::gfx {
|
||||
class MoveColorCommand: public studio::UndoCommand {
|
||||
private:
|
||||
Palette &m_pal;
|
||||
size_t const m_page = 0;
|
||||
std::size_t const m_srcIdx = 0;
|
||||
std::size_t const m_dstIdx = 0;
|
||||
|
||||
public:
|
||||
MoveColorCommand(Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept;
|
||||
MoveColorCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept;
|
||||
|
||||
~MoveColorCommand() noexcept override = default;
|
||||
|
||||
|
@@ -23,6 +23,15 @@ namespace nostalgia::gfx {
|
||||
|
||||
namespace ig = studio::ig;
|
||||
|
||||
struct ColorDragDrop {
|
||||
static constexpr auto TypeName = "nostalgia.gfx.ColorDragDrop";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
uint32_t i{};
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(ColorDragDrop)
|
||||
OX_MODEL_FIELD(i)
|
||||
OX_MODEL_END()
|
||||
|
||||
void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept {
|
||||
if (!m_show) {
|
||||
@@ -45,7 +54,7 @@ void PaletteEditorImGui::PageRenameDialog::draw(turbine::Context &tctx) noexcept
|
||||
|
||||
|
||||
PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
|
||||
Editor(std::move(path)),
|
||||
Editor(sctx, std::move(path)),
|
||||
m_sctx(sctx),
|
||||
m_tctx(sctx.tctx),
|
||||
m_pal(*keel::readObj<Palette>(keelCtx(m_tctx), itemPath()).unwrapThrow()) {
|
||||
@@ -125,26 +134,6 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
||||
m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow);
|
||||
colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(m_selectedColorRow <= 0);
|
||||
{
|
||||
if (ImGui::Button("Move Up", sz)) {
|
||||
std::ignore = pushCommand<MoveColorCommand>(
|
||||
m_pal, m_page, m_selectedColorRow, m_selectedColorRow - 1);
|
||||
--m_selectedColorRow;
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page) - 1);
|
||||
{
|
||||
if (ImGui::Button("Move Down", sz)) {
|
||||
std::ignore = pushCommand<MoveColorCommand>(
|
||||
m_pal, m_page, m_selectedColorRow, m_selectedColorRow + 1);
|
||||
++m_selectedColorRow;
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
@@ -179,6 +168,16 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
|
||||
"##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
m_selectedColorRow = i;
|
||||
}
|
||||
std::ignore = ig::dragDropSource([this, i] {
|
||||
ImGui::Text("%s", m_pal.colorNames[i].c_str());
|
||||
return ig::setDragDropPayload(ColorDragDrop{i});
|
||||
}, ImGuiDragDropFlags_SourceAllowNullID);
|
||||
if (ig::DragDropTarget const d; d) {
|
||||
auto const [src, err] = ig::getDragDropPayload<ColorDragDrop>();
|
||||
if (!err) {
|
||||
std::ignore = pushCommand<MoveColorCommand>(m_pal, src.i, i);
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ target_sources(
|
||||
cutpastecommand.cpp
|
||||
deletetilescommand.cpp
|
||||
drawcommand.cpp
|
||||
flipcommand.cpp
|
||||
inserttilescommand.cpp
|
||||
palettechangecommand.cpp
|
||||
rmsubsheetcommand.cpp
|
||||
|
@@ -12,14 +12,16 @@ namespace nostalgia::gfx {
|
||||
// Command IDs to use with QUndoCommand::id()
|
||||
enum class CommandId {
|
||||
Draw = 1,
|
||||
AddSubSheet = 2,
|
||||
RmSubSheet = 3,
|
||||
DeleteTile = 4,
|
||||
InsertTile = 4,
|
||||
UpdateSubSheet = 5,
|
||||
Cut = 6,
|
||||
Paste = 7,
|
||||
PaletteChange = 8,
|
||||
AddSubSheet,
|
||||
RmSubSheet,
|
||||
DeleteTile,
|
||||
FlipXCommand,
|
||||
FlipYCommand,
|
||||
InsertTile,
|
||||
UpdateSubSheet,
|
||||
Cut,
|
||||
Paste,
|
||||
PaletteChange,
|
||||
};
|
||||
|
||||
constexpr bool operator==(CommandId c, int i) noexcept {
|
||||
|
72
deps/nostalgia/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/commands/flipcommand.cpp
vendored
Normal file
72
deps/nostalgia/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/commands/flipcommand.cpp
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "flipcommand.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
FlipXCommand::FlipXCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept:
|
||||
m_img{img},
|
||||
m_subSheetIdx{std::move(subSheetIdx)},
|
||||
m_ptA{a},
|
||||
m_ptB{b} {}
|
||||
|
||||
ox::Error FlipXCommand::redo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipX(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error FlipXCommand::undo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipX(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
int FlipXCommand::commandId() const noexcept {
|
||||
return static_cast<int>(CommandId::FlipXCommand);
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx const &FlipXCommand::subsheetIdx() const noexcept {
|
||||
return m_subSheetIdx;
|
||||
}
|
||||
|
||||
|
||||
FlipYCommand::FlipYCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept:
|
||||
m_img{img},
|
||||
m_subSheetIdx{std::move(subSheetIdx)},
|
||||
m_ptA{a},
|
||||
m_ptB{b} {}
|
||||
|
||||
ox::Error FlipYCommand::redo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipY(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error FlipYCommand::undo() noexcept {
|
||||
auto &ss = getSubSheet(m_img, m_subSheetIdx);
|
||||
flipY(ss, m_ptA, m_ptB);
|
||||
return {};
|
||||
}
|
||||
|
||||
int FlipYCommand::commandId() const noexcept {
|
||||
return static_cast<int>(CommandId::FlipYCommand);
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx const &FlipYCommand::subsheetIdx() const noexcept {
|
||||
return m_subSheetIdx;
|
||||
}
|
||||
|
||||
}
|
63
deps/nostalgia/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/commands/flipcommand.hpp
vendored
Normal file
63
deps/nostalgia/src/nostalgia/modules/gfx/src/studio/tilesheeteditor/commands/flipcommand.hpp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "commands.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
class FlipXCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_subSheetIdx;
|
||||
ox::Point const m_ptA;
|
||||
ox::Point const m_ptB;
|
||||
|
||||
public:
|
||||
FlipXCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept;
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
ox::Error undo() noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
||||
|
||||
};
|
||||
|
||||
class FlipYCommand: public TileSheetCommand {
|
||||
private:
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_subSheetIdx;
|
||||
ox::Point const m_ptA;
|
||||
ox::Point const m_ptB;
|
||||
|
||||
public:
|
||||
FlipYCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx subSheetIdx,
|
||||
ox::Point const &a,
|
||||
ox::Point const &b) noexcept;
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
ox::Error undo() noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept final;
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheetIdx const&subsheetIdx() const noexcept override;
|
||||
|
||||
};
|
||||
|
||||
}
|
@@ -70,8 +70,8 @@ static ox::Error toPngFile(
|
||||
ox::Vector<uint32_t> &&pixels,
|
||||
Palette const&pal,
|
||||
size_t page,
|
||||
unsigned width,
|
||||
unsigned height) noexcept {
|
||||
unsigned const width,
|
||||
unsigned const height) noexcept {
|
||||
for (auto &c : pixels) {
|
||||
c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
|
||||
}
|
||||
@@ -87,7 +87,7 @@ static ox::Error toPngFile(
|
||||
}
|
||||
|
||||
TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
|
||||
Editor(std::move(path)),
|
||||
Editor(sctx, std::move(path)),
|
||||
m_sctx{sctx},
|
||||
m_tctx{m_sctx.tctx},
|
||||
m_palPicker{"Palette Chooser", keelCtx(sctx), FileExt_npal},
|
||||
@@ -96,7 +96,6 @@ TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::Stri
|
||||
// connect signal/slots
|
||||
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
|
||||
m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng);
|
||||
m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
|
||||
// load config
|
||||
auto const&config = studio::readConfig<TileSheetEditorConfig>(
|
||||
keelCtx(m_sctx), itemPath());
|
||||
@@ -125,7 +124,7 @@ bool TileSheetEditorImGui::acceptsClipboardPayload() const noexcept {
|
||||
return m_model.acceptsClipboardPayload();
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
|
||||
void TileSheetEditorImGui::keyStateChanged(turbine::Key const key, bool const down) {
|
||||
if (!down) {
|
||||
return;
|
||||
}
|
||||
@@ -223,7 +222,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
auto const ySize = controlsSize.y - 38;
|
||||
ImGui::BeginChild("OperationsBox", {s_palViewWidth - 24, ig::BtnSz.y + 17}, true);
|
||||
{
|
||||
auto constexpr btnSz = ImVec2{55, ig::BtnSz.y};
|
||||
if (ig::PushButton("Flip X", btnSz)) {
|
||||
oxLogError(m_model.flipX());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("Flip Y", btnSz)) {
|
||||
oxLogError(m_model.flipY());
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
auto const ySize = controlsSize.y - (38 + ig::BtnSz.y + 21);
|
||||
// draw palette/color picker
|
||||
ImGui::BeginChild("Palette", {s_palViewWidth - 24, ySize / 2.f}, true);
|
||||
{
|
||||
@@ -526,14 +537,11 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorImGui::updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept {
|
||||
ox::Error TileSheetEditorImGui::updateActiveSubsheet(
|
||||
ox::StringView const&name, int const cols, int const rows) noexcept {
|
||||
return m_model.updateSubsheet(m_model.activeSubSheetIdx(), name, cols, rows);
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept {
|
||||
m_model.setActiveSubsheet(path);
|
||||
studio::editConfig<TileSheetEditorConfig>(keelCtx(m_sctx), itemPath(),
|
||||
|
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/model/def.hpp>
|
||||
#include <ox/std/vec.hpp>
|
||||
|
||||
#include <glutils/glutils.hpp>
|
||||
@@ -29,7 +28,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
public:
|
||||
ox::Signal<ox::Error(ox::StringViewCR name, int cols, int rows)> inputSubmitted;
|
||||
void show(ox::StringViewCR name, int cols, int rows) noexcept;
|
||||
void draw(turbine::Context &sctx) noexcept;
|
||||
void draw(turbine::Context &tctx) noexcept;
|
||||
void close() noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr bool isOpen() const noexcept { return m_show; }
|
||||
@@ -41,7 +40,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
public:
|
||||
ox::Signal<ox::Error(int scale)> inputSubmitted;
|
||||
void show() noexcept;
|
||||
void draw(turbine::Context &sctx) noexcept;
|
||||
void draw(turbine::Context &tctx) noexcept;
|
||||
void close() noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr bool isOpen() const noexcept { return m_show; }
|
||||
@@ -99,8 +98,6 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
|
||||
ox::Error updateActiveSubsheet(ox::StringView const&name, int cols, int rows) noexcept;
|
||||
|
||||
ox::Error setPaletteSelection() noexcept;
|
||||
|
||||
// slots
|
||||
private:
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept;
|
||||
|
@@ -22,6 +22,8 @@
|
||||
#include "commands/updatesubsheetcommand.hpp"
|
||||
#include "tilesheeteditormodel.hpp"
|
||||
|
||||
#include "commands/flipcommand.hpp"
|
||||
|
||||
namespace nostalgia::gfx {
|
||||
|
||||
// delete pixels of all non-leaf nodes
|
||||
@@ -42,17 +44,19 @@ Palette const TileSheetEditorModel::s_defaultPalette = {
|
||||
};
|
||||
|
||||
TileSheetEditorModel::TileSheetEditorModel(
|
||||
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
|
||||
m_sctx(sctx),
|
||||
m_tctx(m_sctx.tctx),
|
||||
m_path(path),
|
||||
m_img(*readObj<TileSheet>(keelCtx(m_tctx), m_path).unwrapThrow()),
|
||||
// ignore failure to load palette
|
||||
m_pal(readObj<Palette>(keelCtx(m_tctx), m_img.defaultPalette).value),
|
||||
m_undoStack(undoStack) {
|
||||
studio::StudioContext &sctx, ox::StringParam path, studio::UndoStack &undoStack):
|
||||
m_sctx(sctx),
|
||||
m_tctx(m_sctx.tctx),
|
||||
m_path(std::move(path)),
|
||||
m_img(*readObj<TileSheet>(keelCtx(m_tctx), m_path).unwrapThrow()),
|
||||
// ignore failure to load palette
|
||||
m_pal(readObj<Palette>(keelCtx(m_tctx), m_img.defaultPalette).value),
|
||||
m_undoStack(undoStack) {
|
||||
normalizeSubsheets(m_img.subsheet);
|
||||
m_pal.updated.connect(this, &TileSheetEditorModel::markUpdated);
|
||||
m_undoStack.changeTriggered.connect(this, &TileSheetEditorModel::markUpdatedCmdId);
|
||||
setPalPath();
|
||||
m_sctx.project->fileMoved.connect(this, &TileSheetEditorModel::handleFileRename);
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::cut() {
|
||||
@@ -73,7 +77,7 @@ void TileSheetEditorModel::cut() {
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
turbine::setClipboardObject(m_tctx, std::move(cb));
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
std::ignore = pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
}
|
||||
|
||||
@@ -106,7 +110,7 @@ void TileSheetEditorModel::paste() {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
std::ignore = pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
}
|
||||
|
||||
@@ -116,23 +120,12 @@ bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
|
||||
}
|
||||
|
||||
ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
auto &path = m_img.defaultPalette;
|
||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||
if (ox::beginsWith(path, uuidPrefix)) {
|
||||
auto const uuid = substr(path, uuidPrefix.bytes());
|
||||
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
if (out.error) {
|
||||
return {};
|
||||
}
|
||||
return *out.value;
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
return m_palPath;
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(
|
||||
std::ignore = pushCommand(ox::make<PaletteChangeCommand>(
|
||||
activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
@@ -155,7 +148,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (getPixel(activeSubSheet, idx) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
@@ -165,27 +158,27 @@ void TileSheetEditorModel::endDrawCommand() noexcept {
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept {
|
||||
pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
|
||||
std::ignore = pushCommand(ox::make<AddSubSheetCommand>(m_img, parentIdx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
std::ignore = pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::insertTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
std::ignore = pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::deleteTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
std::ignore = pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(
|
||||
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
pushCommand(cmd);
|
||||
std::ignore = pushCommand(cmd);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -215,7 +208,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idxList);
|
||||
} else if (getPixel(activeSubSheet, pt) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
std::ignore = pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +281,28 @@ bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept {
|
||||
return m_selection && m_selection->contains(pt);
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::flipX() noexcept {
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point a;
|
||||
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipXCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::flipY() noexcept {
|
||||
auto const &ss = activeSubSheet();
|
||||
ox::Point a;
|
||||
ox::Point b{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
|
||||
if (m_selection) {
|
||||
a = m_selection->a;
|
||||
b = m_selection->b;
|
||||
}
|
||||
return pushCommand(ox::make<FlipYCommand>(m_img, m_activeSubsSheetIdx, a, b));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
@@ -318,10 +333,34 @@ void TileSheetEditorModel::getFillPixels(
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
void TileSheetEditorModel::setPalPath() noexcept {
|
||||
auto &path = m_img.defaultPalette;
|
||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||
if (ox::beginsWith(path, uuidPrefix)) {
|
||||
auto const uuid = substr(path, uuidPrefix.bytes());
|
||||
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
if (!out.error) {
|
||||
m_palPath = *out.value;
|
||||
}
|
||||
} else {
|
||||
m_palPath = path;
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
|
||||
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd});
|
||||
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
|
||||
m_updated = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept {
|
||||
if ((beginsWith(m_img.defaultPalette, "uuid://") &&
|
||||
substr(m_img.defaultPalette, 7) == id.toString()) ||
|
||||
m_img.defaultPalette == newPath) {
|
||||
m_palPath = newPath;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
studio::StudioContext &m_sctx;
|
||||
turbine::Context &m_tctx;
|
||||
ox::String m_path;
|
||||
ox::String m_palPath;
|
||||
TileSheet m_img;
|
||||
TileSheet::SubSheetIdx m_activeSubsSheetIdx;
|
||||
keel::AssetRef<Palette> m_pal;
|
||||
@@ -36,7 +37,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool m_updated = false;
|
||||
|
||||
public:
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack);
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringParam path, studio::UndoStack &undoStack);
|
||||
|
||||
~TileSheetEditorModel() override = default;
|
||||
|
||||
@@ -125,6 +126,10 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
bool pixelSelected(std::size_t idx) const noexcept;
|
||||
|
||||
ox::Error flipX() noexcept;
|
||||
|
||||
ox::Error flipY() noexcept;
|
||||
|
||||
private:
|
||||
void getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
@@ -132,7 +137,11 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
ox::Point const&pt,
|
||||
int oldColor) const noexcept;
|
||||
|
||||
void pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
void setPalPath() noexcept;
|
||||
|
||||
ox::Error pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
|
||||
ox::Error handleFileRename(ox::StringViewCR, ox::StringViewCR newPath, ox::UUID const&id) noexcept;
|
||||
|
||||
};
|
||||
|
||||
|
@@ -103,6 +103,30 @@ ox::Error setPixelCount(TileSheet::SubSheet &ss, std::size_t const cnt) noexcept
|
||||
return setPixelCount(ss.pixels, cnt);
|
||||
}
|
||||
|
||||
void flipX(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
|
||||
auto const w = (b.x - a.x) / 2 + 1;
|
||||
auto const pixCols = ss.columns * TileWidth;
|
||||
for (int32_t yi = a.y; yi <= b.y; ++yi) {
|
||||
for (int32_t xi = 0; xi < w; ++xi) {
|
||||
auto const aIdx = ptToIdx(a.x + xi, yi, pixCols);
|
||||
auto const bIdx = ptToIdx(b.x - xi, yi, pixCols);
|
||||
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipY(TileSheet::SubSheet &ss, ox::Point const &a, ox::Point const &b) noexcept {
|
||||
auto const h = (b.y - a.y) / 2 + 1;
|
||||
auto const pixCols = ss.columns * TileWidth;
|
||||
for (int32_t yi = 0; yi < h; ++yi) {
|
||||
for (int32_t xi = a.x; xi <= b.x; ++xi) {
|
||||
auto const aIdx = ptToIdx(xi, a.y + yi, pixCols);
|
||||
auto const bIdx = ptToIdx(xi, b.y - yi, pixCols);
|
||||
std::swap(ss.pixels[aIdx], ss.pixels[bIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned pixelCnt(TileSheet::SubSheet const&ss) noexcept {
|
||||
return static_cast<unsigned>(ss.pixels.size());
|
||||
}
|
||||
|
Reference in New Issue
Block a user