[nostalgia/core/studio] Add select and cut/copy/paste to TileSheetEditor
This commit is contained in:
parent
985b2b57ba
commit
2580dbb7ab
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2019 gtalent2@gmail.com
|
* Copyright 2016 - 2020 gtalent2@gmail.com
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -27,4 +27,12 @@ Rectangle {
|
|||||||
anchors.bottom: pixel.bottom
|
anchors.bottom: pixel.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: sheetData ? sheetData.pixelSelected[pixel.pixelNumber] & 1 == 1 : false
|
||||||
|
color: '#0088ff'
|
||||||
|
opacity: 0.5
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2019 gtalent2@gmail.com
|
* Copyright 2016 - 2020 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -34,10 +34,21 @@ Rectangle {
|
|||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (mouse.button === Qt.LeftButton && !contextMenu.visible) {
|
if (mouse.button === Qt.LeftButton && !contextMenu.visible) {
|
||||||
var pixel = pixelAt(mouseX, mouseY);
|
switch (sheetData.activeTool) {
|
||||||
if (pixel) {
|
case 'Draw':
|
||||||
sheetData.beginCmd();
|
var pixel = pixelAt(mouseX, mouseY);
|
||||||
sheetData.updatePixel(pixel);
|
if (pixel) {
|
||||||
|
sheetData.beginCmd();
|
||||||
|
sheetData.updatePixel(pixel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Select':
|
||||||
|
var pixel = pixelAt(mouseX, mouseY);
|
||||||
|
if (pixel) {
|
||||||
|
sheetData.beginCmd();
|
||||||
|
sheetData.selectPixel(pixel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +123,19 @@ Rectangle {
|
|||||||
|
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
if (mouseArea.pressedButtons & Qt.LeftButton && !contextMenu.visible) {
|
if (mouseArea.pressedButtons & Qt.LeftButton && !contextMenu.visible) {
|
||||||
sheetData.updatePixel(pixelAt(mouseX, mouseY));
|
var pixel = pixelAt(mouseX, mouseY);
|
||||||
|
switch (sheetData.activeTool) {
|
||||||
|
case 'Draw':
|
||||||
|
if (pixel) {
|
||||||
|
sheetData.updatePixel(pixel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Select':
|
||||||
|
if (pixel) {
|
||||||
|
sheetData.selectPixel(pixel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,12 +14,4 @@ constexpr auto PluginName = "NostalgiaCore";
|
|||||||
constexpr auto TileSheetDir = "/TileSheets/";
|
constexpr auto TileSheetDir = "/TileSheets/";
|
||||||
constexpr auto PaletteDir = "/Palettes/";
|
constexpr auto PaletteDir = "/Palettes/";
|
||||||
|
|
||||||
// Command IDs to use with QUndoCommand::id()
|
|
||||||
enum class CommandId {
|
|
||||||
UpdatePixel = 1,
|
|
||||||
ModPixel = 2,
|
|
||||||
UpdateDimension = 3,
|
|
||||||
InsertTile = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,16 +26,6 @@ namespace {
|
|||||||
return (a << 15) | (r << 10) | (g << 5) | (b << 0);
|
return (a << 15) | (r << 10) | (g << 5) | (b << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int pointToIdx(int w, int x, int y) {
|
|
||||||
constexpr auto colLength = PixelsPerTile;
|
|
||||||
const auto rowLength = (w / TileWidth) * colLength;
|
|
||||||
const auto colStart = colLength * (x / TileWidth);
|
|
||||||
const auto rowStart = rowLength * (y / TileHeight);
|
|
||||||
const auto colOffset = x % TileWidth;
|
|
||||||
const auto rowOffset = (y % TileHeight) * TileHeight;
|
|
||||||
return colStart + colOffset + rowStart + rowOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static int countColors(const QImage &img, int argTiles) {
|
[[nodiscard]] static int countColors(const QImage &img, int argTiles) {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <ox/std/error.hpp>
|
#include <ox/std/error.hpp>
|
||||||
@ -17,6 +19,16 @@
|
|||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr int pointToIdx(int w, int x, int y) noexcept {
|
||||||
|
constexpr auto colLength = PixelsPerTile;
|
||||||
|
const auto rowLength = (w / TileWidth) * colLength;
|
||||||
|
const auto colStart = colLength * (x / TileWidth);
|
||||||
|
const auto rowStart = rowLength * (y / TileHeight);
|
||||||
|
const auto colOffset = x % TileWidth;
|
||||||
|
const auto rowOffset = (y % TileHeight) * TileHeight;
|
||||||
|
return colStart + colOffset + rowStart + rowOffset;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<std::vector<uint8_t>> toBuffer(T *data, std::size_t buffSize = ox::units::MB) {
|
ox::Result<std::vector<uint8_t>> toBuffer(T *data, std::size_t buffSize = ox::units::MB) {
|
||||||
std::vector<uint8_t> buff(buffSize);
|
std::vector<uint8_t> buff(buffSize);
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2019 gtalent2@gmail.com
|
* Copyright 2016 - 2020 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <array>
|
#include <memory>
|
||||||
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
@ -23,22 +22,52 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QUndoCommand>
|
|
||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
#include <QUndoCommand>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <memory>
|
#include <qnamespace.h>
|
||||||
|
|
||||||
#include <nostalgia/core/consts.hpp>
|
#include <nostalgia/core/consts.hpp>
|
||||||
#include <nostalgia/common/point.hpp>
|
#include <nostalgia/common/point.hpp>
|
||||||
#include <qnamespace.h>
|
|
||||||
|
|
||||||
#include "consts.hpp"
|
#include "consts.hpp"
|
||||||
|
#include "imgconv.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "tilesheeteditor.hpp"
|
#include "tilesheeteditor.hpp"
|
||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr int ptToIdx(int x, int y, int c) noexcept {
|
||||||
|
return pointToIdx(c * TileWidth, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr int ptToIdx(common::Point pt, int c) noexcept {
|
||||||
|
return pointToIdx(c * TileWidth, pt.x, pt.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr common::Point idxToPt(int i, int c) noexcept {
|
||||||
|
const auto t = i / PixelsPerTile; // tile number
|
||||||
|
const auto iti = i % PixelsPerTile; // in tile index
|
||||||
|
const auto tc = t % c; // tile column
|
||||||
|
const auto tr = t / c; // tile row
|
||||||
|
const auto itx = iti % TileWidth; // in tile x
|
||||||
|
const auto ity = iti / TileHeight; // in tile y
|
||||||
|
return {
|
||||||
|
itx + tc * TileWidth,
|
||||||
|
ity + tr * TileHeight,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(idxToPt(4, 1) == common::Point{4, 0});
|
||||||
|
static_assert(idxToPt(8, 1) == common::Point{0, 1});
|
||||||
|
static_assert(idxToPt(8, 2) == common::Point{0, 1});
|
||||||
|
static_assert(idxToPt(64, 2) == common::Point{8, 0});
|
||||||
|
static_assert(idxToPt(128, 2) == common::Point{0, 8});
|
||||||
|
static_assert(idxToPt(129, 2) == common::Point{1, 8});
|
||||||
|
static_assert(idxToPt(192, 2) == common::Point{8, 8});
|
||||||
|
static_assert(idxToPt(384, 8) == common::Point{48, 0});
|
||||||
|
|
||||||
class ModAfterDialog: public QDialog {
|
class ModAfterDialog: public QDialog {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -57,7 +86,8 @@ class ModAfterDialog: public QDialog {
|
|||||||
}
|
}
|
||||||
m_mod->setMinimum(-range + 1);
|
m_mod->setMinimum(-range + 1);
|
||||||
m_mod->setMaximum(range - 1);
|
m_mod->setMaximum(range - 1);
|
||||||
auto lyt = new QFormLayout(this); lyt->addRow(tr("&Greater/Equal To:"), m_afterColor);
|
auto lyt = new QFormLayout(this);
|
||||||
|
lyt->addRow(tr("&Greater/Equal To:"), m_afterColor);
|
||||||
lyt->addRow(tr("&Mod By:"), m_mod);
|
lyt->addRow(tr("&Mod By:"), m_mod);
|
||||||
lyt->addWidget(okBtn);
|
lyt->addWidget(okBtn);
|
||||||
connect(okBtn, &QPushButton::clicked, this, &ModAfterDialog::accept);
|
connect(okBtn, &QPushButton::clicked, this, &ModAfterDialog::accept);
|
||||||
@ -149,10 +179,9 @@ class ModPixelsCommand: public QUndoCommand {
|
|||||||
private:
|
private:
|
||||||
SheetData *m_sheetData = nullptr;
|
SheetData *m_sheetData = nullptr;
|
||||||
QHash<int, int> m_pixelUpdates;
|
QHash<int, int> m_pixelUpdates;
|
||||||
int m_mod = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModPixelsCommand(SheetData *sheetData, int mod): m_sheetData(sheetData), m_mod(mod) {
|
ModPixelsCommand(SheetData *sheetData): m_sheetData(sheetData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ModPixelsCommand() = default;
|
virtual ~ModPixelsCommand() = default;
|
||||||
@ -198,10 +227,11 @@ class UpdatePixelsCommand: public QUndoCommand {
|
|||||||
int m_newColorId = 0;
|
int m_newColorId = 0;
|
||||||
const QStringList &m_palette;
|
const QStringList &m_palette;
|
||||||
QVector<int> &m_pixels;
|
QVector<int> &m_pixels;
|
||||||
|
QVector<int> &m_pixelSelected;
|
||||||
QSet<PixelUpdate> m_pixelUpdates;
|
QSet<PixelUpdate> m_pixelUpdates;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UpdatePixelsCommand(QVector<int> &pixels, const QStringList &palette, QQuickItem *pixelItem, int newColorId, uint64_t cmdIdx): m_palette(palette), m_pixels(pixels) {
|
UpdatePixelsCommand(QVector<int> &pixels, QVector<int> &pixelSelected, const QStringList &palette, QQuickItem *pixelItem, int newColorId, uint64_t cmdIdx): m_palette(palette), m_pixels(pixels), m_pixelSelected(pixelSelected) {
|
||||||
m_newColorId = newColorId;
|
m_newColorId = newColorId;
|
||||||
m_cmdIdx = cmdIdx;
|
m_cmdIdx = cmdIdx;
|
||||||
PixelUpdate pu;
|
PixelUpdate pu;
|
||||||
@ -235,6 +265,7 @@ class UpdatePixelsCommand: public QUndoCommand {
|
|||||||
if (m_pixels.size() < index) {
|
if (m_pixels.size() < index) {
|
||||||
auto sz = (index / 64 + 1) * 64;
|
auto sz = (index / 64 + 1) * 64;
|
||||||
m_pixels.resize(sz);
|
m_pixels.resize(sz);
|
||||||
|
m_pixelSelected.resize(sz);
|
||||||
}
|
}
|
||||||
m_pixels[index] = m_newColorId;
|
m_pixels[index] = m_newColorId;
|
||||||
}
|
}
|
||||||
@ -288,6 +319,86 @@ class InsertTileCommand: public QUndoCommand {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PasteClipboardCommand: public QUndoCommand {
|
||||||
|
private:
|
||||||
|
SheetData *m_sheetData = nullptr;
|
||||||
|
TileSheetClipboard m_restore;
|
||||||
|
TileSheetClipboard m_apply;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PasteClipboardCommand(SheetData *sheetData,
|
||||||
|
const TileSheetClipboard &restore,
|
||||||
|
const TileSheetClipboard &apply): m_sheetData(sheetData), m_restore(restore), m_apply(apply) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~PasteClipboardCommand() = default;
|
||||||
|
|
||||||
|
int id() const override {
|
||||||
|
return static_cast<int>(CommandId::ClipboardPaste);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redo() override {
|
||||||
|
m_sheetData->applyClipboard(m_apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void undo() override {
|
||||||
|
m_sheetData->applyClipboard(m_restore);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void TileSheetClipboard::add(int idx, int color) {
|
||||||
|
if (m_chunks.size() && m_chunks.back().index + m_chunks.back().size == idx) {
|
||||||
|
++m_chunks.back().size;
|
||||||
|
} else {
|
||||||
|
m_chunks.push_back({idx, 1});
|
||||||
|
}
|
||||||
|
m_pixels.push_back(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetClipboard::clear() {
|
||||||
|
m_chunks.clear();
|
||||||
|
m_pixels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TileSheetClipboard::empty() const {
|
||||||
|
return m_pixels.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetClipboard::paste(int targetIdx, QVector<int> *pixels) const {
|
||||||
|
std::size_t ci = 0; // clipboard index
|
||||||
|
// set prevSrcIdx to current source index, as first iteration is already
|
||||||
|
// correct
|
||||||
|
int prevSrcIdx = m_chunks.size() ? m_chunks[0].index : 0;
|
||||||
|
for (std::size_t chunkIdx = 0; chunkIdx < m_chunks.size(); ++chunkIdx) {
|
||||||
|
const auto &chunk = m_chunks[chunkIdx];
|
||||||
|
targetIdx += chunk.index - prevSrcIdx;
|
||||||
|
prevSrcIdx = chunk.index;
|
||||||
|
const auto targetMod = targetIdx - chunk.index;
|
||||||
|
for (int i = 0; i < chunk.size; ++i) {
|
||||||
|
const auto ti = chunk.index + i + targetMod; // target index
|
||||||
|
if (ti < pixels->size()) {
|
||||||
|
(*pixels)[ti] = m_pixels[ci];
|
||||||
|
}
|
||||||
|
++ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetClipboard::setPoints(common::Point p1, common::Point p2) {
|
||||||
|
m_p1 = p1;
|
||||||
|
m_p2 = p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
common::Point TileSheetClipboard::point1() const {
|
||||||
|
return m_p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
common::Point TileSheetClipboard::point2() const {
|
||||||
|
return m_p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TileSheetEditorColorTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const {
|
void TileSheetEditorColorTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const {
|
||||||
if (idx.column() != 1) {
|
if (idx.column() != 1) {
|
||||||
@ -302,21 +413,37 @@ void TileSheetEditorColorTableDelegate::paint(QPainter *painter, const QStyleOpt
|
|||||||
SheetData::SheetData(QUndoStack *undoStack): m_cmdStack(undoStack) {
|
SheetData::SheetData(QUndoStack *undoStack): m_cmdStack(undoStack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SheetData::selectPixel(QVariant pixelItem) {
|
||||||
|
auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
||||||
|
if (p && p != m_prevPixelOperand) {
|
||||||
|
const auto index = p->property("pixelNumber").toInt();
|
||||||
|
const auto point = idxToPt(index, m_columns);
|
||||||
|
m_prevPixelOperand = p;
|
||||||
|
if (m_selectionStart == common::Point{-1, -1}) {
|
||||||
|
m_selectionStart = point;
|
||||||
|
}
|
||||||
|
markSelection(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SheetData::updatePixel(QVariant pixelItem) {
|
void SheetData::updatePixel(QVariant pixelItem) {
|
||||||
auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
|
||||||
if (p && p != m_prevPixelUpdated) {
|
if (p && p != m_prevPixelOperand) {
|
||||||
m_cmdStack->push(new UpdatePixelsCommand(m_pixels, m_palette, p, m_selectedColor, m_cmdIdx));
|
m_cmdStack->push(new UpdatePixelsCommand(m_pixels, m_pixelSelected, m_palette, p, m_selectedColor, m_cmdIdx));
|
||||||
m_prevPixelUpdated = p;
|
m_prevPixelOperand = p;
|
||||||
emit changeOccurred();
|
emit changeOccurred();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SheetData::beginCmd() {
|
void SheetData::beginCmd() {
|
||||||
++m_cmdIdx;
|
++m_cmdIdx;
|
||||||
|
m_selectionStart = {-1, -1};
|
||||||
|
m_selectionEnd = {-1, -1};
|
||||||
|
emit pixelSelectedChanged(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SheetData::endCmd() {
|
void SheetData::endCmd() {
|
||||||
m_prevPixelUpdated = nullptr;
|
m_prevPixelOperand = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SheetData::insertTileCmd(int tileIdx) {
|
void SheetData::insertTileCmd(int tileIdx) {
|
||||||
@ -335,6 +462,10 @@ int SheetData::rows() const {
|
|||||||
return m_rows;
|
return m_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QVector<int> &SheetData::pixelSelected() const {
|
||||||
|
return m_pixelSelected;
|
||||||
|
}
|
||||||
|
|
||||||
const QVector<int> &SheetData::pixels() const {
|
const QVector<int> &SheetData::pixels() const {
|
||||||
return m_pixels;
|
return m_pixels;
|
||||||
}
|
}
|
||||||
@ -401,6 +532,7 @@ void SheetData::setPalette(const studio::Context *ctx, QString palPath) {
|
|||||||
void SheetData::insertTile(int tileIdx, QVector<int> tileData) {
|
void SheetData::insertTile(int tileIdx, QVector<int> tileData) {
|
||||||
auto pxIdx = tileIdx * PixelsPerTile;
|
auto pxIdx = tileIdx * PixelsPerTile;
|
||||||
m_pixels.insert(pxIdx, PixelsPerTile, 0);
|
m_pixels.insert(pxIdx, PixelsPerTile, 0);
|
||||||
|
m_pixelSelected.insert(pxIdx, PixelsPerTile, 0);
|
||||||
std::copy(tileData.begin(), tileData.end(), &m_pixels[pxIdx]);
|
std::copy(tileData.begin(), tileData.end(), &m_pixels[pxIdx]);
|
||||||
emit pixelsChanged();
|
emit pixelsChanged();
|
||||||
emit changeOccurred();
|
emit changeOccurred();
|
||||||
@ -413,6 +545,7 @@ QVector<int> SheetData::deleteTile(int tileIdx) {
|
|||||||
m_pixels.begin() + (pxIdx + PixelsPerTile),
|
m_pixels.begin() + (pxIdx + PixelsPerTile),
|
||||||
std::back_inserter(out));
|
std::back_inserter(out));
|
||||||
m_pixels.remove(pxIdx, PixelsPerTile);
|
m_pixels.remove(pxIdx, PixelsPerTile);
|
||||||
|
m_pixelSelected.remove(pxIdx, PixelsPerTile);
|
||||||
emit pixelsChanged();
|
emit pixelsChanged();
|
||||||
emit changeOccurred();
|
emit changeOccurred();
|
||||||
return out;
|
return out;
|
||||||
@ -423,7 +556,7 @@ void SheetData::setSelectedColor(int index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SheetData::modGteCmd(int color, int mod) {
|
void SheetData::modGteCmd(int color, int mod) {
|
||||||
auto cmd = new ModPixelsCommand(this, mod);
|
auto cmd = new ModPixelsCommand(this);
|
||||||
for (int i = 0; i < m_pixels.size(); ++i) {
|
for (int i = 0; i < m_pixels.size(); ++i) {
|
||||||
const auto p = m_pixels[i];
|
const auto p = m_pixels[i];
|
||||||
if (p >= color) {
|
if (p >= color) {
|
||||||
@ -469,6 +602,79 @@ std::unique_ptr<NostalgiaGraphic> SheetData::toNostalgiaGraphic() const {
|
|||||||
return ng;
|
return ng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SheetData::activeTool() const {
|
||||||
|
return static_cast<int>(m_activeTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SheetData::clipboardEmpty() const {
|
||||||
|
return m_clipboard.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::cutToClipboard() {
|
||||||
|
m_clipboard.setPoints(m_selectionStart, m_selectionEnd);
|
||||||
|
cutToClipboard(&m_clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::cutToClipboard(TileSheetClipboard *cb) {
|
||||||
|
const auto start = ptToIdx(cb->point1(), m_columns);
|
||||||
|
const auto end = ptToIdx(cb->point2(), m_columns);
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
const auto s = m_pixelSelected[i];
|
||||||
|
if (s) {
|
||||||
|
cb->add(i, m_pixels[i]);
|
||||||
|
m_pixels[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit pixelsChanged();
|
||||||
|
emit changeOccurred();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::copyToClipboard() {
|
||||||
|
m_clipboard.setPoints(m_selectionStart, m_selectionEnd);
|
||||||
|
copyToClipboard(&m_clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::copyToClipboard(TileSheetClipboard *cb) {
|
||||||
|
const auto start = ptToIdx(cb->point1(), m_columns);
|
||||||
|
const auto end = ptToIdx(cb->point2(), m_columns);
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
const auto s = m_pixelSelected[i];
|
||||||
|
if (s) {
|
||||||
|
cb->add(i, m_pixels[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::pasteClipboard() {
|
||||||
|
TileSheetClipboard apply = m_clipboard;
|
||||||
|
TileSheetClipboard restore;
|
||||||
|
const auto p2 = m_selectionStart + (apply.point2() - apply.point1());
|
||||||
|
apply.setPoints(m_selectionStart, p2);
|
||||||
|
restore.setPoints(m_selectionStart, p2);
|
||||||
|
markSelection(p2);
|
||||||
|
copyToClipboard(&restore);
|
||||||
|
m_cmdStack->push(new PasteClipboardCommand(this, restore, apply));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::applyClipboard(const TileSheetClipboard &cb) {
|
||||||
|
const auto idx = ptToIdx(cb.point1(), m_columns);
|
||||||
|
cb.paste(idx, &m_pixels);
|
||||||
|
emit pixelsChanged();
|
||||||
|
emit changeOccurred();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SheetData::markSelection(common::Point selectionEnd) {
|
||||||
|
// mark selection
|
||||||
|
m_selectionEnd = selectionEnd;
|
||||||
|
ox::memsetElements(m_pixelSelected.data(), 0, static_cast<std::size_t>(m_pixelSelected.size()));
|
||||||
|
for (auto x = m_selectionStart.x; x <= m_selectionEnd.x; ++x) {
|
||||||
|
for (auto y = m_selectionStart.y; y <= m_selectionEnd.y; ++y) {
|
||||||
|
m_pixelSelected[ptToIdx(x, y, m_columns)] |= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit pixelSelectedChanged((m_selectionEnd.x - m_selectionStart.x + 1) * (m_selectionEnd.y - m_selectionStart.y + 1));
|
||||||
|
}
|
||||||
|
|
||||||
void SheetData::setColumns(int columns) {
|
void SheetData::setColumns(int columns) {
|
||||||
m_columns = columns;
|
m_columns = columns;
|
||||||
emit columnsChanged(columns);
|
emit columnsChanged(columns);
|
||||||
@ -489,11 +695,19 @@ void SheetData::updateRows(int rows) {
|
|||||||
m_cmdStack->push(new UpdateDimensionsCommand(this, UpdateDimensionsCommand::Dimension::Rows, m_rows, rows));
|
m_cmdStack->push(new UpdateDimensionsCommand(this, UpdateDimensionsCommand::Dimension::Rows, m_rows, rows));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SheetData::setActiveTool(int t) {
|
||||||
|
m_activeTool = static_cast<TileSheetTool>(t);
|
||||||
|
ox::memsetElements(m_pixelSelected.data(), 0, static_cast<std::size_t>(m_pixelSelected.size()));
|
||||||
|
emit pixelSelectedChanged(0);
|
||||||
|
}
|
||||||
|
|
||||||
void SheetData::updatePixels(const NostalgiaGraphic *ng) {
|
void SheetData::updatePixels(const NostalgiaGraphic *ng) {
|
||||||
m_pixels.clear();
|
m_pixels.clear();
|
||||||
|
m_pixelSelected.clear();
|
||||||
if (ng->bpp == 8) {
|
if (ng->bpp == 8) {
|
||||||
for (std::size_t i = 0; i < ng->tiles.size(); i++) {
|
for (std::size_t i = 0; i < ng->tiles.size(); i++) {
|
||||||
m_pixels.push_back(ng->tiles[i]);
|
m_pixels.push_back(ng->tiles[i]);
|
||||||
|
m_pixelSelected.push_back(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < ng->tiles.size() * 2; i++) {
|
for (std::size_t i = 0; i < ng->tiles.size() * 2; i++) {
|
||||||
@ -504,11 +718,16 @@ void SheetData::updatePixels(const NostalgiaGraphic *ng) {
|
|||||||
p = ng->tiles[i / 2] & 0xF;
|
p = ng->tiles[i / 2] & 0xF;
|
||||||
}
|
}
|
||||||
m_pixels.push_back(p);
|
m_pixels.push_back(p);
|
||||||
|
m_pixelSelected.push_back(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit pixelsChanged();
|
emit pixelsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *SheetData::activeToolString() const {
|
||||||
|
return toString(m_activeTool);
|
||||||
|
}
|
||||||
|
|
||||||
TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent): studio::Editor(parent), m_sheetData(undoStack()) {
|
TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidget *parent): studio::Editor(parent), m_sheetData(undoStack()) {
|
||||||
m_ctx = ctx;
|
m_ctx = ctx;
|
||||||
m_itemPath = path;
|
m_itemPath = path;
|
||||||
@ -522,10 +741,21 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
|
|||||||
auto tb = new QToolBar(tr("Tile Sheet Options"));
|
auto tb = new QToolBar(tr("Tile Sheet Options"));
|
||||||
m_tilesX = new LabeledSpinner(tr("Tiles &X:"), 1, m_sheetData.columns());
|
m_tilesX = new LabeledSpinner(tr("Tiles &X:"), 1, m_sheetData.columns());
|
||||||
m_tilesY = new LabeledSpinner(tr("Tiles &Y:"), 1, m_sheetData.rows());
|
m_tilesY = new LabeledSpinner(tr("Tiles &Y:"), 1, m_sheetData.rows());
|
||||||
m_updateAfterBtn = new QPushButton(tr("&Mod After"), tb);
|
auto updateAfterBtn = new QPushButton(tr("&Mod GTE"), tb);
|
||||||
|
m_toolBtns.addButton(new QPushButton(tr("&Select"), tb), static_cast<int>(TileSheetTool::Select));
|
||||||
|
m_toolBtns.addButton(new QPushButton(tr("&Draw"), tb), static_cast<int>(TileSheetTool::Draw));
|
||||||
|
//m_toolBtns.addButton(new QPushButton(tr("F&ill"), tb), static_cast<int>(TileSheetTool::Fill));
|
||||||
tb->addWidget(m_tilesX);
|
tb->addWidget(m_tilesX);
|
||||||
tb->addWidget(m_tilesY);
|
tb->addWidget(m_tilesY);
|
||||||
tb->addWidget(m_updateAfterBtn);
|
tb->addSeparator();
|
||||||
|
tb->addWidget(updateAfterBtn);
|
||||||
|
tb->addSeparator();
|
||||||
|
for (auto btn : m_toolBtns.buttons()) {
|
||||||
|
btn->setCheckable(true);
|
||||||
|
tb->addWidget(btn);
|
||||||
|
}
|
||||||
|
m_toolBtns.button(m_sheetData.activeTool())->setChecked(true);
|
||||||
|
connect(&m_toolBtns, &QButtonGroup::idClicked, &m_sheetData, &SheetData::setActiveTool);
|
||||||
canvasLyt->setMenuBar(tb);
|
canvasLyt->setMenuBar(tb);
|
||||||
lyt->addWidget(m_splitter);
|
lyt->addWidget(m_splitter);
|
||||||
m_splitter->addWidget(canvasParent);
|
m_splitter->addWidget(canvasParent);
|
||||||
@ -544,7 +774,7 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
|
|||||||
m_sheetData.load(m_ctx, m_itemPath);
|
m_sheetData.load(m_ctx, m_itemPath);
|
||||||
connect(m_tilesX->spinBox, QOverload<int>::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateColumns);
|
connect(m_tilesX->spinBox, QOverload<int>::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateColumns);
|
||||||
connect(m_tilesY->spinBox, QOverload<int>::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateRows);
|
connect(m_tilesY->spinBox, QOverload<int>::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateRows);
|
||||||
connect(m_updateAfterBtn, &QPushButton::clicked, this, &TileSheetEditor::updateAfterClicked);
|
connect(updateAfterBtn, &QPushButton::clicked, this, &TileSheetEditor::updateAfterClicked);
|
||||||
m_canvas->rootContext()->setContextProperty("sheetData", &m_sheetData);
|
m_canvas->rootContext()->setContextProperty("sheetData", &m_sheetData);
|
||||||
m_canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml"));
|
m_canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml"));
|
||||||
m_canvas->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
m_canvas->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||||
@ -553,6 +783,11 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
|
|||||||
restoreState();
|
restoreState();
|
||||||
connect(&m_sheetData, &SheetData::changeOccurred, [this] { setUnsavedChanges(true); });
|
connect(&m_sheetData, &SheetData::changeOccurred, [this] { setUnsavedChanges(true); });
|
||||||
connect(&m_sheetData, &SheetData::paletteChanged, this, &TileSheetEditor::setColorTable);
|
connect(&m_sheetData, &SheetData::paletteChanged, this, &TileSheetEditor::setColorTable);
|
||||||
|
connect(&m_sheetData, &SheetData::pixelSelectedChanged, [this](int selected) {
|
||||||
|
setCutEnabled(selected);
|
||||||
|
setCopyEnabled(selected);
|
||||||
|
setPasteEnabled(selected && !m_sheetData.clipboardEmpty());
|
||||||
|
});
|
||||||
setExportable(true);
|
setExportable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,6 +819,18 @@ void TileSheetEditor::exportFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::cut() {
|
||||||
|
m_sheetData.cutToClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::copy() {
|
||||||
|
m_sheetData.copyToClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSheetEditor::paste() {
|
||||||
|
m_sheetData.pasteClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
void TileSheetEditor::saveItem() {
|
void TileSheetEditor::saveItem() {
|
||||||
m_sheetData.save(m_ctx, m_itemPath);
|
m_sheetData.save(m_ctx, m_itemPath);
|
||||||
}
|
}
|
||||||
@ -657,28 +904,6 @@ QString TileSheetEditor::palettePath(QString paletteName) const {
|
|||||||
return PaletteDir + paletteName + FileExt_npal;
|
return PaletteDir + paletteName + FileExt_npal;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr common::Point idxToPt(int i, int c) noexcept {
|
|
||||||
const auto t = i / PixelsPerTile; // tile number
|
|
||||||
const auto iti = i % PixelsPerTile; // in tile index
|
|
||||||
const auto tc = t % c; // tile column
|
|
||||||
const auto tr = t / c; // tile row
|
|
||||||
const auto itx = iti % TileWidth; // in tile x
|
|
||||||
const auto ity = iti / TileHeight; // in tile y
|
|
||||||
return {
|
|
||||||
itx + tc * TileWidth,
|
|
||||||
ity + tr * TileHeight,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert(idxToPt(4, 1) == common::Point{4, 0});
|
|
||||||
static_assert(idxToPt(8, 1) == common::Point{0, 1});
|
|
||||||
static_assert(idxToPt(8, 2) == common::Point{0, 1});
|
|
||||||
static_assert(idxToPt(64, 2) == common::Point{8, 0});
|
|
||||||
static_assert(idxToPt(128, 2) == common::Point{0, 8});
|
|
||||||
static_assert(idxToPt(129, 2) == common::Point{1, 8});
|
|
||||||
static_assert(idxToPt(192, 2) == common::Point{8, 8});
|
|
||||||
static_assert(idxToPt(384, 8) == common::Point{48, 0});
|
|
||||||
|
|
||||||
QImage TileSheetEditor::toQImage(NostalgiaGraphic *ng, NostalgiaPalette *npal) const {
|
QImage TileSheetEditor::toQImage(NostalgiaGraphic *ng, NostalgiaPalette *npal) const {
|
||||||
const auto w = ng->columns * TileWidth;
|
const auto w = ng->columns * TileWidth;
|
||||||
const auto h = ng->rows * TileHeight;
|
const auto h = ng->rows * TileHeight;
|
||||||
@ -730,7 +955,10 @@ void TileSheetEditor::setColorTable() {
|
|||||||
void TileSheetEditor::updateAfterClicked() {
|
void TileSheetEditor::updateAfterClicked() {
|
||||||
auto d = new ModAfterDialog(m_sheetData.palette(), this);
|
auto d = new ModAfterDialog(m_sheetData.palette(), this);
|
||||||
connect(d, &ModAfterDialog::accepted, [this, d] {
|
connect(d, &ModAfterDialog::accepted, [this, d] {
|
||||||
m_sheetData.modGteCmd(d->color(), d->mod());
|
auto mod = d->mod();
|
||||||
|
if (mod) {
|
||||||
|
m_sheetData.modGteCmd(d->color(), mod);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
connect(d, &ModAfterDialog::finished, d, &ModAfterDialog::deleteLater);
|
connect(d, &ModAfterDialog::finished, d, &ModAfterDialog::deleteLater);
|
||||||
d->open();
|
d->open();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2019 gtalent2@gmail.com
|
* Copyright 2016 - 2020 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -8,16 +8,100 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QButtonGroup>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QUndoStack>
|
#include <QUndoStack>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <nostalgia/common/bounds.hpp>
|
||||||
#include <nostalgia/core/gfx.hpp>
|
#include <nostalgia/core/gfx.hpp>
|
||||||
#include <nostalgia/studio/studio.hpp>
|
#include <nostalgia/studio/studio.hpp>
|
||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
// Command IDs to use with QUndoCommand::id()
|
||||||
|
enum class CommandId {
|
||||||
|
UpdatePixel = 1,
|
||||||
|
ModPixel = 2,
|
||||||
|
UpdateDimension = 3,
|
||||||
|
InsertTile = 4,
|
||||||
|
ClipboardPaste = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TileSheetTool: int {
|
||||||
|
Select,
|
||||||
|
Draw,
|
||||||
|
Fill,
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto toString(TileSheetTool t) noexcept {
|
||||||
|
switch (t) {
|
||||||
|
case TileSheetTool::Select:
|
||||||
|
return "Select";
|
||||||
|
case TileSheetTool::Draw:
|
||||||
|
return "Draw";
|
||||||
|
case TileSheetTool::Fill:
|
||||||
|
return "Fill";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PixelChunk {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.PixelChunk";
|
||||||
|
static constexpr auto Fields = 2;
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
int index = -1;
|
||||||
|
int size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Error model(T *io, PixelChunk *c) {
|
||||||
|
io->template setTypeInfo<PixelChunk>();
|
||||||
|
oxReturnError(io->field("index", &c->index));
|
||||||
|
oxReturnError(io->field("size", &c->size));
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TileSheetClipboard {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard";
|
||||||
|
static constexpr auto Fields = 2;
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
friend ox::Error model(T*, TileSheetClipboard*);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ox::Vector<PixelChunk> m_chunks;
|
||||||
|
ox::Vector<int> m_pixels;
|
||||||
|
common::Point m_p1;
|
||||||
|
common::Point m_p2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add(int idx, int color);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
|
void paste(int targetIdx, QVector<int> *pixels) const;
|
||||||
|
|
||||||
|
void setPoints(common::Point p1, common::Point p2);
|
||||||
|
|
||||||
|
[[nodiscard]] common::Point point1() const;
|
||||||
|
|
||||||
|
[[nodiscard]] common::Point point2() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Error model(T *io, TileSheetClipboard *b) {
|
||||||
|
io->template setTypeInfo<TileSheetClipboard>();
|
||||||
|
oxReturnError(io->field("chunks", &b->m_chunks));
|
||||||
|
oxReturnError(io->field("pixels", &b->m_pixels));
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
|
||||||
struct TileSheetEditorColorTableDelegate: public QStyledItemDelegate {
|
struct TileSheetEditorColorTableDelegate: public QStyledItemDelegate {
|
||||||
|
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const;
|
void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const;
|
||||||
@ -28,24 +112,33 @@ class SheetData: public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
|
Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
|
||||||
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
|
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
|
||||||
|
Q_PROPERTY(QVector<int> pixelSelected READ pixelSelected NOTIFY pixelSelectedChanged)
|
||||||
Q_PROPERTY(QVector<int> pixels READ pixels NOTIFY pixelsChanged)
|
Q_PROPERTY(QVector<int> pixels READ pixels NOTIFY pixelsChanged)
|
||||||
Q_PROPERTY(QStringList palette READ palette NOTIFY paletteChanged)
|
Q_PROPERTY(QStringList palette READ palette NOTIFY paletteChanged)
|
||||||
|
Q_PROPERTY(QString activeTool READ activeToolString)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class QQuickItem *m_prevPixelUpdated = nullptr;
|
class QQuickItem *m_prevPixelOperand = nullptr;
|
||||||
QString m_tilesheetPath;
|
QString m_tilesheetPath;
|
||||||
QString m_currentPalettePath;
|
QString m_currentPalettePath;
|
||||||
uint64_t m_cmdIdx = 0;
|
uint64_t m_cmdIdx = 0;
|
||||||
QUndoStack *m_cmdStack;
|
QUndoStack *m_cmdStack;
|
||||||
QStringList m_palette;
|
QStringList m_palette;
|
||||||
|
QVector<int> m_pixelSelected;
|
||||||
QVector<int> m_pixels;
|
QVector<int> m_pixels;
|
||||||
|
common::Point m_selectionStart = {-1, -1};
|
||||||
|
common::Point m_selectionEnd = {-1, -1};
|
||||||
int m_columns = 1;
|
int m_columns = 1;
|
||||||
int m_rows = 1;
|
int m_rows = 1;
|
||||||
int m_selectedColor = 0;
|
int m_selectedColor = 0;
|
||||||
|
TileSheetTool m_activeTool = TileSheetTool::Draw;
|
||||||
|
TileSheetClipboard m_clipboard;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SheetData(QUndoStack*);
|
SheetData(QUndoStack*);
|
||||||
|
|
||||||
|
Q_INVOKABLE void selectPixel(QVariant pixel);
|
||||||
|
|
||||||
Q_INVOKABLE void updatePixel(QVariant pixel);
|
Q_INVOKABLE void updatePixel(QVariant pixel);
|
||||||
|
|
||||||
Q_INVOKABLE void beginCmd();
|
Q_INVOKABLE void beginCmd();
|
||||||
@ -60,6 +153,8 @@ class SheetData: public QObject {
|
|||||||
|
|
||||||
[[nodiscard]] int rows() const;
|
[[nodiscard]] int rows() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const QVector<int> &pixelSelected() const;
|
||||||
|
|
||||||
[[nodiscard]] const QVector<int> &pixels() const;
|
[[nodiscard]] const QVector<int> &pixels() const;
|
||||||
|
|
||||||
[[nodiscard]] QStringList palette() const;
|
[[nodiscard]] QStringList palette() const;
|
||||||
@ -88,6 +183,24 @@ class SheetData: public QObject {
|
|||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<NostalgiaGraphic> toNostalgiaGraphic() const;
|
[[nodiscard]] std::unique_ptr<NostalgiaGraphic> toNostalgiaGraphic() const;
|
||||||
|
|
||||||
|
[[nodiscard]] int activeTool() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool clipboardEmpty() const;
|
||||||
|
|
||||||
|
void cutToClipboard();
|
||||||
|
|
||||||
|
void cutToClipboard(TileSheetClipboard *cb);
|
||||||
|
|
||||||
|
void copyToClipboard();
|
||||||
|
|
||||||
|
void copyToClipboard(TileSheetClipboard *cb);
|
||||||
|
|
||||||
|
void pasteClipboard();
|
||||||
|
|
||||||
|
void applyClipboard(const TileSheetClipboard &cb);
|
||||||
|
|
||||||
|
void markSelection(common::Point selectionEnd);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setColumns(int columns);
|
void setColumns(int columns);
|
||||||
|
|
||||||
@ -103,9 +216,13 @@ class SheetData: public QObject {
|
|||||||
*/
|
*/
|
||||||
void updateRows(int rows);
|
void updateRows(int rows);
|
||||||
|
|
||||||
|
void setActiveTool(int t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updatePixels(const NostalgiaGraphic *ng);
|
void updatePixels(const NostalgiaGraphic *ng);
|
||||||
|
|
||||||
|
const char *activeToolString() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changeOccurred();
|
void changeOccurred();
|
||||||
|
|
||||||
@ -113,6 +230,8 @@ class SheetData: public QObject {
|
|||||||
|
|
||||||
void rowsChanged(int);
|
void rowsChanged(int);
|
||||||
|
|
||||||
|
void pixelSelectedChanged(int pixelsSelected);
|
||||||
|
|
||||||
void pixelsChanged();
|
void pixelsChanged();
|
||||||
|
|
||||||
void paletteChanged();
|
void paletteChanged();
|
||||||
@ -129,10 +248,10 @@ class TileSheetEditor: public studio::Editor {
|
|||||||
QString m_itemName;
|
QString m_itemName;
|
||||||
const studio::Context *m_ctx = nullptr;
|
const studio::Context *m_ctx = nullptr;
|
||||||
SheetData m_sheetData;
|
SheetData m_sheetData;
|
||||||
|
QButtonGroup m_toolBtns;
|
||||||
class QSplitter *m_splitter = nullptr;
|
class QSplitter *m_splitter = nullptr;
|
||||||
struct LabeledSpinner *m_tilesX = nullptr;
|
struct LabeledSpinner *m_tilesX = nullptr;
|
||||||
struct LabeledSpinner *m_tilesY = nullptr;
|
struct LabeledSpinner *m_tilesY = nullptr;
|
||||||
class QPushButton *m_updateAfterBtn = nullptr;
|
|
||||||
class QQuickWidget* m_canvas = nullptr;
|
class QQuickWidget* m_canvas = nullptr;
|
||||||
struct {
|
struct {
|
||||||
QComboBox *palette = nullptr;
|
QComboBox *palette = nullptr;
|
||||||
@ -148,6 +267,12 @@ class TileSheetEditor: public studio::Editor {
|
|||||||
|
|
||||||
void exportFile() override;
|
void exportFile() override;
|
||||||
|
|
||||||
|
void cut() override;
|
||||||
|
|
||||||
|
void copy() override;
|
||||||
|
|
||||||
|
void paste() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void saveItem() override;
|
void saveItem() override;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user