Allow vertical layout for layer view

This commit is contained in:
GriffinR 2025-07-29 03:10:46 -04:00
parent a88730ee3f
commit f6f07ca5fc
13 changed files with 317 additions and 177 deletions

View File

@ -158,7 +158,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>166</height>
<height>190</height>
</size>
</property>
<property name="frameShape">
@ -194,7 +194,7 @@
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<layout class="QGridLayout" name="gridLayout_MetatileProperties">
<item row="15" column="0" colspan="4">
<widget class="QLineEdit" name="lineEdit_metatileLabel">
<property name="clearButtonEnabled">
@ -451,12 +451,6 @@
</item>
<item row="4" column="0" colspan="2">
<widget class="QFrame" name="frame_SelectedTile">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>98</width>
@ -497,16 +491,22 @@
</item>
<item>
<widget class="QGraphicsView" name="graphicsView_selectedTile">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>18</width>
<height>18</height>
<width>98</width>
<height>98</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>98</width>
<height>34</height>
<height>98</height>
</size>
</property>
<property name="frameShape">
@ -520,22 +520,22 @@
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="6" column="0" colspan="2">
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
@ -686,6 +686,14 @@
<property name="title">
<string>View</string>
</property>
<widget class="QMenu" name="menuLayer_Arrangement">
<property name="title">
<string>Layer Arrangement</string>
</property>
<addaction name="actionLayer_Arrangement_Horizontal"/>
<addaction name="actionLayer_Arrangement_Vertical"/>
</widget>
<addaction name="menuLayer_Arrangement"/>
<addaction name="actionLayer_Grid"/>
<addaction name="actionMetatile_Grid"/>
<addaction name="actionShow_Tileset_Divider"/>
@ -863,6 +871,22 @@
<string>Secondary...</string>
</property>
</action>
<action name="actionLayer_Arrangement_Horizontal">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Horizontal</string>
</property>
</action>
<action name="actionLayer_Arrangement_Vertical">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Vertical</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -108,6 +108,7 @@ public:
int metatilesZoom;
int tilesetEditorMetatilesZoom;
int tilesetEditorTilesZoom;
Qt::Orientation tilesetEditorLayerOrientation;
bool showPlayerView;
bool showCursorTile;
bool showBorder;

View File

@ -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<QPoint> 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:

View File

@ -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

View File

@ -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<MetatileHistoryItem*> metatileHistory;

View File

@ -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<uint16_t> usedTiles;
bool showUnused = false;
bool showDivider = false;
@ -49,6 +51,7 @@ private:
int externalSelectionHeight;
QList<Tile> externalSelectedTiles;
QList<int> externalSelectedPos;
QPoint prevCellPos = QPoint(-1,-1);
Tileset *primaryTileset;
Tileset *secondaryTileset;

View File

@ -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<int>(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<QString, QString> 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");

View File

@ -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);
}

View File

@ -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);

View File

@ -3,8 +3,8 @@
#include "imageproviders.h"
#include <QPainter>
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<QPoint> 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;

View File

@ -1,11 +1,6 @@
#include "selectablepixmapitem.h"
#include <QPainter>
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<int>(pos.x()) / this->cellWidth,
static_cast<int>(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<int>(pos.x()), pixmap().width() - 1);
int y = qBound(0, static_cast<int>(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);

View File

@ -62,12 +62,16 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
connect(ui->spinBox_paletteSelector, QOverload<int>::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<QPoint> tileCoords = QList<QPoint>{
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<Tile> 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<Tile> tiles;
QList<int> 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<uint16_t>(tiles[0].tileId));
this->tileSelector->highlight(tiles[0].tileId);
this->redrawTileSelector();
}
this->metatileLayersItem->clearLastModifiedCoords();

View File

@ -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<Tile> 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();