nostalgia/src/nostalgia/core/studio/tilesheeteditormodel.hpp

194 lines
4.0 KiB
C++

/*
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/string.hpp>
#include <nostalgia/core/gfx.hpp>
#include <nostalgia/geo/point.hpp>
#include <nostalgia/geo/vec.hpp>
#include <nostalgia/studio/studio.hpp>
namespace nostalgia::core {
// Command IDs to use with QUndoCommand::id()
enum class CommandId {
UpdatePixel = 1,
ModPixel = 2,
UpdateDimension = 3,
InsertTile = 4,
ClipboardPaste = 5,
};
struct DrawCommand: public studio::UndoCommand {
private:
struct Change {
uint32_t idx = 0;
uint16_t oldPalIdx = 0;
};
TileSheet *m_img = nullptr;
ox::Vector<Change, 8> m_changes;
int m_palIdx = 0;
bool *m_modelUpdated = nullptr;
public:
constexpr DrawCommand(bool *updated, TileSheet *img, std::size_t idx, int palIdx) noexcept {
m_modelUpdated = updated;
m_img = img;
m_changes.emplace_back(idx, m_img->getPixel(idx));
m_palIdx = palIdx;
}
constexpr bool append(std::size_t idx) noexcept {
if (m_changes.back().value.idx != idx && m_img->getPixel(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, m_img->getPixel(idx));
m_img->setPixel(idx, m_palIdx);
return true;
}
}
return false;
}
void redo() noexcept final {
for (const auto &c : m_changes) {
m_img->setPixel(c.idx, m_palIdx);
}
*m_modelUpdated = true;
}
void undo() noexcept final {
for (const auto &c : m_changes) {
m_img->setPixel(c.idx, c.oldPalIdx);
}
*m_modelUpdated = true;
}
};
struct TileSheetClipboard {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
static constexpr auto TypeVersion = 1;
oxModelFriend(TileSheetClipboard);
protected:
ox::Vector<int> m_pixels;
geo::Point m_p1;
geo::Point m_p2;
public:
void addPixel(int color);
[[nodiscard]]
bool empty() const;
void pastePixels(const geo::Point &pt, ox::Vector<int> *tgt, int tgtColumns) const;
void setPoints(const geo::Point &p1, const geo::Point &p2);
[[nodiscard]]
constexpr geo::Point point1() const noexcept {
return m_p1;
}
[[nodiscard]]
constexpr geo::Point point2() const noexcept {
return m_p2;
}
};
oxModelBegin(TileSheetClipboard)
oxModelFieldRename(pixels, m_pixels)
oxModelFieldRename(p1, m_p1)
oxModelFieldRename(p2, m_p2)
oxModelEnd()
class TileSheetEditorModel {
private:
TileSheet m_img;
AssetRef<Palette> m_pal;
studio::UndoStack m_undoStack;
DrawCommand *m_ongoingDrawCommand = nullptr;
bool m_updated = false;
Context *m_ctx = nullptr;
ox::String m_path;
public:
TileSheetEditorModel(Context *ctx, const ox::String &path);
~TileSheetEditorModel() = default;
void cut();
void copy();
void paste();
[[nodiscard]]
constexpr const TileSheet &img() const noexcept;
[[nodiscard]]
constexpr TileSheet &img() noexcept;
[[nodiscard]]
constexpr const Palette &pal() const noexcept;
void drawCommand(const geo::Point &pt, std::size_t palIdx) noexcept;
void endDrawCommand() noexcept;
[[nodiscard]]
bool updated() const noexcept;
void ackUpdate() noexcept;
ox::Error saveFile() noexcept;
constexpr studio::UndoStack *undoStack() noexcept {
return &m_undoStack;
}
protected:
void saveItem();
void getFillPixels(bool *pixels, const geo::Point &pt, int oldColor) const noexcept;
private:
void pushCommand(studio::UndoCommand *cmd) noexcept;
void setPalette();
void saveState();
void restoreState();
[[nodiscard]]
ox::String paletteName(const ox::String &palettePath) const;
[[nodiscard]]
ox::String palettePath(const ox::String &palettePath) const;
};
constexpr const TileSheet &TileSheetEditorModel::img() const noexcept {
return m_img;
}
constexpr TileSheet &TileSheetEditorModel::img() noexcept {
return m_img;
}
constexpr const Palette &TileSheetEditorModel::pal() const noexcept {
return *m_pal;
}
}