From f6f07ca5fc85556b5b404a2d12f973d1d05edb8d Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 29 Jul 2025 03:10:46 -0400 Subject: [PATCH] Allow vertical layout for layer view --- forms/tileseteditor.ui | 72 +++++++++----- include/config.h | 1 + include/ui/metatilelayersitem.h | 20 +++- include/ui/selectablepixmapitem.h | 19 +++- include/ui/tileseteditor.h | 3 +- include/ui/tileseteditortileselector.h | 7 +- src/config.cpp | 5 + src/editor.cpp | 4 +- src/project.cpp | 17 ++++ src/ui/metatilelayersitem.cpp | 131 +++++++++++++++---------- src/ui/selectablepixmapitem.cpp | 84 +++++++++------- src/ui/tileseteditor.cpp | 116 +++++++++++++--------- src/ui/tileseteditortileselector.cpp | 15 ++- 13 files changed, 317 insertions(+), 177 deletions(-) diff --git a/forms/tileseteditor.ui b/forms/tileseteditor.ui index 895fae0f..b7f78f66 100644 --- a/forms/tileseteditor.ui +++ b/forms/tileseteditor.ui @@ -158,7 +158,7 @@ 0 - 166 + 190 @@ -194,7 +194,7 @@ false - + @@ -451,12 +451,6 @@ - - - 0 - 0 - - 98 @@ -497,16 +491,22 @@ + + + 0 + 0 + + - 18 - 18 + 98 + 98 98 - 34 + 98 @@ -520,22 +520,22 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 1 + + + + - - - - Qt::Orientation::Vertical - - - - 20 - 10 - - - - @@ -686,6 +686,14 @@ View + + + Layer Arrangement + + + + + @@ -863,6 +871,22 @@ Secondary... + + + true + + + Horizontal + + + + + true + + + Vertical + + diff --git a/include/config.h b/include/config.h index 25482457..1f2a1a14 100644 --- a/include/config.h +++ b/include/config.h @@ -108,6 +108,7 @@ public: int metatilesZoom; int tilesetEditorMetatilesZoom; int tilesetEditorTilesZoom; + Qt::Orientation tilesetEditorLayerOrientation; bool showPlayerView; bool showCursorTile; bool showBorder; diff --git a/include/ui/metatilelayersitem.h b/include/ui/metatilelayersitem.h index 61eae902..7d1c74ce 100644 --- a/include/ui/metatilelayersitem.h +++ b/include/ui/metatilelayersitem.h @@ -9,28 +9,40 @@ class MetatileLayersItem: public SelectablePixmapItem { Q_OBJECT public: - MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset); + MetatileLayersItem(Metatile *metatile, + Tileset *primaryTileset, + Tileset *secondaryTileset, + Qt::Orientation orientation = Qt::Horizontal); void draw(); void setTilesets(Tileset*, Tileset*); void setMetatile(Metatile*); void clearLastModifiedCoords(); void clearLastHoveredCoords(); + + QPoint tileIndexToPos(int index) const { return this->tilePositions.value(index); } + int posToTileIndex(const QPoint &pos) const { return this->tilePositions.indexOf(pos); } + int posToTileIndex(int x, int y) const { return posToTileIndex(QPoint(x, y)); } + + void setOrientation(Qt::Orientation orientation); + bool showGrid; private: Metatile* metatile; Tileset *primaryTileset; Tileset *secondaryTileset; + Qt::Orientation orientation; QPoint prevChangedPos; QPoint prevHoveredPos; + QList tilePositions; + QPoint getBoundedPos(const QPointF &); - void requestTileChange(const QPoint &pos); - void requestPaletteChange(const QPoint &pos); void updateSelection(); + void hover(const QPoint &pos); signals: void tileChanged(const QPoint &pos); void paletteChanged(const QPoint &pos); - void selectedTilesChanged(QPoint, int, int); + void selectedTilesChanged(const QPoint &pos, const QSize &dimensions); void hoveredTileChanged(const Tile &tile); void hoveredTileCleared(); protected: diff --git a/include/ui/selectablepixmapitem.h b/include/ui/selectablepixmapitem.h index 05dadb86..433b2bca 100644 --- a/include/ui/selectablepixmapitem.h +++ b/include/ui/selectablepixmapitem.h @@ -19,9 +19,13 @@ public: selectionOffsetX(0), selectionOffsetY(0) {} - virtual QSize getSelectionDimensions() const; + virtual QSize getSelectionDimensions() const { return QSize(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1); } virtual void draw() = 0; + virtual void setMaxSelectionSize(const QSize &size) { setMaxSelectionSize(size.width(), size.height()); } + virtual void setMaxSelectionSize(int width, int height); + QSize maxSelectionSize() { return QSize(this->maxSelectionWidth, this->maxSelectionHeight); } + protected: int cellWidth; int cellHeight; @@ -33,17 +37,22 @@ protected: int selectionOffsetY; QPoint getSelectionStart(); - void select(int x, int y, int width = 0, int height = 0); - void select(const QPoint &pos, const QSize &size = QSize(0,0)) { select(pos.x(), pos.y(), size.width(), size.height()); } - void updateSelection(int, int); + 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); virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; 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; } signals: - void selectionChanged(int, int, int, int); + void selectionChanged(const QPoint&, const QSize&); + +private: + QPoint prevCellPos = QPoint(-1,-1); }; #endif // SELECTABLEPIXMAPITEM_H diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index 4d8c5b95..43467713 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -69,7 +69,7 @@ private slots: void onHoveredTileChanged(const Tile&); void onHoveredTileChanged(uint16_t); void onHoveredTileCleared(); - void onMetatileLayerSelectionChanged(QPoint, int, int); + void onMetatileLayerSelectionChanged(const QPoint&, const QSize&); void onPaletteEditorChangedPaletteColor(); void on_actionChange_Metatiles_Count_triggered(); @@ -138,6 +138,7 @@ private: void refreshTileFlips(); void refreshPaletteId(); void paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly = false); + void setMetatileLayerOrientation(Qt::Orientation orientation); Ui::TilesetEditor *ui; History metatileHistory; diff --git a/include/ui/tileseteditortileselector.h b/include/ui/tileseteditortileselector.h index 01b0aed9..915a9227 100644 --- a/include/ui/tileseteditortileselector.h +++ b/include/ui/tileseteditortileselector.h @@ -7,8 +7,8 @@ class TilesetEditorTileSelector: public SelectablePixmapItem { Q_OBJECT public: - TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset, int numLayers) - : SelectablePixmapItem(16, 16, numLayers * Metatile::tileWidth(), Metatile::tileHeight()) { + TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset) + : SelectablePixmapItem(16, 16, Metatile::tileWidth(), Metatile::tileWidth()) { this->primaryTileset = primaryTileset; this->secondaryTileset = secondaryTileset; this->numTilesWide = 16; @@ -19,6 +19,7 @@ public: setAcceptHoverEvents(true); } QSize getSelectionDimensions() const override; + void setMaxSelectionSize(int width, int height) override; void draw() override; void select(uint16_t metatileId); void highlight(uint16_t metatileId); @@ -31,6 +32,7 @@ public: QImage buildPrimaryTilesIndexedImage(); QImage buildSecondaryTilesIndexedImage(); + QVector usedTiles; bool showUnused = false; bool showDivider = false; @@ -49,6 +51,7 @@ private: int externalSelectionHeight; QList externalSelectedTiles; QList externalSelectedPos; + QPoint prevCellPos = QPoint(-1,-1); Tileset *primaryTileset; Tileset *secondaryTileset; diff --git a/src/config.cpp b/src/config.cpp index b53f171b..4bff6a78 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -338,6 +338,7 @@ void PorymapConfig::reset() { this->metatilesZoom = 30; this->tilesetEditorMetatilesZoom = 30; this->tilesetEditorTilesZoom = 30; + this->tilesetEditorLayerOrientation = Qt::Horizontal; this->showPlayerView = false; this->showCursorTile = true; this->showBorder = true; @@ -454,6 +455,9 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { this->tilesetEditorMetatilesZoom = getConfigInteger(key, value, 10, 100, 30); } else if (key == "tileset_editor_tiles_zoom") { this->tilesetEditorTilesZoom = getConfigInteger(key, value, 10, 100, 30); + } else if (key == "tileset_editor_layer_orientation") { + // Being explicit here to avoid casting out-of-range values. + this->tilesetEditorLayerOrientation = (getConfigInteger(key, value) == static_cast(Qt::Horizontal)) ? Qt::Horizontal : Qt::Vertical; } else if (key == "show_player_view") { this->showPlayerView = getConfigBool(key, value); } else if (key == "show_cursor_tile") { @@ -604,6 +608,7 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("metatiles_zoom", QString::number(this->metatilesZoom)); map.insert("tileset_editor_metatiles_zoom", QString::number(this->tilesetEditorMetatilesZoom)); map.insert("tileset_editor_tiles_zoom", QString::number(this->tilesetEditorTilesZoom)); + map.insert("tileset_editor_layer_orientation", QString::number(this->tilesetEditorLayerOrientation)); map.insert("show_player_view", this->showPlayerView ? "1" : "0"); map.insert("show_cursor_tile", this->showCursorTile ? "1" : "0"); map.insert("show_border", this->showBorder ? "1" : "0"); diff --git a/src/editor.cpp b/src/editor.cpp index 69009613..9c6a7d0a 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1773,8 +1773,8 @@ void Editor::displayMovementPermissionSelector() { this, &Editor::onHoveredMovementPermissionChanged); connect(movement_permissions_selector_item, &MovementPermissionsSelector::hoveredMovementPermissionCleared, this, &Editor::onHoveredMovementPermissionCleared); - connect(movement_permissions_selector_item, &SelectablePixmapItem::selectionChanged, [this](int x, int y, int, int) { - this->setCollisionTabSpinBoxes(x, y); + connect(movement_permissions_selector_item, &SelectablePixmapItem::selectionChanged, [this](const QPoint &pos, const QSize&) { + this->setCollisionTabSpinBoxes(pos.x(), pos.y()); }); movement_permissions_selector_item->select(projectConfig.defaultCollision, projectConfig.defaultElevation); } diff --git a/src/project.cpp b/src/project.cpp index 9c5fd9af..07ac72ea 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -1187,6 +1187,18 @@ bool Project::loadLayoutTilesets(Layout *layout) { logError(QString("Failed to load %1: missing secondary tileset label.").arg(layout->name)); return false; } + if (!this->primaryTilesetLabels.contains(layout->tileset_primary_label)) { + logError(QString("Failed to load %1: unknown primary tileset label '%2'.") + .arg(layout->name) + .arg(layout->tileset_primary_label)); + return false; + } + if (!this->secondaryTilesetLabels.contains(layout->tileset_secondary_label)) { + logError(QString("Failed to load %1: unknown secondary tileset label '%2'.") + .arg(layout->name) + .arg(layout->tileset_secondary_label)); + return false; + } layout->tileset_primary = getTileset(layout->tileset_primary_label); layout->tileset_secondary = getTileset(layout->tileset_secondary_label); @@ -1194,6 +1206,11 @@ bool Project::loadLayoutTilesets(Layout *layout) { } Tileset* Project::getTileset(const QString &label, bool forceLoad) { + if (!this->tilesetLabelsOrdered.contains(label)) { + logError(QString("Unknown tileset name '%1'.").arg(label)); + return nullptr; + } + Tileset *tileset = nullptr; auto it = this->tilesetCache.constFind(label); diff --git a/src/ui/metatilelayersitem.cpp b/src/ui/metatilelayersitem.cpp index 9c95f6ef..34afce00 100644 --- a/src/ui/metatilelayersitem.cpp +++ b/src/ui/metatilelayersitem.cpp @@ -3,8 +3,8 @@ #include "imageproviders.h" #include -MetatileLayersItem::MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset) - : SelectablePixmapItem(16, 16, Metatile::tileWidth() * projectConfig.getNumLayersInMetatile(), Metatile::tileHeight()), +MetatileLayersItem::MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset, Qt::Orientation orientation) + : SelectablePixmapItem(16, 16, Metatile::tileWidth(), Metatile::tileHeight()), metatile(metatile), primaryTileset(primaryTileset), secondaryTileset(secondaryTileset) @@ -12,28 +12,51 @@ MetatileLayersItem::MetatileLayersItem(Metatile *metatile, Tileset *primaryTiles clearLastModifiedCoords(); clearLastHoveredCoords(); setAcceptHoverEvents(true); + setOrientation(orientation); } -static const QList tilePositions = { - QPoint(0, 0), - QPoint(1, 0), - QPoint(0, 1), - QPoint(1, 1), - QPoint(2, 0), - QPoint(3, 0), - QPoint(2, 1), - QPoint(3, 1), - QPoint(4, 0), - QPoint(5, 0), - QPoint(4, 1), - QPoint(5, 1), -}; +void MetatileLayersItem::setOrientation(Qt::Orientation orientation) { + this->orientation = orientation; + int maxWidth = Metatile::tileWidth(); + int maxHeight = Metatile::tileHeight(); + + // Generate a table of tile positions that allows us to map between + // the index of a tile in the metatile and its position in this layer view. + this->tilePositions.clear(); + if (this->orientation == Qt::Horizontal) { + // Tiles are laid out horizontally, with the bottom layer on the left: + // 0 1 4 5 8 9 + // 2 3 6 7 10 11 + for (int layer = 0; layer < projectConfig.getNumLayersInMetatile(); layer++) + for (int y = 0; y < Metatile::tileHeight(); y++) + for (int x = 0; x < Metatile::tileWidth(); x++) { + this->tilePositions.append(QPoint(x + layer * Metatile::tileWidth(), y)); + } + maxWidth *= projectConfig.getNumLayersInMetatile(); + } else if (this->orientation == Qt::Vertical) { + // Tiles are laid out vertically, with the bottom layer on the bottom: + // 8 9 + // 10 11 + // 4 5 + // 6 7 + // 0 1 + // 2 3 + for (int layer = projectConfig.getNumLayersInMetatile() - 1; layer >= 0; layer--) + for (int y = 0; y < Metatile::tileHeight(); y++) + for (int x = 0; x < Metatile::tileWidth(); x++) { + this->tilePositions.append(QPoint(x, y + layer * Metatile::tileHeight())); + } + maxHeight *= projectConfig.getNumLayersInMetatile(); + } + setMaxSelectionSize(maxWidth, maxHeight); + update(); + if (!this->pixmap().isNull()) { + draw(); + } +} void MetatileLayersItem::draw() { - const int numLayers = projectConfig.getNumLayersInMetatile(); - const int layerWidth = this->cellWidth * Metatile::tileWidth(); - const int layerHeight = this->cellHeight * Metatile::tileHeight(); - QPixmap pixmap(numLayers * layerWidth, layerHeight); + QPixmap pixmap(this->cellWidth * this->maxSelectionWidth, this->cellHeight * this->maxSelectionHeight); QPainter painter(&pixmap); // Draw tile images @@ -47,15 +70,22 @@ void MetatileLayersItem::draw() { true ).scaled(this->cellWidth, this->cellHeight); tile.flip(&tileImage); - QPoint pos = tilePositions.at(i); + QPoint pos = tileIndexToPos(i); painter.drawImage(pos.x() * this->cellWidth, pos.y() * this->cellHeight, tileImage); } if (this->showGrid) { // Draw grid painter.setPen(Qt::white); - for (int i = 1; i < numLayers; i++) { - int x = i * layerWidth; - painter.drawLine(x, 0, x, layerHeight); + const int layerWidth = this->cellWidth * Metatile::tileWidth(); + const int layerHeight = this->cellHeight * Metatile::tileHeight(); + for (int i = 1; i < projectConfig.getNumLayersInMetatile(); i++) { + if (this->orientation == Qt::Vertical) { + int y = i * layerHeight; + painter.drawLine(0, y, layerWidth, y); + } else if (this->orientation == Qt::Horizontal) { + int x = i * layerWidth; + painter.drawLine(x, 0, x, layerHeight); + } } } @@ -76,51 +106,41 @@ void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondary this->clearLastHoveredCoords(); } -// We request our current selection to be painted, -// this class doesn't handle changing the metatile data. -void MetatileLayersItem::requestTileChange(const QPoint &pos) { - this->prevChangedPos = pos; - this->clearLastHoveredCoords(); - emit this->tileChanged(pos); -} -void MetatileLayersItem::requestPaletteChange(const QPoint &pos) { - this->prevChangedPos = pos; - this->clearLastHoveredCoords(); - emit this->paletteChanged(pos); -} - void MetatileLayersItem::updateSelection() { - QPoint selectionOrigin = this->getSelectionStart(); - QSize dimensions = this->getSelectionDimensions(); - emit this->selectedTilesChanged(selectionOrigin, dimensions.width(), dimensions.height()); - this->drawSelection(); + drawSelection(); + emit selectedTilesChanged(getSelectionStart(), getSelectionDimensions()); } void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + const QPoint pos = this->getBoundedPos(event->pos()); + hover(pos); + if (event->buttons() & Qt::RightButton) { SelectablePixmapItem::mousePressEvent(event); updateSelection(); } else if (event->modifiers() & Qt::ControlModifier) { - requestPaletteChange(getBoundedPos(event->pos())); + emit paletteChanged(pos); } else { - requestTileChange(getBoundedPos(event->pos())); + emit tileChanged(pos); } + this->prevChangedPos = pos; } void MetatileLayersItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + const QPoint pos = this->getBoundedPos(event->pos()); + if (this->prevChangedPos == pos) + return; + hover(pos); + if (event->buttons() & Qt::RightButton) { SelectablePixmapItem::mouseMoveEvent(event); updateSelection(); + } else if (event->modifiers() & Qt::ControlModifier) { + emit paletteChanged(pos); } else { - const QPoint pos = this->getBoundedPos(event->pos()); - if (this->prevChangedPos != pos) { - if (event->modifiers() & Qt::ControlModifier) { - requestPaletteChange(pos); - } else { - requestTileChange(pos); - } - } + emit tileChanged(pos); } + this->prevChangedPos = pos; } void MetatileLayersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { @@ -130,16 +150,19 @@ void MetatileLayersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { } // Clear selection rectangle - this->draw(); + draw(); } void MetatileLayersItem::hoverMoveEvent(QGraphicsSceneHoverEvent * event) { - const QPoint pos = this->getBoundedPos(event->pos()); + hover(getBoundedPos(event->pos())); +} + +void MetatileLayersItem::hover(const QPoint &pos) { if (pos == this->prevHoveredPos) return; this->prevHoveredPos = pos; - int tileIndex = tilePositions.indexOf(pos); + int tileIndex = posToTileIndex(pos); if (tileIndex < 0 || tileIndex >= this->metatile->tiles.length()) return; diff --git a/src/ui/selectablepixmapitem.cpp b/src/ui/selectablepixmapitem.cpp index d3aa2914..7a38913e 100644 --- a/src/ui/selectablepixmapitem.cpp +++ b/src/ui/selectablepixmapitem.cpp @@ -1,11 +1,6 @@ #include "selectablepixmapitem.h" #include -QSize SelectablePixmapItem::getSelectionDimensions() const -{ - return QSize(abs(this->selectionOffsetX) + 1, abs(this->selectionOffsetY) + 1); -} - QPoint SelectablePixmapItem::getSelectionStart() { int x = this->selectionInitialX; @@ -15,47 +10,62 @@ QPoint SelectablePixmapItem::getSelectionStart() return QPoint(x, y); } -void SelectablePixmapItem::select(int x, int y, int width, int height) +void SelectablePixmapItem::select(const QPoint &pos, const QSize &size) { - this->selectionInitialX = x; - this->selectionInitialY = y; - this->selectionOffsetX = qBound(0, width, this->maxSelectionWidth); - this->selectionOffsetY = qBound(0, height, this->maxSelectionHeight); - this->draw(); - emit this->selectionChanged(x, y, width, height); + 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); + draw(); + emit selectionChanged(pos, getSelectionDimensions()); } void SelectablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPoint pos = this->getCellPos(event->pos()); + QPoint pos = getCellPos(event->pos()); this->selectionInitialX = pos.x(); this->selectionInitialY = pos.y(); this->selectionOffsetX = 0; this->selectionOffsetY = 0; - this->updateSelection(pos.x(), pos.y()); + this->prevCellPos = pos; + updateSelection(pos); } void SelectablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - QPoint pos = this->getCellPos(event->pos()); - this->updateSelection(pos.x(), pos.y()); + QPoint pos = getCellPos(event->pos()); + if (pos == this->prevCellPos) + return; + this->prevCellPos = pos; + updateSelection(pos); } void SelectablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - QPoint pos = this->getCellPos(event->pos()); - this->updateSelection(pos.x(), pos.y()); + updateSelection(getCellPos(event->pos())); } -void SelectablePixmapItem::updateSelection(int x, int y) -{ +void SelectablePixmapItem::setMaxSelectionSize(int width, int height) { + this->maxSelectionWidth = qMax(width, 1); + this->maxSelectionHeight = qMax(height, 1); + + // 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; + draw(); + emit selectionChanged(getSelectionStart(), getSelectionDimensions()); + } +} + +void SelectablePixmapItem::updateSelection(const QPoint &pos) { // Snap to a valid position inside the selection area. - int width = pixmap().width() / this->cellWidth; - int height = pixmap().height() / this->cellHeight; - if (x < 0) x = 0; - if (x >= width) x = width - 1; - if (y < 0) y = 0; - if (y >= height) y = height - 1; + int x = qBound(0, pos.x(), cellsWide() - 1); + int y = qBound(0, pos.y(), cellsTall() - 1); this->selectionOffsetX = x - this->selectionInitialX; this->selectionOffsetY = y - this->selectionInitialY; @@ -76,22 +86,20 @@ void SelectablePixmapItem::updateSelection(int x, int y) this->selectionOffsetY = -this->maxSelectionHeight + 1; } - this->draw(); - emit this->selectionChanged(x, y, width, height); + draw(); + emit selectionChanged(QPoint(x, y), getSelectionDimensions()); } -QPoint SelectablePixmapItem::getCellPos(QPointF pos) -{ - if (pos.x() < 0) pos.setX(0); - if (pos.y() < 0) pos.setY(0); - if (pos.x() >= this->pixmap().width()) pos.setX(this->pixmap().width() - 1); - if (pos.y() >= this->pixmap().height()) pos.setY(this->pixmap().height() - 1); - return QPoint(static_cast(pos.x()) / this->cellWidth, - static_cast(pos.y()) / this->cellHeight); +QPoint SelectablePixmapItem::getCellPos(QPointF pos) { + if (this->cellWidth == 0 || this->cellHeight == 0 || pixmap().isNull()) + return QPoint(0,0); + + int x = qBound(0, static_cast(pos.x()), pixmap().width() - 1); + int y = qBound(0, static_cast(pos.y()), pixmap().height() - 1); + return QPoint(x / this->cellWidth, y / this->cellHeight); } -void SelectablePixmapItem::drawSelection() -{ +void SelectablePixmapItem::drawSelection() { QPoint origin = this->getSelectionStart(); QSize dimensions = this->getSelectionDimensions(); QRect selectionRect(origin.x() * this->cellWidth, origin.y() * this->cellHeight, dimensions.width() * this->cellWidth, dimensions.height() * this->cellHeight); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index adc3f280..5b4c34b1 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -62,12 +62,16 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent) connect(ui->spinBox_paletteSelector, QOverload::of(&QSpinBox::valueChanged), this, &TilesetEditor::refreshPaletteId); + connect(ui->actionLayer_Arrangement_Horizontal, &QAction::triggered, [this] { setMetatileLayerOrientation(Qt::Horizontal); }); + connect(ui->actionLayer_Arrangement_Vertical, &QAction::triggered, [this] { setMetatileLayerOrientation(Qt::Vertical); }); + initAttributesUi(); initMetatileSelector(); initMetatileLayersItem(); initTileSelector(); initSelectedTileItem(); initShortcuts(); + setMetatileLayerOrientation(porymapConfig.tilesetEditorLayerOrientation); this->metatileSelector->select(0); restoreWindowState(); } @@ -234,6 +238,53 @@ void TilesetEditor::initMetatileSelector() this->ui->horizontalSlider_MetatilesZoom->setValue(porymapConfig.tilesetEditorMetatilesZoom); } +void TilesetEditor::setMetatileLayerOrientation(Qt::Orientation orientation) { + // Sync settings + bool horizontal = (orientation == Qt::Horizontal); + porymapConfig.tilesetEditorLayerOrientation = orientation; + const QSignalBlocker b_Horizontal(ui->actionLayer_Arrangement_Horizontal); + const QSignalBlocker b_Vertical(ui->actionLayer_Arrangement_Vertical); + ui->actionLayer_Arrangement_Horizontal->setChecked(horizontal); + ui->actionLayer_Arrangement_Vertical->setChecked(!horizontal); + + this->metatileLayersItem->setOrientation(orientation); + + int numTilesWide = Metatile::tileWidth(); + int numTilesTall = Metatile::tileHeight(); + int numLayers = projectConfig.getNumLayersInMetatile(); + if (horizontal) { + numTilesWide *= numLayers; + } else { + numTilesTall *= numLayers; + } + this->tileSelector->setMaxSelectionSize(numTilesWide, numTilesTall); + + const int scale = 2; + int w = Tile::pixelWidth() * numTilesWide * scale + 2; + int h = Tile::pixelHeight() * numTilesTall * scale + 2; + ui->graphicsView_selectedTile->setFixedSize(w, h); + ui->graphicsView_metatileLayers->setFixedSize(w, h); + + // If the layers are laid out vertically then the orientation is obvious, no need to label them. + ui->label_BottomTop->setVisible(horizontal); + + // Let the graphics view take over the label's vertical space (or conversely, give the space back). + // (This is a bit of a process, apparently there's no quick way to set a widget's row / row span once they're added to the layout + int row, col, rowSpan, colSpan; + int index = ui->gridLayout_MetatileProperties->indexOf(ui->label_BottomTop); + ui->gridLayout_MetatileProperties->getItemPosition(index, &row, &col, &rowSpan, &colSpan); + + // TODO: Rearrange the rest of the metatile properties panel. The vertical triple-layer metatiles layout esp. looks terrible. + ui->gridLayout_MetatileProperties->removeWidget(ui->graphicsView_metatileLayers); + if (horizontal) { + // Give space from graphics view back to label + ui->gridLayout_MetatileProperties->addWidget(ui->graphicsView_metatileLayers, row + 1, col, rowSpan, colSpan); + } else { + // Take space from label and give it to graphics view + ui->gridLayout_MetatileProperties->addWidget(ui->graphicsView_metatileLayers, row, col, rowSpan + 1, colSpan); + } +} + void TilesetEditor::initMetatileLayersItem() { Metatile *metatile = Tileset::getMetatile(this->getSelectedMetatileId(), this->primaryTileset, this->secondaryTileset); this->metatileLayersItem = new MetatileLayersItem(metatile, this->primaryTileset, this->secondaryTileset); @@ -252,9 +303,8 @@ void TilesetEditor::initMetatileLayersItem() { this->ui->graphicsView_metatileLayers->setScene(this->metatileLayersScene); } -void TilesetEditor::initTileSelector() -{ - this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset, projectConfig.getNumLayersInMetatile()); +void TilesetEditor::initTileSelector() { + this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset); connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged, [this](uint16_t tileId) { onHoveredTileChanged(tileId); }); @@ -277,7 +327,6 @@ void TilesetEditor::initSelectedTileItem() { this->selectedTileScene = new QGraphicsScene; this->drawSelectedTiles(); this->ui->graphicsView_selectedTile->setScene(this->selectedTileScene); - this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width() + 2, this->selectedTilePixmapItem->pixmap().height() + 2); } void TilesetEditor::initShortcuts() { @@ -392,13 +441,12 @@ void TilesetEditor::drawSelectedTiles() { QImage selectionImage(imgTileWidth * dimensions.width(), imgTileHeight * dimensions.height(), QImage::Format_RGBA8888); QPainter painter(&selectionImage); int tileIndex = 0; - for (int j = 0; j < dimensions.height(); j++) { - for (int i = 0; i < dimensions.width(); i++) { - auto tile = tiles.at(tileIndex); + for (int y = 0; y < dimensions.height(); y++) { + for (int x = 0; x < dimensions.width(); x++) { + auto tile = tiles.at(tileIndex++); QImage tileImage = getPalettedTileImage(tile.tileId, this->primaryTileset, this->secondaryTileset, tile.palette, true).scaled(imgTileWidth, imgTileHeight); tile.flip(&tileImage); - tileIndex++; - painter.drawImage(i * imgTileWidth, j * imgTileHeight, tileImage); + painter.drawImage(x * imgTileWidth, y * imgTileHeight, tileImage); } } @@ -407,7 +455,6 @@ void TilesetEditor::drawSelectedTiles() { QSize size(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height()); this->ui->graphicsView_selectedTile->setSceneRect(0, 0, size.width(), size.height()); - this->ui->graphicsView_selectedTile->setFixedSize(size.width() + 2, size.height() + 2); } void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) { @@ -436,7 +483,6 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) { this->metatileLayersItem->setMetatile(metatile); this->metatileLayersItem->draw(); - this->ui->graphicsView_metatileLayers->setFixedSize(this->metatileLayersItem->pixmap().width() + 2, this->metatileLayersItem->pixmap().height() + 2); MetatileLabelPair labels = Tileset::getMetatileLabelPair(metatileId, this->primaryTileset, this->secondaryTileset); this->ui->lineEdit_metatileLabel->setText(labels.owned); @@ -467,33 +513,18 @@ void TilesetEditor::onHoveredTileCleared() { } void TilesetEditor::paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly) { - static const QList tileCoords = QList{ - QPoint(0, 0), - QPoint(1, 0), - QPoint(0, 1), - QPoint(1, 1), - QPoint(2, 0), - QPoint(3, 0), - QPoint(2, 1), - QPoint(3, 1), - QPoint(4, 0), - QPoint(5, 0), - QPoint(4, 1), - QPoint(5, 1), - }; bool changed = false; Metatile *prevMetatile = new Metatile(*this->metatile); QSize dimensions = this->tileSelector->getSelectionDimensions(); QList tiles = this->tileSelector->getSelectedTiles(); - int selectedTileIndex = 0; + int srcTileIndex = 0; int maxTileIndex = projectConfig.getNumTilesInMetatile(); - for (int j = 0; j < dimensions.height(); j++) { - for (int i = 0; i < dimensions.width(); i++) { - int tileIndex = ((pos.x() + i) / 2 * 4) + ((pos.y() + j) * 2) + ((pos.x() + i) % 2); - QPoint tilePos = tileCoords.at(tileIndex); - if (tileIndex < maxTileIndex && tilePos.x() >= pos.x() && tilePos.y() >= pos.y()){ - Tile &destTile = this->metatile->tiles[tileIndex]; - const Tile srcTile = tiles.at(selectedTileIndex); + for (int y = 0; y < dimensions.height(); y++) { + for (int x = 0; x < dimensions.width(); x++) { + int destTileIndex = this->metatileLayersItem->posToTileIndex(pos.x() + x, pos.y() + y); + if (destTileIndex < maxTileIndex) { + Tile &destTile = this->metatile->tiles[destTileIndex]; + const Tile srcTile = tiles.at(srcTileIndex++); if (paletteOnly) { if (srcTile.palette == destTile.palette) continue; // Ignore no-ops for edit history @@ -511,7 +542,6 @@ void TilesetEditor::paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly) } changed = true; } - selectedTileIndex++; } } if (!changed) { @@ -525,26 +555,24 @@ void TilesetEditor::paintSelectedLayerTiles(const QPoint &pos, bool paletteOnly) this->commitMetatileChange(prevMetatile); } -void TilesetEditor::onMetatileLayerSelectionChanged(QPoint selectionOrigin, int width, int height) { +void TilesetEditor::onMetatileLayerSelectionChanged(const QPoint &selectionOrigin, const QSize &size) { QList tiles; QList tileIdxs; - int x = selectionOrigin.x(); - int y = selectionOrigin.y(); int maxTileIndex = projectConfig.getNumTilesInMetatile(); - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - int tileIndex = ((x + i) / 2 * 4) + ((y + j) * 2) + ((x + i) % 2); + for (int j = 0; j < size.height(); j++) { + 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.at(tileIndex)); + tiles.append(this->metatile->tiles.value(tileIndex)); tileIdxs.append(tileIndex); } } } - this->tileSelector->setExternalSelection(width, height, tiles, tileIdxs); - if (width == 1 && height == 1) { + this->tileSelector->setExternalSelection(size.width(), size.height(), tiles, tileIdxs); + if (size == QSize(1,1)) { setPaletteId(tiles[0].palette); - this->tileSelector->highlight(static_cast(tiles[0].tileId)); + this->tileSelector->highlight(tiles[0].tileId); this->redrawTileSelector(); } this->metatileLayersItem->clearLastModifiedCoords(); diff --git a/src/ui/tileseteditortileselector.cpp b/src/ui/tileseteditortileselector.cpp index 3d42cb72..ec3caa83 100644 --- a/src/ui/tileseteditortileselector.cpp +++ b/src/ui/tileseteditortileselector.cpp @@ -12,6 +12,11 @@ QSize TilesetEditorTileSelector::getSelectionDimensions() const { } } +void TilesetEditorTileSelector::setMaxSelectionSize(int width, int height) { + SelectablePixmapItem::setMaxSelectionSize(width, height); + updateSelectedTiles(); +} + void TilesetEditorTileSelector::updateBasePixmap() { if (!this->primaryTileset || !this->secondaryTileset || this->numTilesWide == 0) { this->basePixmap = QPixmap(); @@ -134,7 +139,7 @@ QList TilesetEditorTileSelector::buildSelectedTiles(int width, int height, // If we've completed a layer row, or its the last tile of an incompletely // selected layer, then append the layer row to the full row // If not an external selection, treat the whole row as 1 "layer" - if (i == width - 1 || (this->externalSelection && (this->externalSelectedPos.at(index) % 4) & 1)) { + if (i == width - 1 || (this->externalSelection && (this->externalSelectedPos.at(index) % Metatile::tilesPerLayer()) & 1)) { row.append(layerRow); layerRow.clear(); } @@ -170,16 +175,20 @@ uint16_t TilesetEditorTileSelector::getTileId(int x, int y) { } void TilesetEditorTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) { + this->prevCellPos = getCellPos(event->pos()); SelectablePixmapItem::mousePressEvent(event); this->updateSelectedTiles(); emit selectedTilesChanged(); } void TilesetEditorTileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + QPoint pos = getCellPos(event->pos()); + if (this->prevCellPos == pos) + return; + this->prevCellPos = pos; + SelectablePixmapItem::mouseMoveEvent(event); this->updateSelectedTiles(); - - QPoint pos = this->getCellPos(event->pos()); uint16_t tile = this->getTileId(pos.x(), pos.y()); emit hoveredTileChanged(tile); emit selectedTilesChanged();