194 lines
4.0 KiB
C++
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;
|
|
}
|
|
|
|
} |