[nostalgia/core/studio] Add select and cut/copy/paste to TileSheetEditor
This commit is contained in:
		@@ -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
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
@@ -27,4 +27,12 @@ Rectangle {
 | 
			
		||||
		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
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
@@ -34,10 +34,21 @@ Rectangle {
 | 
			
		||||
 | 
			
		||||
		onPressed: {
 | 
			
		||||
			if (mouse.button === Qt.LeftButton && !contextMenu.visible) {
 | 
			
		||||
				var pixel = pixelAt(mouseX, mouseY);
 | 
			
		||||
				if (pixel) {
 | 
			
		||||
					sheetData.beginCmd();
 | 
			
		||||
					sheetData.updatePixel(pixel);
 | 
			
		||||
				switch (sheetData.activeTool) {
 | 
			
		||||
					case 'Draw':
 | 
			
		||||
						var pixel = pixelAt(mouseX, mouseY);
 | 
			
		||||
						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: {
 | 
			
		||||
			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 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QString>
 | 
			
		||||
 | 
			
		||||
#include <ox/std/error.hpp>
 | 
			
		||||
@@ -17,6 +19,16 @@
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
ox::Result<std::vector<uint8_t>> toBuffer(T *data, std::size_t buffSize = ox::units::MB) {
 | 
			
		||||
	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
 | 
			
		||||
 * 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/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include <QBuffer>
 | 
			
		||||
#include <QDialog>
 | 
			
		||||
#include <QFormLayout>
 | 
			
		||||
#include <QHBoxLayout>
 | 
			
		||||
@@ -23,22 +22,52 @@
 | 
			
		||||
#include <QSettings>
 | 
			
		||||
#include <QSpinBox>
 | 
			
		||||
#include <QSplitter>
 | 
			
		||||
#include <QUndoCommand>
 | 
			
		||||
#include <QTableWidget>
 | 
			
		||||
#include <QToolBar>
 | 
			
		||||
#include <QUndoCommand>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <qnamespace.h>
 | 
			
		||||
 | 
			
		||||
#include <nostalgia/core/consts.hpp>
 | 
			
		||||
#include <nostalgia/common/point.hpp>
 | 
			
		||||
#include <qnamespace.h>
 | 
			
		||||
 | 
			
		||||
#include "consts.hpp"
 | 
			
		||||
#include "imgconv.hpp"
 | 
			
		||||
#include "util.hpp"
 | 
			
		||||
#include "tilesheeteditor.hpp"
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
@@ -57,7 +86,8 @@ class ModAfterDialog: public QDialog {
 | 
			
		||||
			}
 | 
			
		||||
			m_mod->setMinimum(-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->addWidget(okBtn);
 | 
			
		||||
			connect(okBtn, &QPushButton::clicked, this, &ModAfterDialog::accept);
 | 
			
		||||
@@ -149,10 +179,9 @@ class ModPixelsCommand: public QUndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		SheetData *m_sheetData = nullptr;
 | 
			
		||||
		QHash<int, int> m_pixelUpdates;
 | 
			
		||||
		int m_mod = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		ModPixelsCommand(SheetData *sheetData, int mod): m_sheetData(sheetData), m_mod(mod) {
 | 
			
		||||
		ModPixelsCommand(SheetData *sheetData): m_sheetData(sheetData) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual ~ModPixelsCommand() = default;
 | 
			
		||||
@@ -198,10 +227,11 @@ class UpdatePixelsCommand: public QUndoCommand {
 | 
			
		||||
		int m_newColorId = 0;
 | 
			
		||||
		const QStringList &m_palette;
 | 
			
		||||
		QVector<int> &m_pixels;
 | 
			
		||||
		QVector<int> &m_pixelSelected;
 | 
			
		||||
		QSet<PixelUpdate> m_pixelUpdates;
 | 
			
		||||
 | 
			
		||||
	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_cmdIdx = cmdIdx;
 | 
			
		||||
			PixelUpdate pu;
 | 
			
		||||
@@ -235,6 +265,7 @@ class UpdatePixelsCommand: public QUndoCommand {
 | 
			
		||||
				if (m_pixels.size() < index) {
 | 
			
		||||
					auto sz = (index / 64 + 1) * 64;
 | 
			
		||||
					m_pixels.resize(sz);
 | 
			
		||||
					m_pixelSelected.resize(sz);
 | 
			
		||||
				}
 | 
			
		||||
				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 {
 | 
			
		||||
	if (idx.column() != 1) {
 | 
			
		||||
@@ -302,21 +413,37 @@ void TileSheetEditorColorTableDelegate::paint(QPainter *painter, const QStyleOpt
 | 
			
		||||
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) {
 | 
			
		||||
	auto p = qobject_cast<QQuickItem*>(pixelItem.value<QObject*>());
 | 
			
		||||
	if (p && p != m_prevPixelUpdated) {
 | 
			
		||||
		m_cmdStack->push(new UpdatePixelsCommand(m_pixels, m_palette, p, m_selectedColor, m_cmdIdx));
 | 
			
		||||
		m_prevPixelUpdated = p;
 | 
			
		||||
	if (p && p != m_prevPixelOperand) {
 | 
			
		||||
		m_cmdStack->push(new UpdatePixelsCommand(m_pixels, m_pixelSelected, m_palette, p, m_selectedColor, m_cmdIdx));
 | 
			
		||||
		m_prevPixelOperand = p;
 | 
			
		||||
		emit changeOccurred();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SheetData::beginCmd() {
 | 
			
		||||
	++m_cmdIdx;
 | 
			
		||||
	m_selectionStart = {-1, -1};
 | 
			
		||||
	m_selectionEnd = {-1, -1};
 | 
			
		||||
	emit pixelSelectedChanged(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SheetData::endCmd() {
 | 
			
		||||
	m_prevPixelUpdated = nullptr;
 | 
			
		||||
	m_prevPixelOperand = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SheetData::insertTileCmd(int tileIdx) {
 | 
			
		||||
@@ -335,6 +462,10 @@ int SheetData::rows() const {
 | 
			
		||||
	return m_rows;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const QVector<int> &SheetData::pixelSelected() const {
 | 
			
		||||
	return m_pixelSelected;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const QVector<int> &SheetData::pixels() const {
 | 
			
		||||
	return m_pixels;
 | 
			
		||||
}
 | 
			
		||||
@@ -401,6 +532,7 @@ void SheetData::setPalette(const studio::Context *ctx, QString palPath) {
 | 
			
		||||
void SheetData::insertTile(int tileIdx, QVector<int> tileData) {
 | 
			
		||||
	auto pxIdx = tileIdx * PixelsPerTile;
 | 
			
		||||
	m_pixels.insert(pxIdx, PixelsPerTile, 0);
 | 
			
		||||
	m_pixelSelected.insert(pxIdx, PixelsPerTile, 0);
 | 
			
		||||
	std::copy(tileData.begin(), tileData.end(), &m_pixels[pxIdx]);
 | 
			
		||||
	emit pixelsChanged();
 | 
			
		||||
	emit changeOccurred();
 | 
			
		||||
@@ -413,6 +545,7 @@ QVector<int> SheetData::deleteTile(int tileIdx) {
 | 
			
		||||
	          m_pixels.begin() + (pxIdx + PixelsPerTile),
 | 
			
		||||
	          std::back_inserter(out));
 | 
			
		||||
	m_pixels.remove(pxIdx, PixelsPerTile);
 | 
			
		||||
	m_pixelSelected.remove(pxIdx, PixelsPerTile);
 | 
			
		||||
	emit pixelsChanged();
 | 
			
		||||
	emit changeOccurred();
 | 
			
		||||
	return out;
 | 
			
		||||
@@ -423,7 +556,7 @@ void SheetData::setSelectedColor(int index) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
		const auto p = m_pixels[i];
 | 
			
		||||
		if (p >= color) {
 | 
			
		||||
@@ -469,6 +602,79 @@ std::unique_ptr<NostalgiaGraphic> SheetData::toNostalgiaGraphic() const {
 | 
			
		||||
	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) {
 | 
			
		||||
	m_columns = 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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
	m_pixels.clear();
 | 
			
		||||
	m_pixelSelected.clear();
 | 
			
		||||
	if (ng->bpp == 8) {
 | 
			
		||||
		for (std::size_t i = 0; i < ng->tiles.size(); i++) {
 | 
			
		||||
			m_pixels.push_back(ng->tiles[i]);
 | 
			
		||||
			m_pixelSelected.push_back(0);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		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;
 | 
			
		||||
			}
 | 
			
		||||
			m_pixels.push_back(p);
 | 
			
		||||
			m_pixelSelected.push_back(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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()) {
 | 
			
		||||
	m_ctx = ctx;
 | 
			
		||||
	m_itemPath = path;
 | 
			
		||||
@@ -522,10 +741,21 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
 | 
			
		||||
	auto tb = new QToolBar(tr("Tile Sheet Options"));
 | 
			
		||||
	m_tilesX = new LabeledSpinner(tr("Tiles &X:"), 1, m_sheetData.columns());
 | 
			
		||||
	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_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);
 | 
			
		||||
	lyt->addWidget(m_splitter);
 | 
			
		||||
	m_splitter->addWidget(canvasParent);
 | 
			
		||||
@@ -544,7 +774,7 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
 | 
			
		||||
	m_sheetData.load(m_ctx, m_itemPath);
 | 
			
		||||
	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_updateAfterBtn, &QPushButton::clicked, this, &TileSheetEditor::updateAfterClicked);
 | 
			
		||||
	connect(updateAfterBtn, &QPushButton::clicked, this, &TileSheetEditor::updateAfterClicked);
 | 
			
		||||
	m_canvas->rootContext()->setContextProperty("sheetData", &m_sheetData);
 | 
			
		||||
	m_canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml"));
 | 
			
		||||
	m_canvas->setResizeMode(QQuickWidget::SizeRootObjectToView);
 | 
			
		||||
@@ -553,6 +783,11 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg
 | 
			
		||||
	restoreState();
 | 
			
		||||
	connect(&m_sheetData, &SheetData::changeOccurred, [this] { setUnsavedChanges(true); });
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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() {
 | 
			
		||||
	m_sheetData.save(m_ctx, m_itemPath);
 | 
			
		||||
}
 | 
			
		||||
@@ -657,28 +904,6 @@ QString TileSheetEditor::palettePath(QString paletteName) const {
 | 
			
		||||
	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 {
 | 
			
		||||
	const auto w = ng->columns * TileWidth;
 | 
			
		||||
	const auto h = ng->rows * TileHeight;
 | 
			
		||||
@@ -730,7 +955,10 @@ void TileSheetEditor::setColorTable() {
 | 
			
		||||
void TileSheetEditor::updateAfterClicked() {
 | 
			
		||||
	auto d = new ModAfterDialog(m_sheetData.palette(), this);
 | 
			
		||||
	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);
 | 
			
		||||
	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
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
@@ -8,16 +8,100 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <QButtonGroup>
 | 
			
		||||
#include <QStringList>
 | 
			
		||||
#include <QStyledItemDelegate>
 | 
			
		||||
#include <QUndoStack>
 | 
			
		||||
#include <QVariant>
 | 
			
		||||
 | 
			
		||||
#include <nostalgia/common/bounds.hpp>
 | 
			
		||||
#include <nostalgia/core/gfx.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 = 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 {
 | 
			
		||||
 | 
			
		||||
	void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const;
 | 
			
		||||
@@ -28,24 +112,33 @@ class SheetData: public QObject {
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
	Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
 | 
			
		||||
	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(QStringList palette READ palette NOTIFY paletteChanged)
 | 
			
		||||
	Q_PROPERTY(QString activeTool READ activeToolString)
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		class QQuickItem *m_prevPixelUpdated = nullptr;
 | 
			
		||||
		class QQuickItem *m_prevPixelOperand = nullptr;
 | 
			
		||||
		QString m_tilesheetPath;
 | 
			
		||||
		QString m_currentPalettePath;
 | 
			
		||||
		uint64_t m_cmdIdx = 0;
 | 
			
		||||
		QUndoStack *m_cmdStack;
 | 
			
		||||
		QStringList m_palette;
 | 
			
		||||
		QVector<int> m_pixelSelected;
 | 
			
		||||
		QVector<int> m_pixels;
 | 
			
		||||
		common::Point m_selectionStart = {-1, -1};
 | 
			
		||||
		common::Point m_selectionEnd = {-1, -1};
 | 
			
		||||
		int m_columns = 1;
 | 
			
		||||
		int m_rows = 1;
 | 
			
		||||
		int m_selectedColor = 0;
 | 
			
		||||
		TileSheetTool m_activeTool = TileSheetTool::Draw;
 | 
			
		||||
		TileSheetClipboard m_clipboard;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		SheetData(QUndoStack*);
 | 
			
		||||
 | 
			
		||||
		Q_INVOKABLE void selectPixel(QVariant pixel);
 | 
			
		||||
 | 
			
		||||
		Q_INVOKABLE void updatePixel(QVariant pixel);
 | 
			
		||||
 | 
			
		||||
		Q_INVOKABLE void beginCmd();
 | 
			
		||||
@@ -60,6 +153,8 @@ class SheetData: public QObject {
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] int rows() const;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] const QVector<int> &pixelSelected() const;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] const QVector<int> &pixels() const;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] QStringList palette() const;
 | 
			
		||||
@@ -88,6 +183,24 @@ class SheetData: public QObject {
 | 
			
		||||
 | 
			
		||||
		[[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:
 | 
			
		||||
		void setColumns(int columns);
 | 
			
		||||
 | 
			
		||||
@@ -103,9 +216,13 @@ class SheetData: public QObject {
 | 
			
		||||
		 */
 | 
			
		||||
		void updateRows(int rows);
 | 
			
		||||
 | 
			
		||||
		void setActiveTool(int t);
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void updatePixels(const NostalgiaGraphic *ng);
 | 
			
		||||
 | 
			
		||||
		const char *activeToolString() const;
 | 
			
		||||
 | 
			
		||||
	signals:
 | 
			
		||||
		void changeOccurred();
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +230,8 @@ class SheetData: public QObject {
 | 
			
		||||
 | 
			
		||||
		void rowsChanged(int);
 | 
			
		||||
 | 
			
		||||
		void pixelSelectedChanged(int pixelsSelected);
 | 
			
		||||
 | 
			
		||||
		void pixelsChanged();
 | 
			
		||||
 | 
			
		||||
		void paletteChanged();
 | 
			
		||||
@@ -129,10 +248,10 @@ class TileSheetEditor: public studio::Editor {
 | 
			
		||||
		QString m_itemName;
 | 
			
		||||
		const studio::Context *m_ctx = nullptr;
 | 
			
		||||
		SheetData m_sheetData;
 | 
			
		||||
		QButtonGroup m_toolBtns;
 | 
			
		||||
		class QSplitter *m_splitter = nullptr;
 | 
			
		||||
		struct LabeledSpinner *m_tilesX = nullptr;
 | 
			
		||||
		struct LabeledSpinner *m_tilesY = nullptr;
 | 
			
		||||
		class QPushButton *m_updateAfterBtn = nullptr;
 | 
			
		||||
		class QQuickWidget* m_canvas = nullptr;
 | 
			
		||||
		struct {
 | 
			
		||||
			QComboBox *palette = nullptr;
 | 
			
		||||
@@ -148,6 +267,12 @@ class TileSheetEditor: public studio::Editor {
 | 
			
		||||
 | 
			
		||||
		void exportFile() override;
 | 
			
		||||
 | 
			
		||||
		void cut() override;
 | 
			
		||||
 | 
			
		||||
		void copy() override;
 | 
			
		||||
 | 
			
		||||
		void paste() override;
 | 
			
		||||
 | 
			
		||||
	protected:
 | 
			
		||||
		void saveItem() override;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user