Add metatile swap

This commit is contained in:
GriffinR 2025-07-29 16:45:31 -04:00
parent f6f07ca5fc
commit d0337a7ae3
19 changed files with 504 additions and 177 deletions

View File

@ -173,6 +173,9 @@
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Undo</string>
</property>
@ -187,6 +190,9 @@
</property>
</action>
<action name="actionRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Redo</string>
</property>

View File

@ -681,6 +681,7 @@
<addaction name="actionPaste"/>
<addaction name="separator"/>
<addaction name="actionChange_Metatiles_Count"/>
<addaction name="actionSwap_Metatiles"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@ -752,6 +753,9 @@
</property>
</action>
<action name="actionUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Undo</string>
</property>
@ -760,6 +764,9 @@
</property>
</action>
<action name="actionRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Redo</string>
</property>
@ -784,6 +791,9 @@
</property>
</action>
<action name="actionPaste">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Paste</string>
</property>
@ -887,6 +897,17 @@
<string>Vertical</string>
</property>
</action>
<action name="actionSwap_Metatiles">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Swap Metatiles</string>
</property>
<property name="shortcut">
<string>X</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -25,6 +25,7 @@ public:
if (head > 0) {
return history.at(--head);
}
head = -1;
return NULL;
}
@ -37,9 +38,7 @@ public:
void push(T commit) {
while (head + 1 < history.length()) {
T item = history.last();
history.removeLast();
delete item;
delete history.takeLast();
}
if (saved > head) {
saved = -1;
@ -48,7 +47,7 @@ public:
head++;
}
T current() {
T current() const {
if (head < 0 || history.length() == 0) {
return NULL;
}
@ -59,10 +58,30 @@ public:
saved = head;
}
bool isSaved() {
bool isSaved() const {
return saved == head;
}
int length() const {
return history.length();
}
bool isEmpty() const {
return history.isEmpty();
}
int index() const {
return head;
}
bool canUndo() const {
return head >= 0;
}
bool canRedo() const {
return (head + 1) < history.length();
}
private:
QList<T> history;
int head = -1;

View File

@ -124,10 +124,13 @@ public:
bool isWithinBounds(const QRect &rect) const;
bool isWithinBorderBounds(int x, int y) const;
bool getBlock(int x, int y, Block *out);
bool getBlock(int x, int y, Block *out) const;
void setBlock(int x, int y, Block block, bool enableScriptCallback = false);
void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false);
uint16_t getMetatileId(int x, int y) const;
bool setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback = false);
void adjustDimensions(const QMargins &margins, bool setNewBlockdata = true);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true);
void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);

View File

@ -109,6 +109,7 @@ public:
QStringList secondaryTilesetLabels;
QStringList tilesetLabelsOrdered;
QSet<QString> getPairedTilesetLabels(const Tileset *tileset) const;
QSet<QString> getTilesetLayoutIds(const Tileset *priamryTileset, const Tileset *secondaryTileset) const;
bool readMapGroups();
void addNewMapGroup(const QString &groupName);

View File

@ -44,7 +44,7 @@ private:
Tileset *secondaryTileset;
QList<ColorInputWidget*> colorInputs;
QList<History<PaletteHistoryItem*>> palettesHistory;
QMap<int, History<PaletteHistoryItem*>> palettesHistory;
QMap<int,QSet<int>> unusedColorCache;
QPointer<PaletteColorSearch> colorSearchWindow;
@ -53,6 +53,7 @@ private:
void refreshPaletteId();
void commitEditHistory();
void commitEditHistory(int paletteId);
void updateEditHistoryActions();
void restoreWindowState();
void invalidateCache();
void closeEvent(QCloseEvent*);

View File

@ -26,6 +26,8 @@ public:
virtual void setMaxSelectionSize(int width, int height);
QSize maxSelectionSize() { return QSize(this->maxSelectionWidth, this->maxSelectionHeight); }
void setSelectionStyle(Qt::PenStyle style);
protected:
int cellWidth;
int cellHeight;
@ -40,10 +42,13 @@ protected:
void select(const QPoint &pos, const QSize &size = QSize(1,1));
void select(int x, int y, int width = 1, int height = 1) { select(QPoint(x, y), QSize(width, height)); }
void updateSelection(const QPoint &pos);
QPoint getCellPos(QPointF);
QPoint getCellPos(const QPointF &itemPos);
int getBoundedWidth(int width) const { return qBound(1, width, this->maxSelectionWidth); }
int getBoundedHeight(int height) const { return qBound(1, height, this->maxSelectionHeight); }
virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
virtual void drawSelectionRect(const QPoint &, const QSize &, Qt::PenStyle style = Qt::SolidLine);
virtual void drawSelection();
virtual int cellsWide() const { return this->cellWidth ? (pixmap().width() / this->cellWidth) : 0; }
virtual int cellsTall() const { return this->cellHeight ? (pixmap().height() / this->cellHeight) : 0; }
@ -53,6 +58,9 @@ signals:
private:
QPoint prevCellPos = QPoint(-1,-1);
Qt::PenStyle selectionStyle = Qt::SolidLine;
void setSelection(const QPoint &pos, const QSize &size);
};
#endif // SELECTABLEPIXMAPITEM_H

View File

@ -20,6 +20,7 @@ class TilesetEditor;
class MetatileHistoryItem {
public:
MetatileHistoryItem() {};
MetatileHistoryItem(uint16_t metatileId, Metatile *prevMetatile, Metatile *newMetatile, QString prevLabel, QString newLabel) {
this->metatileId = metatileId;
this->prevMetatile = prevMetatile;
@ -27,15 +28,24 @@ public:
this->prevLabel = prevLabel;
this->newLabel = newLabel;
}
MetatileHistoryItem(uint16_t metatileIdA, uint16_t metatileIdB) {
this->metatileId = metatileIdA;
this->swapMetatileId = metatileIdB;
this->isSwap = true;
}
~MetatileHistoryItem() {
delete this->prevMetatile;
delete this->newMetatile;
}
uint16_t metatileId;
Metatile *prevMetatile;
Metatile *newMetatile;
uint16_t metatileId = 0;
Metatile *prevMetatile = nullptr;
Metatile *newMetatile = nullptr;
QString prevLabel;
QString newLabel;
uint16_t swapMetatileId = 0;
bool isSwap = false;
};
class TilesetEditor : public QMainWindow
@ -122,8 +132,8 @@ private:
void countMetatileUsage();
void countTileUsage();
void copyMetatile(bool cut);
void pasteMetatile(const Metatile * toPaste, QString label);
bool replaceMetatile(uint16_t metatileId, const Metatile * src, QString label);
void pasteMetatile(const Metatile &toPaste, QString label);
bool replaceMetatile(uint16_t metatileId, const Metatile &src, QString label);
void commitMetatileChange(Metatile * prevMetatile);
void commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel);
uint32_t attributeNameToValue(Metatile::Attr attribute, const QString &text, bool *ok);
@ -134,11 +144,17 @@ private:
void commitEncounterType();
void commitTerrainType();
void commitLayerType();
void commit(MetatileHistoryItem *item);
void updateEditHistoryActions();
void setRawAttributesVisible(bool visible);
void refreshTileFlips();
void refreshPaletteId();
void paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly = false);
void setMetatileLayerOrientation(Qt::Orientation orientation);
void commitMetatileSwap(uint16_t metatileIdA, uint16_t metatileIdB);
bool swapMetatiles(uint16_t metatileIdA, uint16_t metatileIdB);
void applyMetatileSwapToLayouts(uint16_t metatileIdA, uint16_t metatileIdB);
void applyMetatileSwapsToLayouts();
Ui::TilesetEditor *ui;
History<MetatileHistoryItem*> metatileHistory;
@ -162,6 +178,7 @@ private:
bool lockSelection = false;
QSet<uint16_t> metatileReloadQueue;
MetatileImageExporter::Settings *metatileImageExportSettings = nullptr;
QList<QPair<uint16_t,uint16_t>> metatileIdSwaps;
bool save();

View File

@ -19,9 +19,13 @@ public:
bool select(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*);
uint16_t getSelectedMetatileId() const { return this->selectedMetatileId; }
void updateSelectedMetatile();
QPoint getMetatileIdCoordsOnWidget(uint16_t metatileId) const;
void setSwapMode(bool enabeled);
void addToSwapSelection(uint16_t metatileId);
void removeFromSwapSelection(uint16_t metatileId);
void clearSwapSelection();
QVector<uint16_t> usedMetatiles;
bool selectorShowUnused = false;
bool selectorShowCounts = false;
@ -42,11 +46,15 @@ private:
Tileset *primaryTileset = nullptr;
Tileset *secondaryTileset = nullptr;
uint16_t selectedMetatileId;
QList<uint16_t> swapMetatileIds;
bool inSwapMode = false;
void updateBasePixmap();
uint16_t posToMetatileId(int x, int y, bool *ok = nullptr) const;
uint16_t posToMetatileId(const QPoint &pos, bool *ok = nullptr) const;
QPoint metatileIdToPos(uint16_t metatileId, bool *ok = nullptr) const;
bool shouldAcceptEvent(QGraphicsSceneMouseEvent*);
bool isValidMetatileId(uint16_t metatileId) const;
int numRows(int numMetatiles) const;
int numRows() const;
void drawGrid();
@ -60,6 +68,7 @@ signals:
void hoveredMetatileChanged(uint16_t);
void hoveredMetatileCleared();
void selectedMetatileChanged(uint16_t);
void swapRequested(uint16_t, uint16_t);
};
#endif // TILESETEDITORMETATILESELECTOR_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -44,6 +44,7 @@
<file>icons/refresh.ico</file>
<file>icons/shift_cursor.ico</file>
<file>icons/shift.ico</file>
<file>icons/swap_cursor.ico</file>
<file>icons/tall_grass.ico</file>
<file>icons/warning.ico</file>
<file>icons/minimap.ico</file>

View File

@ -99,7 +99,7 @@ QRect Layout::getVisibleRect() const {
return area += Project::getPixelViewDistance();
}
bool Layout::getBlock(int x, int y, Block *out) {
bool Layout::getBlock(int x, int y, Block *out) const {
if (isWithinBounds(x, y)) {
int i = y * getWidth() + x;
*out = this->blockdata.value(i);
@ -134,6 +134,20 @@ void Layout::setBlockdata(Blockdata newBlockdata, bool enableScriptCallback) {
}
}
uint16_t Layout::getMetatileId(int x, int y) const {
Block block;
return getBlock(x, y, &block) ? block.metatileId() : 0;
}
bool Layout::setMetatileId(int x, int y, uint16_t metatileId, bool enableScriptCallback) {
Block block;
if (!getBlock(x, y, &block)) {
return false;
}
setBlock(x, y, Block(metatileId, block.collision(), block.elevation()), enableScriptCallback);
return true;
}
void Layout::clearBorderCache() {
this->cached_border.clear();
}

View File

@ -3563,3 +3563,19 @@ QSet<QString> Project::getPairedTilesetLabels(const Tileset *tileset) const {
}
return pairedLabels;
}
// Returns the set of IDs for the layouts that use the specified tilesets.
// nullptr for either tileset is treated as a wildcard (so 'getTilesetLayouts(nullptr, nullptr)' returns all layout IDs).
QSet<QString> Project::getTilesetLayoutIds(const Tileset *primaryTileset, const Tileset *secondaryTileset) const {
// Note: We're intentioanlly just returning the layout IDs, and not the pointer to the layout.
// The layout may not be loaded yet (which isn't obvious), and we should leave it up to the caller to request that.
QSet<QString> layoutIds;
for (const auto &layout : this->mapLayouts) {
if (primaryTileset && primaryTileset->name != layout->tileset_primary_label)
continue;
if (secondaryTileset && secondaryTileset->name != layout->tileset_secondary_label)
continue;
layoutIds.insert(layout->id);
}
return layoutIds;
}

View File

@ -96,21 +96,15 @@ void MainWindow::setBlocksFromSelection(int x, int y, bool forceRedraw, bool com
int MainWindow::getMetatileId(int x, int y) {
if (!this->editor || !this->editor->layout)
return 0;
Block block;
if (!this->editor->layout->getBlock(x, y, &block)) {
return 0;
}
return block.metatileId();
return this->editor->layout->getMetatileId(x, y);
}
void MainWindow::setMetatileId(int x, int y, int metatileId, bool forceRedraw, bool commitChanges) {
if (!this->editor || !this->editor->layout)
return;
Block block;
if (!this->editor->layout->getBlock(x, y, &block)) {
if (!this->editor->layout->setMetatileId(x, y, metatileId)) {
return;
}
this->editor->layout->setBlock(x, y, Block(metatileId, block.collision(), block.elevation()));
this->tryCommitMapChanges(commitChanges);
this->tryRedrawMapArea(forceRedraw);
}

View File

@ -163,7 +163,7 @@ void MetatileLayersItem::hover(const QPoint &pos) {
this->prevHoveredPos = pos;
int tileIndex = posToTileIndex(pos);
if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length())
if (tileIndex < 0 || !this->metatile || tileIndex >= this->metatile->tiles.length())
return;
emit this->hoveredTileChanged(this->metatile->tiles.at(tileIndex));

View File

@ -28,11 +28,6 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow);
}
// Setup edit-undo history for each of the palettes.
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
this->palettesHistory.append(History<PaletteHistoryItem*>());
}
int bitDepth = porymapConfig.paletteEditorBitDepth;
if (bitDepth == 15) {
this->ui->bit_depth_15->setChecked(true);
@ -62,6 +57,8 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::refreshPaletteId);
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::changedPalette);
ui->actionRedo->setShortcuts({ui->actionRedo->shortcut(), QKeySequence("Ctrl+Shift+Z")});
refreshPaletteId();
restoreWindowState();
}
@ -127,8 +124,12 @@ void PaletteEditor::refreshPaletteId() {
refreshColorInputs();
int paletteId = currentPaletteId();
if (!this->palettesHistory[paletteId].current()) {
// The original colors are saved as an initial commit.
commitEditHistory(paletteId);
} else {
updateEditHistoryActions();
}
if (this->colorSearchWindow) {
this->colorSearchWindow->setPaletteId(paletteId);
@ -154,8 +155,8 @@ void PaletteEditor::commitEditHistory(int paletteId) {
for (int i = 0; i < this->colorInputs.length(); i++) {
colors.append(this->colorInputs.at(i)->color());
}
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
this->palettesHistory[paletteId].push(commit);
this->palettesHistory[paletteId].push(new PaletteHistoryItem(colors));
updateEditHistoryActions();
}
void PaletteEditor::restoreWindowState() {
@ -165,18 +166,27 @@ void PaletteEditor::restoreWindowState() {
restoreState(geometry.value("palette_editor_state"));
}
void PaletteEditor::updateEditHistoryActions() {
int paletteId = currentPaletteId();
// We have an initial commit that shouldn't be available to Undo, so we ignore that.
ui->actionUndo->setEnabled(this->palettesHistory[paletteId].index() > 0);
ui->actionRedo->setEnabled(this->palettesHistory[paletteId].canRedo());
}
void PaletteEditor::on_actionUndo_triggered() {
int paletteId = currentPaletteId();
PaletteHistoryItem *prev = this->palettesHistory[paletteId].back();
if (prev)
setPalette(paletteId, prev->colors);
PaletteHistoryItem *commit = this->palettesHistory[paletteId].back();
if (!commit) return;
setPalette(paletteId, commit->colors);
updateEditHistoryActions();
}
void PaletteEditor::on_actionRedo_triggered() {
int paletteId = currentPaletteId();
PaletteHistoryItem *next = this->palettesHistory[paletteId].next();
if (next)
setPalette(paletteId, next->colors);
PaletteHistoryItem *commit = this->palettesHistory[paletteId].next();
if (!commit) return;
setPalette(paletteId, commit->colors);
updateEditHistoryActions();
}
void PaletteEditor::on_actionImport_Palette_triggered() {

View File

@ -10,12 +10,15 @@ QPoint SelectablePixmapItem::getSelectionStart()
return QPoint(x, y);
}
void SelectablePixmapItem::select(const QPoint &pos, const QSize &size)
{
void SelectablePixmapItem::setSelection(const QPoint &pos, const QSize &size) {
this->selectionInitialX = pos.x();
this->selectionInitialY = pos.y();
this->selectionOffsetX = qBound(0, size.width(), this->maxSelectionWidth - 1);
this->selectionOffsetY = qBound(0, size.height(), this->maxSelectionHeight - 1);
this->selectionOffsetX = getBoundedWidth(size.width()) - 1;
this->selectionOffsetY = getBoundedHeight(size.height()) - 1;
}
void SelectablePixmapItem::select(const QPoint &pos, const QSize &size) {
setSelection(pos, size);
draw();
emit selectionChanged(pos, getSelectionDimensions());
}
@ -23,10 +26,7 @@ void SelectablePixmapItem::select(const QPoint &pos, const QSize &size)
void SelectablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QPoint pos = getCellPos(event->pos());
this->selectionInitialX = pos.x();
this->selectionInitialY = pos.y();
this->selectionOffsetX = 0;
this->selectionOffsetY = 0;
setSelection(pos, QSize(1,1));
this->prevCellPos = pos;
updateSelection(pos);
}
@ -52,12 +52,10 @@ void SelectablePixmapItem::setMaxSelectionSize(int width, int height) {
// Update the selection if we shrank below the current selection size.
QSize size = getSelectionDimensions();
if (size.width() > this->maxSelectionWidth || size.height() > this->maxSelectionHeight) {
QPoint origin = getSelectionStart();
this->selectionInitialX = origin.x();
this->selectionInitialY = origin.y();
this->selectionOffsetX = qMin(size.width(), this->maxSelectionWidth) - 1;
this->selectionOffsetY = qMin(size.height(), this->maxSelectionHeight) - 1;
setSelection(getSelectionStart(), size);
draw();
// 'draw' is allowed to change the selection position/size,
// so call these again rather than keep values from above.
emit selectionChanged(getSelectionStart(), getSelectionDimensions());
}
}
@ -90,18 +88,25 @@ void SelectablePixmapItem::updateSelection(const QPoint &pos) {
emit selectionChanged(QPoint(x, y), getSelectionDimensions());
}
QPoint SelectablePixmapItem::getCellPos(QPointF pos) {
void SelectablePixmapItem::setSelectionStyle(Qt::PenStyle style) {
this->selectionStyle = style;
draw();
}
QPoint SelectablePixmapItem::getCellPos(const QPointF &itemPos) {
if (this->cellWidth == 0 || this->cellHeight == 0 || pixmap().isNull())
return QPoint(0,0);
int x = qBound(0, static_cast<int>(pos.x()), pixmap().width() - 1);
int y = qBound(0, static_cast<int>(pos.y()), pixmap().height() - 1);
int x = qBound(0, static_cast<int>(itemPos.x()), pixmap().width() - 1);
int y = qBound(0, static_cast<int>(itemPos.y()), pixmap().height() - 1);
return QPoint(x / this->cellWidth, y / this->cellHeight);
}
void SelectablePixmapItem::drawSelection() {
QPoint origin = this->getSelectionStart();
QSize dimensions = this->getSelectionDimensions();
drawSelectionRect(getSelectionStart(), getSelectionDimensions(), this->selectionStyle);
}
void SelectablePixmapItem::drawSelectionRect(const QPoint &origin, const QSize &dimensions, Qt::PenStyle style) {
QRect selectionRect(origin.x() * this->cellWidth, origin.y() * this->cellHeight, dimensions.width() * this->cellWidth, dimensions.height() * this->cellHeight);
// If a selection is fully outside the bounds of the selectable area, don't draw anything.
@ -110,12 +115,27 @@ void SelectablePixmapItem::drawSelection() {
if (!selectionRect.intersects(pixmap.rect()))
return;
auto fillPen = QPen(QColor(Qt::white));
auto borderPen = QPen(QColor(Qt::black));
borderPen.setStyle(style);
QPainter painter(&pixmap);
painter.setPen(QColor(0xff, 0xff, 0xff));
painter.drawRect(selectionRect.x(), selectionRect.y(), selectionRect.width() - 1, selectionRect.height() - 1);
painter.setPen(QColor(0, 0, 0));
painter.drawRect(selectionRect.x() - 1, selectionRect.y() - 1, selectionRect.width() + 1, selectionRect.height() + 1);
painter.drawRect(selectionRect.x() + 1, selectionRect.y() + 1, selectionRect.width() - 3, selectionRect.height() - 3);
if (style == Qt::SolidLine) {
painter.setPen(fillPen);
painter.drawRect(selectionRect - QMargins(1,1,1,1));
painter.setPen(borderPen);
painter.drawRect(selectionRect);
painter.drawRect(selectionRect - QMargins(2,2,2,2));
} else {
// Having separately sized rectangles with anything but a
// solid line looks a little wonky because the dashes wont align.
// For non-solid styles we'll draw a base white rectangle, then draw
// a styled black rectangle on top
painter.setPen(fillPen);
painter.drawRect(selectionRect);
painter.setPen(borderPen);
painter.drawRect(selectionRect);
}
this->setPixmap(pixmap);
}

View File

@ -137,7 +137,7 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
this->primaryTileset = new Tileset(*primaryTileset);
this->secondaryTileset = new Tileset(*secondaryTileset);
if (this->paletteEditor) this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
this->initMetatileHistory();
initMetatileHistory();
}
void TilesetEditor::initAttributesUi() {
@ -217,12 +217,11 @@ void TilesetEditor::setRawAttributesVisible(bool visible) {
void TilesetEditor::initMetatileSelector()
{
this->metatileSelector = new TilesetEditorMetatileSelector(projectConfig.metatileSelectorWidth, this->primaryTileset, this->secondaryTileset, this->layout);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged,
this, &TilesetEditor::onHoveredMetatileChanged);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared,
this, &TilesetEditor::onHoveredMetatileCleared);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::selectedMetatileChanged,
this, &TilesetEditor::onSelectedMetatileChanged);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileChanged, this, &TilesetEditor::onHoveredMetatileChanged);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::hoveredMetatileCleared, this, &TilesetEditor::onHoveredMetatileCleared);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::selectedMetatileChanged, this, &TilesetEditor::onSelectedMetatileChanged);
connect(this->metatileSelector, &TilesetEditorMetatileSelector::swapRequested, this, &TilesetEditor::commitMetatileSwap);
connect(ui->actionSwap_Metatiles, &QAction::triggered, this->metatileSelector, &TilesetEditorMetatileSelector::setSwapMode);
bool showGrid = porymapConfig.showTilesetEditorMetatileGrid;
this->ui->actionMetatile_Grid->setChecked(showGrid);
@ -389,13 +388,6 @@ void TilesetEditor::onWindowActivated() {
}
}
void TilesetEditor::initMetatileHistory() {
metatileHistory.clear();
MetatileHistoryItem *commit = new MetatileHistoryItem(0, nullptr, new Metatile(), QString(), QString());
metatileHistory.push(commit);
this->hasUnsavedChanges = false;
}
void TilesetEditor::reset() {
this->setTilesets(this->primaryTileset->name, this->secondaryTileset->name);
if (this->paletteEditor)
@ -472,6 +464,7 @@ void TilesetEditor::onHoveredMetatileCleared() {
void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
if (!this->metatile) return;
// The scripting API allows users to change metatiles in the project, and these changes are saved to disk.
// The Tileset Editor (if open) needs to reflect these changes when the metatile is next displayed.
@ -513,6 +506,8 @@ void TilesetEditor::onHoveredTileCleared() {
}
void TilesetEditor::paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly) {
if (!this->metatile) return;
bool changed = false;
Metatile *prevMetatile = new Metatile(*this->metatile);
QSize dimensions = this->tileSelector->getSelectionDimensions();
@ -563,7 +558,7 @@ void TilesetEditor::onMetatileLayerSelectionChanged(const QPoint &selectionOrigi
for (int i = 0; i < size.width(); i++) {
int tileIndex = this->metatileLayersItem->posToTileIndex(selectionOrigin.x() + i, selectionOrigin.y() + j);
if (tileIndex < maxTileIndex) {
tiles.append(this->metatile->tiles.value(tileIndex));
tiles.append(this->metatile ? this->metatile->tiles.value(tileIndex) : Tile());
tileIdxs.append(tileIndex);
}
}
@ -612,8 +607,9 @@ void TilesetEditor::on_lineEdit_metatileLabel_editingFinished()
commitMetatileLabel();
}
void TilesetEditor::commitMetatileLabel()
{
void TilesetEditor::commitMetatileLabel() {
if (!this->metatile) return;
// Only commit if the field has changed.
uint16_t metatileId = this->getSelectedMetatileId();
QString oldLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
@ -625,12 +621,12 @@ void TilesetEditor::commitMetatileLabel()
}
}
void TilesetEditor::commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel)
{
metatileHistory.push(new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile),
prevLabel, this->ui->lineEdit_metatileLabel->text()));
this->hasUnsavedChanges = true;
void TilesetEditor::commitMetatileAndLabelChange(Metatile * prevMetatile, QString prevLabel) {
if (!this->metatile) return;
commit(new MetatileHistoryItem(this->getSelectedMetatileId(),
prevMetatile, new Metatile(*this->metatile),
prevLabel, this->ui->lineEdit_metatileLabel->text()));
}
void TilesetEditor::commitMetatileChange(Metatile * prevMetatile)
@ -661,8 +657,7 @@ uint32_t TilesetEditor::attributeNameToValue(Metatile::Attr attribute, const QSt
}
void TilesetEditor::commitAttributeFromComboBox(Metatile::Attr attribute, NoScrollComboBox *combo) {
if (!this->metatile)
return;
if (!this->metatile) return;
bool ok;
uint32_t newValue = this->attributeNameToValue(attribute, combo->currentText(), &ok);
@ -683,6 +678,8 @@ void TilesetEditor::commitAttributeFromComboBox(Metatile::Attr attribute, NoScro
}
void TilesetEditor::onRawAttributesEdited() {
if (!this->metatile) return;
uint32_t newAttributes = ui->spinBox_rawAttributesValue->value();
if (newAttributes != this->metatile->getAttributes()) {
Metatile *prevMetatile = new Metatile(*this->metatile);
@ -732,6 +729,7 @@ bool TilesetEditor::save() {
this->lockSelection = true;
bool success = this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
applyMetatileSwapsToLayouts();
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
if (this->paletteEditor) {
this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
@ -883,8 +881,15 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
if (dialog.exec() == QDialog::Accepted) {
this->primaryTileset->resizeMetatiles(primarySpinBox->value());
this->secondaryTileset->resizeMetatiles(secondarySpinBox->value());
this->metatileSelector->updateSelectedMetatile();
this->refresh();
// Our selected metatile ID may have become invalid. Make sure it's in-bounds.
uint16_t metatileId = this->metatileSelector->getSelectedMetatileId();
Tileset *tileset = Tileset::getMetatileTileset(metatileId, this->primaryTileset, this->secondaryTileset);
if (tileset && !tileset->contains(metatileId)) {
this->metatileSelector->select(qBound(tileset->firstMetatileId(), metatileId, tileset->lastMetatileId()));
}
refresh();
this->hasUnsavedChanges = true;
}
}
@ -906,11 +911,10 @@ void TilesetEditor::onPaletteEditorChangedPaletteColor() {
this->hasUnsavedChanges = true;
}
bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, QString newLabel)
{
bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile &src, QString newLabel) {
Metatile * dest = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
QString oldLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
if (!dest || !src || (*dest == *src && oldLabel == newLabel))
if (!dest || (*dest == src && oldLabel == newLabel))
return false;
Tileset::setMetatileLabel(metatileId, newLabel, this->primaryTileset, this->secondaryTileset);
@ -921,8 +925,8 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
if (this->tileSelector && this->tileSelector->showUnused) {
int numTiles = projectConfig.getNumTilesInMetatile();
for (int i = 0; i < numTiles; i++) {
if (src->tiles[i].tileId != dest->tiles[i].tileId) {
this->tileSelector->usedTiles[src->tiles[i].tileId] += 1;
if (src.tiles[i].tileId != dest->tiles[i].tileId) {
this->tileSelector->usedTiles[src.tiles[i].tileId] += 1;
this->tileSelector->usedTiles[dest->tiles[i].tileId] -= 1;
}
}
@ -930,7 +934,7 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
}
this->metatile = dest;
*this->metatile = *src;
*this->metatile = src;
this->metatileSelector->select(metatileId);
this->metatileSelector->drawMetatile(metatileId);
this->metatileLayersItem->draw();
@ -939,29 +943,52 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, Q
return true;
}
void TilesetEditor::on_actionUndo_triggered()
{
MetatileHistoryItem *commit = this->metatileHistory.current();
if (!commit) return;
Metatile *prev = commit->prevMetatile;
if (!prev) return;
this->metatileHistory.back();
this->replaceMetatile(commit->metatileId, prev, commit->prevLabel);
void TilesetEditor::initMetatileHistory() {
this->metatileHistory.clear();
updateEditHistoryActions();
this->hasUnsavedChanges = false;
}
void TilesetEditor::on_actionRedo_triggered()
{
void TilesetEditor::commit(MetatileHistoryItem *item) {
this->metatileHistory.push(item);
updateEditHistoryActions();
this->hasUnsavedChanges = true;
}
void TilesetEditor::updateEditHistoryActions() {
ui->actionUndo->setEnabled(this->metatileHistory.canUndo());
ui->actionRedo->setEnabled(this->metatileHistory.canRedo());
}
void TilesetEditor::on_actionUndo_triggered() {
MetatileHistoryItem *commit = this->metatileHistory.current();
if (!commit) return;
this->metatileHistory.back();
if (commit->isSwap) {
swapMetatiles(commit->swapMetatileId, commit->metatileId);
} else if (commit->prevMetatile) {
replaceMetatile(commit->metatileId, *commit->prevMetatile, commit->prevLabel);
};
updateEditHistoryActions();
}
void TilesetEditor::on_actionRedo_triggered() {
MetatileHistoryItem *commit = this->metatileHistory.next();
if (!commit) return;
this->replaceMetatile(commit->metatileId, commit->newMetatile, commit->newLabel);
if (commit->isSwap) {
swapMetatiles(commit->metatileId, commit->swapMetatileId);
} else if (commit->newMetatile) {
replaceMetatile(commit->metatileId, *commit->newMetatile, commit->newLabel);
}
updateEditHistoryActions();
}
void TilesetEditor::on_actionCut_triggered()
{
Metatile * empty = new Metatile(projectConfig.getNumTilesInMetatile());
this->copyMetatile(true);
this->pasteMetatile(empty, "");
delete empty;
this->pasteMetatile(Metatile(projectConfig.getNumTilesInMetatile()), "");
}
void TilesetEditor::on_actionCopy_triggered()
@ -971,7 +998,9 @@ void TilesetEditor::on_actionCopy_triggered()
void TilesetEditor::on_actionPaste_triggered()
{
this->pasteMetatile(this->copiedMetatile, this->copiedMetatileLabel);
if (this->copiedMetatile) {
this->pasteMetatile(*this->copiedMetatile, this->copiedMetatileLabel);
}
}
void TilesetEditor::copyMetatile(bool cut) {
@ -984,12 +1013,15 @@ void TilesetEditor::copyMetatile(bool cut) {
else
*this->copiedMetatile = *toCopy;
ui->actionPaste->setEnabled(true);
// Don't try to copy the label unless it's a cut, these should be unique to each metatile.
this->copiedMetatileLabel = cut ? Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset) : QString();
}
void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel)
{
void TilesetEditor::pasteMetatile(const Metatile &toPaste, QString newLabel) {
if (!this->metatile) return;
Metatile *prevMetatile = new Metatile(*this->metatile);
QString prevLabel = this->ui->lineEdit_metatileLabel->text();
if (newLabel.isNull()) newLabel = prevLabel; // Don't change the label if one wasn't copied
@ -1092,15 +1124,13 @@ void TilesetEditor::importAdvanceMapMetatiles(Tileset *tileset) {
uint16_t metatileId = static_cast<uint16_t>(metatileIdBase + i);
QString prevLabel = Tileset::getOwnedMetatileLabel(metatileId, this->primaryTileset, this->secondaryTileset);
Metatile *prevMetatile = new Metatile(*tileset->metatileAt(i));
MetatileHistoryItem *commit = new MetatileHistoryItem(metatileId,
prevMetatile, new Metatile(*metatiles.at(i)),
prevLabel, prevLabel);
metatileHistory.push(commit);
commit(new MetatileHistoryItem(metatileId,
prevMetatile, new Metatile(*metatiles.at(i)),
prevLabel, prevLabel));
}
tileset->setMetatiles(metatiles);
this->refresh();
this->hasUnsavedChanges = true;
}
void TilesetEditor::on_actionShow_Unused_toggled(bool checked) {
@ -1189,35 +1219,11 @@ void TilesetEditor::countTileUsage() {
this->tileSelector->usedTiles.resize(Project::getNumTilesTotal());
this->tileSelector->usedTiles.fill(0);
QSet<Tileset*> primaryTilesets;
QSet<Tileset*> secondaryTilesets;
for (const auto &layoutId : this->project->layoutIds()) {
Layout *layout = this->project->getLayout(layoutId);
if (layout->tileset_primary_label == this->primaryTileset->name
|| layout->tileset_secondary_label == this->secondaryTileset->name) {
// need to check metatiles
this->project->loadLayoutTilesets(layout);
if (layout->tileset_primary && layout->tileset_secondary) {
primaryTilesets.insert(layout->tileset_primary);
secondaryTilesets.insert(layout->tileset_secondary);
}
}
}
// check primary tilesets that are used with this secondary tileset for
// reference to secondary tiles in primary metatiles
for (const auto &tileset : primaryTilesets) {
for (const auto &metatile : tileset->metatiles()) {
for (const auto &tile : metatile->tiles) {
if (tile.tileId >= Project::getNumTilesPrimary())
this->tileSelector->usedTiles[tile.tileId]++;
}
}
}
// do the opposite for primary tiles in secondary metatiles
for (Tileset *tileset : secondaryTilesets) {
// Count usage of our primary tileset's tiles in the secondary tilesets it gets paired with.
QSet<QString> tilesetNames = this->project->getPairedTilesetLabels(this->primaryTileset);
for (const auto &tilesetName : tilesetNames) {
Tileset *tileset = this->project->getTileset(tilesetName);
if (!tileset) continue;
for (const auto &metatile : tileset->metatiles()) {
for (const auto &tile : metatile->tiles) {
if (tile.tileId < Project::getNumTilesPrimary())
@ -1226,17 +1232,16 @@ void TilesetEditor::countTileUsage() {
}
}
// check this primary tileset metatiles
for (const auto &metatile : this->primaryTileset->metatiles()) {
for (const auto &tile : metatile->tiles) {
this->tileSelector->usedTiles[tile.tileId]++;
}
}
// and the secondary metatiles
for (const auto &metatile : this->secondaryTileset->metatiles()) {
for (const auto &tile : metatile->tiles) {
this->tileSelector->usedTiles[tile.tileId]++;
// Count usage of our secondary tileset's tiles in the primary tilesets it gets paired with.
tilesetNames = this->project->getPairedTilesetLabels(this->secondaryTileset);
for (const auto &tilesetName : tilesetNames) {
Tileset *tileset = this->project->getTileset(tilesetName);
if (!tileset) continue;
for (const auto &metatile : tileset->metatiles()) {
for (const auto &tile : metatile->tiles) {
if (tile.tileId >= Project::getNumTilesPrimary())
this->tileSelector->usedTiles[tile.tileId]++;
}
}
}
}
@ -1304,3 +1309,118 @@ void TilesetEditor::redrawTileSelector() {
this->ui->scrollArea_Tiles->ensureVisible(pos.x(), pos.y(), viewport->width() / 2, viewport->height() / 2);
}
}
void TilesetEditor::commitMetatileSwap(uint16_t metatileIdA, uint16_t metatileIdB) {
if (swapMetatiles(metatileIdA, metatileIdB)) {
commit(new MetatileHistoryItem(metatileIdA, metatileIdB));
}
}
bool TilesetEditor::swapMetatiles(uint16_t metatileIdA, uint16_t metatileIdB) {
this->metatileSelector->clearSwapSelection();
QList<Metatile*> metatiles;
for (const auto &metatileId : {metatileIdA, metatileIdB}) {
Metatile *metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
if (metatile) {
metatiles.append(metatile);
} else {
logError(QString("Failed to load metatile %1 for swap.").arg(Metatile::getMetatileIdString(metatileId)));
}
}
if (metatiles.length() < 2)
return false;
// Swap the metatile data in the tileset
Metatile tempMetatile = *metatiles.at(0);
QString tempLabel = Tileset::getOwnedMetatileLabel(metatileIdA, this->primaryTileset, this->secondaryTileset);
replaceMetatile(metatileIdA, *metatiles.at(1), Tileset::getOwnedMetatileLabel(metatileIdB, this->primaryTileset, this->secondaryTileset));
replaceMetatile(metatileIdB, tempMetatile, tempLabel);
// Record this swap so that we can update the layouts later.
// If this is the inverse of the most recent swap (e.g. from Undo), we instead remove that swap to save time.
if (!this->metatileIdSwaps.isEmpty()) {
auto recentSwapPair = this->metatileIdSwaps.constLast();
if (recentSwapPair.first == metatileIdB && recentSwapPair.second == metatileIdA) {
this->metatileIdSwaps.removeLast();
return true;
}
}
this->metatileIdSwaps.append(QPair<uint16_t,uint16_t>(metatileIdA, metatileIdB));
return true;
}
// If any metatiles swapped positions, apply the swap to all relevant layouts.
// We only do this once changes in the Tileset Editor are saved.
void TilesetEditor::applyMetatileSwapsToLayouts() {
if (this->metatileIdSwaps.isEmpty())
return;
QProgressDialog progress("", "", 0, this->metatileIdSwaps.length(), this);
progress.setAutoClose(true);
progress.setWindowModality(Qt::WindowModal);
progress.setModal(true);
progress.setMinimumDuration(1000);
progress.setValue(progress.minimum());
for (const auto &swapPair : this->metatileIdSwaps) {
progress.setLabelText(QString("Swapping metatiles %1 and %2 in map layouts...")
.arg(Metatile::getMetatileIdString(swapPair.first))
.arg(Metatile::getMetatileIdString(swapPair.second)));
applyMetatileSwapToLayouts(swapPair.first, swapPair.second);
progress.setValue(progress.value() + 1);
}
this->metatileIdSwaps.clear();
}
void TilesetEditor::applyMetatileSwapToLayouts(uint16_t metatileIdA, uint16_t metatileIdB) {
struct TilesetPair {
Tileset* primary = nullptr;
Tileset* secondary = nullptr;
};
TilesetPair tilesets;
// Get which tilesets our swapped metatiles belong to.
auto addSourceTileset = [this](uint16_t metatileId, TilesetPair *tilesets) {
if (this->primaryTileset->contains(metatileId)) {
tilesets->primary = this->primaryTileset;
} else if (this->secondaryTileset->contains(metatileId)) {
tilesets->secondary = this->secondaryTileset;
} else {
// Invalid metatile, shouldn't happen
this->metatileSelector->removeFromSwapSelection(metatileId);
}
};
addSourceTileset(metatileIdA, &tilesets);
addSourceTileset(metatileIdB, &tilesets);
if (!tilesets.primary && !tilesets.secondary) {
return;
}
// In each layout that uses the appropriate tileset(s), swap the two metatiles.
QSet<QString> layoutIds = this->project->getTilesetLayoutIds(tilesets.primary, tilesets.secondary);
for (const auto &layoutId : layoutIds) {
Layout *layout = this->project->loadLayout(layoutId);
if (!layout) continue;
// Perform swap(s) in layout's main data.
for (int y = 0; y < layout->height; y++)
for (int x = 0; x < layout->width; x++) {
uint16_t metatileId = layout->getMetatileId(x, y);
if (metatileId == metatileIdA) {
layout->setMetatileId(x, y, metatileIdB);
} else if (metatileId == metatileIdB) {
layout->setMetatileId(x, y, metatileIdA);
} else continue;
layout->hasUnsavedDataChanges = true;
}
// Perform swap(s) in layout's border data.
for (auto &borderBlock : layout->border) {
if (borderBlock.metatileId() == metatileIdA) {
borderBlock.setMetatileId(metatileIdB);
} else if (borderBlock.metatileId() == metatileIdB) {
borderBlock.setMetatileId(metatileIdA);
} else continue;
layout->hasUnsavedDataChanges = true;
}
}
}

View File

@ -81,7 +81,15 @@ void TilesetEditorMetatileSelector::draw() {
drawDivider();
drawFilters();
drawSelection();
if (this->inSwapMode) {
for (const auto &metatileId : this->swapMetatileIds) {
bool ok;
QPoint pos = metatileIdToPos(metatileId, &ok);
if (ok) drawSelectionRect(pos, QSize(1,1), Qt::DashLine);
}
} else if (isValidMetatileId(this->selectedMetatileId)) {
drawSelection();
}
}
bool TilesetEditorMetatileSelector::select(uint16_t metatileId) {
@ -103,39 +111,76 @@ void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset
draw();
}
void TilesetEditorMetatileSelector::updateSelectedMetatile() {
bool ok;
uint16_t metatileId = posToMetatileId(getSelectionStart(), &ok);
if (!ok)
void TilesetEditorMetatileSelector::addToSwapSelection(uint16_t metatileId) {
if (this->swapMetatileIds.contains(metatileId)) {
return;
}
if (this->swapMetatileIds.length() >= 2) {
this->swapMetatileIds.clear();
}
this->swapMetatileIds.append(metatileId);
draw();
if (this->swapMetatileIds.length() == 2) {
emit swapRequested(this->swapMetatileIds.at(0), this->swapMetatileIds.at(1));
}
}
void TilesetEditorMetatileSelector::removeFromSwapSelection(uint16_t metatileId) {
if (this->swapMetatileIds.removeOne(metatileId)) {
draw();
}
}
void TilesetEditorMetatileSelector::clearSwapSelection() {
if (this->swapMetatileIds.isEmpty())
return;
this->swapMetatileIds.clear();
draw();
}
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
bool ok;
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
if (!ok) return;
if (this->inSwapMode) {
if (this->swapMetatileIds.contains(metatileId)) {
this->removeFromSwapSelection(metatileId);
} else {
this->addToSwapSelection(metatileId);
}
}
SelectablePixmapItem::mousePressEvent(event);
this->selectedMetatileId = metatileId;
emit selectedMetatileChanged(this->selectedMetatileId);
}
bool TilesetEditorMetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) {
bool ok;
posToMetatileId(getCellPos(event->pos()), &ok);
return ok;
}
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedMetatile();
}
void TilesetEditorMetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
if (this->inSwapMode) return;
bool ok;
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
if (!ok) return;
SelectablePixmapItem::mouseMoveEvent(event);
this->updateSelectedMetatile();
this->selectedMetatileId = metatileId;
emit selectedMetatileChanged(this->selectedMetatileId);
emit hoveredMetatileChanged(this->selectedMetatileId);
}
void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
if (!shouldAcceptEvent(event)) return;
if (this->inSwapMode) return;
bool ok;
uint16_t metatileId = posToMetatileId(getCellPos(event->pos()), &ok);
if (!ok) return;
SelectablePixmapItem::mouseReleaseEvent(event);
this->updateSelectedMetatile();
this->selectedMetatileId = metatileId;
emit selectedMetatileChanged(this->selectedMetatileId);
}
void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
@ -195,6 +240,12 @@ QPoint TilesetEditorMetatileSelector::metatileIdToPos(uint16_t metatileId, bool
return QPoint(0,0);
}
bool TilesetEditorMetatileSelector::isValidMetatileId(uint16_t metatileId) const {
bool ok;
metatileIdToPos(metatileId, &ok);
return ok;
}
QPoint TilesetEditorMetatileSelector::getMetatileIdCoordsOnWidget(uint16_t metatileId) const {
QPoint pos = metatileIdToPos(metatileId);
pos.rx() = (pos.x() * this->cellWidth) + (this->cellWidth / 2);
@ -323,3 +374,19 @@ void TilesetEditorMetatileSelector::drawCounts() {
this->setPixmap(metatilesPixmap);
}
void TilesetEditorMetatileSelector::setSwapMode(bool enabled) {
if (enabled == this->inSwapMode)
return;
this->inSwapMode = enabled;
this->swapMetatileIds.clear();
if (porymapConfig.prettyCursors) {
if (enabled) {
static const QCursor cursor = QCursor(QPixmap(":/icons/swap_cursor.ico"), 10, 10);
setCursor(cursor);
} else {
unsetCursor();
}
}
draw();
}