diff --git a/include/core/editcommands.h b/include/core/editcommands.h index ea66b722..40f0cd62 100644 --- a/include/core/editcommands.h +++ b/include/core/editcommands.h @@ -3,9 +3,11 @@ #define EDITCOMMANDS_H #include "blockdata.h" +#include "mapconnection.h" #include #include +#include class MapPixmapItem; class Map; @@ -31,6 +33,11 @@ enum CommandId { ID_EventDelete, ID_EventDuplicate, ID_EventPaste, + ID_MapConnectionMove, + ID_MapConnectionChangeDirection, + ID_MapConnectionChangeMap, + ID_MapConnectionAdd, + ID_MapConnectionRemove, }; #define IDMask_EventType_Object (1 << 8) @@ -379,4 +386,113 @@ private: int newBorderHeight; }; + + +/// Implements a command to commit Map Connectien move actions. +/// Actions are merged into one until the mouse is released when editing by click-and-drag, +/// or when the offset spin box loses focus when editing with the list UI. +class MapConnectionMove : public QUndoCommand { +public: + MapConnectionMove(MapConnection *connection, int newOffset, unsigned actionId, + QUndoCommand *parent = nullptr); + + void undo() override; + void redo() override; + + bool mergeWith(const QUndoCommand *command) override; + int id() const override { return CommandId::ID_MapConnectionMove; } + +private: + MapConnection *connection; + int newOffset; + int oldOffset; + bool mirrored; + unsigned actionId; +}; + + + +/// Implements a command to commit changes to a Map Connectien's 'direction' field. +class MapConnectionChangeDirection : public QUndoCommand { +public: + MapConnectionChangeDirection(MapConnection *connection, QString newDirection, + QUndoCommand *parent = nullptr); + + void undo() override; + void redo() override; + + int id() const override { return CommandId::ID_MapConnectionChangeDirection; } + +private: + QPointer connection; + QString newDirection; + QString oldDirection; + int oldOffset; + int newOffset; + bool mirrored; +}; + + + +/// Implements a command to commit changes to a Map Connectien's 'map' field. +class MapConnectionChangeMap : public QUndoCommand { +public: + MapConnectionChangeMap(MapConnection *connection, QString newMapName, + QUndoCommand *parent = nullptr); + + void undo() override; + void redo() override; + + int id() const override { return CommandId::ID_MapConnectionChangeMap; } + +private: + QPointer connection; + QString newMapName; + QString oldMapName; + int oldOffset; + int newOffset; + bool mirrored; +}; + + + +/// Implements a command to commit adding a Map Connection to a map. +class MapConnectionAdd : public QUndoCommand { +public: + MapConnectionAdd(Map *map, MapConnection *connection, + QUndoCommand *parent = nullptr); + + void undo() override; + void redo() override; + + int id() const override { return CommandId::ID_MapConnectionAdd; } + +private: + Map *map = nullptr; + Map *mirrorMap = nullptr; + QPointer connection = nullptr; + QPointer mirror = nullptr; +}; + + + +/// Implements a command to commit removing a Map Connection from a map. +class MapConnectionRemove : public QUndoCommand { +public: + MapConnectionRemove(Map *map, MapConnection *connection, + QUndoCommand *parent = nullptr); + + void undo() override; + void redo() override; + + int id() const override { return CommandId::ID_MapConnectionRemove; } + +private: + Map *map = nullptr; + Map *mirrorMap = nullptr; + QPointer connection = nullptr; + QPointer mirror = nullptr; +}; + + #endif // EDITCOMMANDS_H diff --git a/include/core/map.h b/include/core/map.h index 9f039a6f..27020236 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -99,10 +99,12 @@ public: void addEvent(Event *); void deleteConnections(); QList getConnections() const; - void removeConnection(MapConnection *); - void addConnection(MapConnection *); + bool takeConnection(MapConnection *); + bool removeConnection(MapConnection *); + bool addConnection(MapConnection *); void loadConnection(MapConnection *); - QPixmap renderConnection(const QString &, MapLayout *); + QRect getConnectionRect(const QString &direction, MapLayout *fromLayout = nullptr); + QPixmap renderConnection(const QString &direction, MapLayout *fromLayout = nullptr); QPixmap renderBorder(bool ignoreCache = false); void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false); @@ -126,12 +128,15 @@ public: QUndoStack editHistory; void modify(); void clean(); + void pruneEditHistory(); private: void setNewDimensionsBlockdata(int newWidth, int newHeight); void setNewBorderDimensionsBlockdata(int newWidth, int newHeight); + // MapConnections in 'ownedConnections' but not 'connections' persist in the edit history. QList connections; + QSet ownedConnections; signals: void modified(); diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h index 956574dc..6ffa85b7 100644 --- a/include/core/mapconnection.h +++ b/include/core/mapconnection.h @@ -29,7 +29,6 @@ public: int offset() const { return m_offset; } void setOffset(int offset, bool mirror = true); - bool isMirror(const MapConnection*); MapConnection* findMirror(); MapConnection* createMirror(); @@ -43,6 +42,7 @@ public: static bool isVertical(const QString &direction); static bool isDiving(const QString &direction); static QString oppositeDirection(const QString &direction) { return oppositeDirections.value(direction, direction); } + static bool areMirrored(const MapConnection*, const MapConnection*); private: Map* m_parentMap; diff --git a/include/editor.h b/include/editor.h index 46432347..3b2c9ae6 100644 --- a/include/editor.h +++ b/include/editor.h @@ -79,6 +79,7 @@ public: void setConnectionsVisibility(bool visible); void updateDivingMapsVisibility(); void displayDivingConnections(); + void renderDivingConnections(); void addConnection(MapConnection* connection); void removeConnection(MapConnection* connection); void removeSelectedConnection(); @@ -111,7 +112,6 @@ public: QPointer scene = nullptr; QGraphicsPixmapItem *current_view = nullptr; QPointer map_item = nullptr; - QPointer selected_connection_item = nullptr; QList> connection_items; QMap> diving_map_items; QGraphicsPathItem *connection_mask = nullptr; @@ -134,6 +134,8 @@ public: QPointer movement_permissions_selector_item = nullptr; QList *selected_events = nullptr; + QPointer selected_connection_item = nullptr; + QPointer connection_to_select = nullptr; QString map_edit_mode = "paint"; QString obj_edit_mode = "select"; @@ -183,10 +185,9 @@ private: void clearMapGrid(); void clearWildMonTables(); void updateBorderVisibility(); - QPoint calculateConnectionPosition(MapConnection *connection, const QPixmap &pixmap); + QPoint getConnectionOrigin(MapConnection *connection); void removeConnectionPixmap(MapConnection* connection); void updateConnectionPixmap(ConnectionPixmapItem* connectionItem); - void updateConnectionPixmapPos(ConnectionPixmapItem* connectionItem); void displayConnection(MapConnection* connection); void displayDivingConnection(MapConnection* connection); void setDivingMapName(QString mapName, QString direction); diff --git a/include/ui/connectionpixmapitem.h b/include/ui/connectionpixmapitem.h index 969fa6c1..62eda6fe 100644 --- a/include/ui/connectionpixmapitem.h +++ b/include/ui/connectionpixmapitem.h @@ -9,36 +9,37 @@ class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: - ConnectionPixmapItem(QPixmap pixmap, MapConnection* connection, int x, int y) - : QGraphicsPixmapItem(pixmap) - { - this->basePixmap = pixmap; - this->connection = connection; - setFlag(ItemIsMovable); - setFlag(ItemSendsGeometryChanges); - this->initialX = x; - this->initialY = y; - this->initialOffset = connection->offset(); - this->setPos(x, y); - } - QPixmap basePixmap; - QPointer connection; - int initialX; - int initialY; - int initialOffset; + ConnectionPixmapItem(MapConnection* connection, int originX, int originY); + ConnectionPixmapItem(MapConnection* connection, QPoint origin); + + const QPointer connection; + + void setOrigin(int x, int y); + void setOrigin(QPoint pos); void setEditable(bool editable); bool getEditable(); + void setSelected(bool selected); - void render(); + + void updatePos(); + void render(bool ignoreCache = false); private: + QPixmap basePixmap; + qreal originX; + qreal originY; bool selected = false; + unsigned actionId = 0; + + static const int mWidth = 16; + static const int mHeight = 16; protected: - QVariant itemChange(GraphicsItemChange change, const QVariant &value); - void mousePressEvent(QGraphicsSceneMouseEvent*); - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*); + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void mousePressEvent(QGraphicsSceneMouseEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override; signals: void connectionItemDoubleClicked(MapConnection*); diff --git a/include/ui/connectionslistitem.h b/include/ui/connectionslistitem.h index 9cac5689..87051c37 100644 --- a/include/ui/connectionslistitem.h +++ b/include/ui/connectionslistitem.h @@ -2,6 +2,7 @@ #define CONNECTIONSLISTITEM_H #include "mapconnection.h" +#include "map.h" #include #include @@ -30,11 +31,13 @@ public: private: Ui::ConnectionsListItem *ui; + Map *map; bool isSelected = false; + unsigned actionId = 0; protected: - void mousePressEvent(QMouseEvent *); - void mouseDoubleClickEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent*) override; + void mouseDoubleClickEvent(QMouseEvent*) override; signals: void removed(MapConnection*); diff --git a/include/ui/divingmappixmapitem.h b/include/ui/divingmappixmapitem.h index 7596aeaf..7eceba07 100644 --- a/include/ui/divingmappixmapitem.h +++ b/include/ui/divingmappixmapitem.h @@ -5,30 +5,26 @@ #include #include +#include class DivingMapPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: - DivingMapPixmapItem(MapConnection* connection) - : QGraphicsPixmapItem(getBasePixmap(connection)) - { - m_connection = connection; + DivingMapPixmapItem(MapConnection *connection, QComboBox *combo); + ~DivingMapPixmapItem(); - // Update pixmap if the connected map is swapped. - connect(m_connection, &MapConnection::targetMapNameChanged, this, &DivingMapPixmapItem::updatePixmap); - } MapConnection* connection() const { return m_connection; } + void updatePixmap(); private: QPointer m_connection; + QPointer m_combo; - static QPixmap getBasePixmap(MapConnection* connection) { - // If the map is connected to itself then rendering is pointless. - if (!connection || connection->targetMapName() == connection->parentMapName()) - return QPixmap(); - return connection->getPixmap(); - } - void updatePixmap() { setPixmap(getBasePixmap(m_connection)); } + void setComboText(const QString &text); + static QPixmap getBasePixmap(MapConnection* connection); + +private slots: + void onTargetMapChanged(); }; #endif // DIVINGMAPPIXMAPITEM_H diff --git a/include/ui/newmapconnectiondialog.h b/include/ui/newmapconnectiondialog.h index 3b37c454..2e0ba28e 100644 --- a/include/ui/newmapconnectiondialog.h +++ b/include/ui/newmapconnectiondialog.h @@ -17,10 +17,11 @@ public: explicit NewMapConnectionDialog(QWidget *parent, Map* map, const QStringList &mapNames); ~NewMapConnectionDialog(); - MapConnection *result; - virtual void accept() override; +signals: + void accepted(MapConnection *result); + private: Ui::NewMapConnectionDialog *ui; }; diff --git a/porymap.pro b/porymap.pro index e5cad6dd..744edaef 100644 --- a/porymap.pro +++ b/porymap.pro @@ -50,6 +50,7 @@ SOURCES += src/core/block.cpp \ src/ui/connectionslistitem.cpp \ src/ui/customscriptseditor.cpp \ src/ui/customscriptslistitem.cpp \ + src/ui/divingmappixmapitem.cpp \ src/ui/draggablepixmapitem.cpp \ src/ui/bordermetatilespixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \ diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp index 394c51cb..640cb43f 100644 --- a/src/core/editcommands.cpp +++ b/src/core/editcommands.cpp @@ -569,3 +569,205 @@ void ScriptEditMap::undo() { QUndoCommand::undo(); } + +/****************************************************************************** + ************************************************************************ + ******************************************************************************/ + +MapConnectionMove::MapConnectionMove(MapConnection *connection, int newOffset, unsigned actionId, + QUndoCommand *parent) : QUndoCommand(parent) { + setText("Move Map Connection"); + + this->connection = connection; + this->oldOffset = connection->offset(); + this->newOffset = newOffset; + + this->mirrored = porymapConfig.mirrorConnectingMaps; + + this->actionId = actionId; +} + +void MapConnectionMove::redo() { + QUndoCommand::redo(); + if (this->connection) + this->connection->setOffset(this->newOffset, this->mirrored); +} + +void MapConnectionMove::undo() { + if (this->connection) + this->connection->setOffset(this->oldOffset, this->mirrored); + QUndoCommand::undo(); +} + +bool MapConnectionMove::mergeWith(const QUndoCommand *command) { + if (this->id() != command->id()) + return false; + + const MapConnectionMove *other = static_cast(command); + if (this->connection != other->connection) + return false; + if (this->actionId != other->actionId) + return false; + + this->newOffset = other->newOffset; + return true; +} + +/****************************************************************************** + ************************************************************************ + ******************************************************************************/ + +MapConnectionChangeDirection::MapConnectionChangeDirection(MapConnection *connection, QString newDirection, + QUndoCommand *parent) : QUndoCommand(parent) { + setText("Change Map Connection Direction"); + + this->connection = connection; + + this->oldDirection = connection->direction(); + this->newDirection = newDirection; + + this->oldOffset = connection->offset(); + + // If the direction changes between vertical/horizontal then the old offset may not make sense, so we reset it. + if (MapConnection::isHorizontal(this->oldDirection) != MapConnection::isHorizontal(this->newDirection) + || MapConnection::isVertical(this->oldDirection) != MapConnection::isVertical(this->newDirection)) { + this->newOffset = 0; + } else { + this->newOffset = oldOffset; + } + + this->mirrored = porymapConfig.mirrorConnectingMaps; +} + +void MapConnectionChangeDirection::redo() { + QUndoCommand::redo(); + if (this->connection) { + this->connection->setDirection(this->newDirection, this->mirrored); + this->connection->setOffset(this->newOffset, this->mirrored); + } +} + +void MapConnectionChangeDirection::undo() { + if (this->connection) { + this->connection->setDirection(this->oldDirection, this->mirrored); + this->connection->setOffset(this->oldOffset, this->mirrored); + } + QUndoCommand::undo(); +} + +/****************************************************************************** + ************************************************************************ + ******************************************************************************/ + +MapConnectionChangeMap::MapConnectionChangeMap(MapConnection *connection, QString newMapName, + QUndoCommand *parent) : QUndoCommand(parent) { + setText("Change Map Connection Map"); + + this->connection = connection; + + this->oldMapName = connection->targetMapName(); + this->newMapName = newMapName; + + this->oldOffset = connection->offset(); + this->newOffset = 0; // The old offset may not make sense, so we reset it + + this->mirrored = porymapConfig.mirrorConnectingMaps; +} + +void MapConnectionChangeMap::redo() { + QUndoCommand::redo(); + if (this->connection) { + this->connection->setTargetMapName(this->newMapName, this->mirrored); + this->connection->setOffset(this->newOffset, this->mirrored); + } +} + +void MapConnectionChangeMap::undo() { + if (this->connection) { + this->connection->setTargetMapName(this->oldMapName, this->mirrored); + this->connection->setOffset(this->oldOffset, this->mirrored); + } + QUndoCommand::undo(); +} + +/****************************************************************************** + ************************************************************************ + ******************************************************************************/ + +MapConnectionAdd::MapConnectionAdd(Map *map, MapConnection *connection, + QUndoCommand *parent) : QUndoCommand(parent) { + setText("Add Map Connection"); + + this->map = map; + this->connection = connection; + + // Set this now because it's needed to create a mirror below. + // It would otherwise be set by Map::addConnection. + this->connection->setParentMap(this->map, false); + + if (porymapConfig.mirrorConnectingMaps) { + this->mirror = this->connection->createMirror(); + this->mirrorMap = this->connection->targetMap(); + } +} + +void MapConnectionAdd::redo() { + QUndoCommand::redo(); + + this->map->addConnection(this->connection); + if (this->mirrorMap) + this->mirrorMap->addConnection(this->mirror); +} + +void MapConnectionAdd::undo() { + if (this->mirrorMap) { + // We can't guarantee that the mirror we created earlier is still our connection's + // mirror because there is no strict source->mirror pairing for map connections + // (a different identical map connection can take its place during any mirrored change). + if (!MapConnection::areMirrored(this->connection, this->mirror)) + this->mirror = this->connection->findMirror(); + + this->mirrorMap->removeConnection(this->mirror); + } + this->map->removeConnection(this->connection); + + QUndoCommand::undo(); +} + +/****************************************************************************** + ************************************************************************ + ******************************************************************************/ + +MapConnectionRemove::MapConnectionRemove(Map *map, MapConnection *connection, + QUndoCommand *parent) : QUndoCommand(parent) { + setText("Remove Map Connection"); + + this->map = map; + this->connection = connection; + + if (porymapConfig.mirrorConnectingMaps) { + this->mirror = this->connection->findMirror(); + this->mirrorMap = this->connection->targetMap(); + } +} + +void MapConnectionRemove::redo() { + QUndoCommand::redo(); + + if (this->mirrorMap) { + // See comment in MapConnectionAdd::undo + if (!MapConnection::areMirrored(this->connection, this->mirror)) + this->mirror = this->connection->findMirror(); + + this->mirrorMap->removeConnection(this->mirror); + } + this->map->removeConnection(this->connection); +} + +void MapConnectionRemove::undo() { + this->map->addConnection(this->connection); + if (this->mirrorMap) + this->mirrorMap->addConnection(this->mirror); + + QUndoCommand::undo(); +} diff --git a/src/core/map.cpp b/src/core/map.cpp index ed011ec5..91e196fe 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -215,11 +215,13 @@ QPixmap Map::renderBorder(bool ignoreCache) { return layout->border_pixmap; } -QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) { - // Cardinal connections are rendered within the bounds of the border draw distance, - // and we need to render the nearest segment of the map. - // Dive/Emerge maps are rendered normally, but within the bounds of their parent map. - int x = 0, y = 0, w = getWidth(), h = getHeight(); +// Get the portion of the map that can be rendered when rendered as a map connection. +// Cardinal connections render the nearest segment of their map and within the bounds of the border draw distance, +// Dive/Emerge connections are rendered normally within the bounds of their parent map. +QRect Map::getConnectionRect(const QString &direction, MapLayout * fromLayout) { + int x = 0, y = 0; + int w = getWidth(), h = getHeight(); + if (direction == "up") { h = qMin(h, BORDER_DISTANCE); y = getHeight() - h; @@ -234,15 +236,26 @@ QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) if (fromLayout) { w = qMin(w, fromLayout->getWidth()); h = qMin(h, fromLayout->getHeight()); - fromLayout = nullptr; // This would be used for palettes later, but we want our own palettes, not the parent's. } } else { // Unknown direction - return QPixmap(); + return QRect(); } + return QRect(x, y, w, h); +} - render(true, fromLayout, QRect(x, y, w, h)); - QImage connection_image = image.copy(x * 16, y * 16, w * 16, h * 16); +QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) { + QRect bounds = getConnectionRect(direction, fromLayout); + if (!bounds.isValid()) + return QPixmap(); + + // 'fromLayout' will be used in 'render' to get the palettes from the parent map. + // Dive/Emerge connections render normally with their own palettes, so we ignore this. + if (MapConnection::isDiving(direction)) + fromLayout = nullptr; + + render(true, fromLayout, bounds); + QImage connection_image = image.copy(bounds.x() * 16, bounds.y() * 16, bounds.width() * 16, bounds.height() * 16); return QPixmap::fromImage(connection_image); } @@ -518,54 +531,62 @@ void Map::addEvent(Event *event) { } void Map::deleteConnections() { - qDeleteAll(connections); - connections.clear(); + qDeleteAll(this->ownedConnections); + this->ownedConnections.clear(); + this->connections.clear(); } QList Map::getConnections() const { return connections; } -void Map::addConnection(MapConnection *connection) { - if (!connection || connections.contains(connection)) - return; +bool Map::addConnection(MapConnection *connection) { + if (!connection || this->connections.contains(connection)) + return false; - // Adding new Dive/Emerge maps replaces an existing one. + // Maps should only have one Dive/Emerge connection at a time. + // (Users can technically have more by editing their data manually, but we will only display one at a time) + // Any additional connections being added (this can happen via mirroring) are tracked for deleting but otherwise ignored. if (MapConnection::isDiving(connection->direction())) { - for (auto connectionToReplace : connections) { - if (connectionToReplace->direction() != connection->direction()) - continue; - if (porymapConfig.mirrorConnectingMaps) { - auto mirror = connectionToReplace->findMirror(); - if (mirror && mirror->parentMap()) - mirror->parentMap()->removeConnection(mirror); - delete mirror; + for (auto i : this->connections) { + if (i->direction() == connection->direction()) { + this->ownedConnections.insert(connection); + connection->setParentMap(this, false); + return true; } - removeConnection(connectionToReplace); - delete connectionToReplace; - break; } } loadConnection(connection); modify(); emit connectionAdded(connection); + return true; } void Map::loadConnection(MapConnection *connection) { if (!connection) return; - connections.append(connection); + this->connections.append(connection); + this->ownedConnections.insert(connection); connection->setParentMap(this, false); } -// Caller takes ownership of connection -void Map::removeConnection(MapConnection *connection) { - if (!connections.removeOne(connection)) - return; - connection->setParentMap(nullptr, false); +// connection should not be deleted here, a pointer to it is allowed to persist in the edit history +bool Map::removeConnection(MapConnection *connection) { + if (!this->connections.removeOne(connection)) + return false; modify(); emit connectionRemoved(connection); + return true; +} + +// Same as Map::removeConnection but caller takes ownership of connection. +bool Map::takeConnection(MapConnection *connection) { + if (!this->removeConnection(connection)) + return false; + connection->setParentMap(nullptr, false); + this->ownedConnections.remove(connection); + return true; } void Map::modify() { @@ -580,6 +601,27 @@ bool Map::hasUnsavedChanges() { return !editHistory.isClean() || hasUnsavedDataChanges || !isPersistedToFile; } +void Map::pruneEditHistory() { + // Edit history for map connections gets messy because edits on other maps can affect the current map. + // To avoid complications we clear MapConnection edit history when the user opens a different map. + // No other edits within a single map depend on MapConnections so they can be pruned safely. + static const QSet mapConnectionIds = { + ID_MapConnectionMove, + ID_MapConnectionChangeDirection, + ID_MapConnectionChangeMap, + ID_MapConnectionAdd, + ID_MapConnectionRemove + }; + for (int i = 0; i < this->editHistory.count(); i++) { + // Qt really doesn't expect editing commands in the stack to be valid (fair). + // A better future design might be to have separate edit histories per map tab, + // and dumping the entire Connections tab history with QUndoStack::clear. + auto command = const_cast(this->editHistory.command(i)); + if (mapConnectionIds.contains(command->id())) + command->setObsolete(true); + } +} + bool Map::isWithinBounds(int x, int y) { return (x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight()); } diff --git a/src/core/mapconnection.cpp b/src/core/mapconnection.cpp index 5fe2e730..022158a0 100644 --- a/src/core/mapconnection.cpp +++ b/src/core/mapconnection.cpp @@ -16,14 +16,14 @@ MapConnection::MapConnection(const QString &targetMapName, const QString &direct m_offset = offset; } -bool MapConnection::isMirror(const MapConnection* other) { - if (!other || !other->m_parentMap) +bool MapConnection::areMirrored(const MapConnection* a, const MapConnection* b) { + if (!a || !b || !a->m_parentMap || !b->m_parentMap) return false; - return parentMapName() == other->m_targetMapName - && m_targetMapName == other->parentMapName() - && m_offset == -other->m_offset - && m_direction == oppositeDirection(other->m_direction); + return a->parentMapName() == b->m_targetMapName + && a->m_targetMapName == b->parentMapName() + && a->m_offset == -b->m_offset + && a->m_direction == oppositeDirection(b->m_direction); } MapConnection* MapConnection::findMirror() { @@ -35,7 +35,7 @@ MapConnection* MapConnection::findMirror() { // Note: There is no strict source -> mirror pairing, i.e. we are not guaranteed // to always get the same MapConnection if there are multiple identical copies. for (auto connection : map->getConnections()) { - if (this != connection && this->isMirror(connection)) + if (this != connection && areMirrored(this, connection)) return connection; } return nullptr; @@ -72,14 +72,14 @@ void MapConnection::setParentMap(Map* map, bool mirror) { if (map == m_parentMap) return; - if (mirror && porymapConfig.mirrorConnectingMaps) { + if (mirror) { auto connection = findMirror(); if (connection) connection->setTargetMapName(map ? map->name : QString(), false); } if (m_parentMap) - m_parentMap->removeConnection(this); + m_parentMap->takeConnection(this); auto before = m_parentMap; m_parentMap = map; @@ -98,7 +98,7 @@ void MapConnection::setTargetMapName(const QString &targetMapName, bool mirror) if (targetMapName == m_targetMapName) return; - if (mirror && porymapConfig.mirrorConnectingMaps) { + if (mirror) { auto connection = findMirror(); if (connection) connection->setParentMap(getMap(targetMapName), false); @@ -114,7 +114,7 @@ void MapConnection::setDirection(const QString &direction, bool mirror) { if (direction == m_direction) return; - if (mirror && porymapConfig.mirrorConnectingMaps) { + if (mirror) { auto connection = findMirror(); if (connection) connection->setDirection(oppositeDirection(direction), false); @@ -130,7 +130,7 @@ void MapConnection::setOffset(int offset, bool mirror) { if (offset == m_offset) return; - if (mirror && porymapConfig.mirrorConnectingMaps) { + if (mirror) { auto connection = findMirror(); if (connection) connection->setOffset(-offset, false); diff --git a/src/editor.cpp b/src/editor.cpp index e2093665..c7acdea0 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -743,9 +743,7 @@ void Editor::displayConnection(MapConnection* connection) { } // Create connection image - QPixmap pixmap = connection->getPixmap(); - QPoint pos = calculateConnectionPosition(connection, pixmap); - ConnectionPixmapItem *pixmapItem = new ConnectionPixmapItem(pixmap, connection, pos.x(), pos.y()); + ConnectionPixmapItem *pixmapItem = new ConnectionPixmapItem(connection, getConnectionOrigin(connection)); pixmapItem->render(); scene->addItem(pixmapItem); maskNonVisibleConnectionTiles(); @@ -756,82 +754,70 @@ void Editor::displayConnection(MapConnection* connection) { // It's possible for a map connection to be displayed repeatedly if it's being // updated by mirroring and the target map is changing to/from the current map. - connection->disconnect(); + QObject::disconnect(connection, &MapConnection::targetMapNameChanged, nullptr, nullptr); + QObject::disconnect(connection, &MapConnection::directionChanged, nullptr, nullptr); + QObject::disconnect(connection, &MapConnection::offsetChanged, nullptr, nullptr); // Double clicking the list item or pixmap opens the connected map connect(listItem, &ConnectionsListItem::doubleClicked, this, &Editor::onMapConnectionDoubleClicked); connect(pixmapItem, &ConnectionPixmapItem::connectionItemDoubleClicked, this, &Editor::onMapConnectionDoubleClicked); // Sync the selection highlight between the list UI and the pixmap - connect(pixmapItem, &ConnectionPixmapItem::selectionChanged, [this, listItem, pixmapItem](bool selected) { + connect(pixmapItem, &ConnectionPixmapItem::selectionChanged, [=](bool selected) { listItem->setSelected(selected); if (selected) setSelectedConnectionItem(pixmapItem); }); - connect(listItem, &ConnectionsListItem::selected, [this, pixmapItem] { + connect(listItem, &ConnectionsListItem::selected, [=] { setSelectedConnectionItem(pixmapItem); }); // Sync edits to 'offset' between the list UI and the pixmap - connect(connection, &MapConnection::offsetChanged, [this, listItem, pixmapItem](int, int) { + connect(connection, &MapConnection::offsetChanged, [=](int, int) { listItem->updateUI(); - updateConnectionPixmapPos(pixmapItem); + pixmapItem->updatePos(); + maskNonVisibleConnectionTiles(); }); // Sync edits to 'direction' between the list UI and the pixmap - connect(connection, &MapConnection::directionChanged, [this, listItem, pixmapItem](QString before, QString after) { - if (MapConnection::isHorizontal(before) != MapConnection::isHorizontal(after) - || MapConnection::isVertical(before) != MapConnection::isVertical(after)) { - // If the direction has changed between vertical/horizontal then the old offset may not make sense, so we reset it - pixmapItem->connection->setOffset(0); - } + connect(connection, &MapConnection::directionChanged, [=](QString, QString) { listItem->updateUI(); updateConnectionPixmap(pixmapItem); }); // Sync edits to 'map' between the list UI and the pixmap - connect(connection, &MapConnection::targetMapNameChanged, [this, listItem, pixmapItem](QString, QString) { - // The old offset may not make sense, so we reset it - pixmapItem->connection->setOffset(0); + connect(connection, &MapConnection::targetMapNameChanged, [=](QString, QString) { listItem->updateUI(); updateConnectionPixmap(pixmapItem); }); - // User manually deleting connection via the remove button - connect(listItem, &ConnectionsListItem::removed, [this](MapConnection* connection) { removeConnection(connection); }); - // When the pixmap is deleted, remove its associated list item connect(pixmapItem, &ConnectionPixmapItem::destroyed, listItem, &ConnectionsListItem::deleteLater); connection_items.append(pixmapItem); + + // If this was a recent addition from the user we should select it. + // We intentionally exclude connections added programmatically, e.g. by mirroring. + if (connection_to_select == connection) { + connection_to_select = nullptr; + setSelectedConnectionItem(pixmapItem); + } } -void Editor::addConnection(MapConnection * connection) { +void Editor::addConnection(MapConnection* connection) { if (!connection) return; - this->map->addConnection(connection); - if (porymapConfig.mirrorConnectingMaps) - connection->createMirror(); + // Mark this connection to be selected once its display elements have been created. + // It's possible this is a Dive/Emerge connection, but that's ok (no selection will occur). + connection_to_select = connection; - // TODO: Edit history + this->map->editHistory.push(new MapConnectionAdd(this->map, connection)); } void Editor::removeConnection(MapConnection* connection) { if (!connection) return; - - if (porymapConfig.mirrorConnectingMaps) { - auto mirror = connection->findMirror(); - if (mirror && mirror->parentMap()) - mirror->parentMap()->removeConnection(mirror); - delete mirror; - } - - if (connection->parentMap()) - connection->parentMap()->removeConnection(connection); - delete connection; - - // TODO: Edit history + this->map->editHistory.push(new MapConnectionRemove(this->map, connection)); } void Editor::removeSelectedConnection() { @@ -887,10 +873,6 @@ void Editor::displayDivingConnection(MapConnection* connection) { if (!MapConnection::isDiving(direction)) return; - // Save some rendering time if it won't be displayed - if (!porymapConfig.showDiveEmergeMaps) - return; - // Note: We only support editing 1 Dive and Emerge connection per map. // In a vanilla game only the first Dive/Emerge connection is considered, so allowing // users to have multiple is likely to lead to confusion. In case users have changed @@ -898,15 +880,18 @@ void Editor::displayDivingConnection(MapConnection* connection) { if (diving_map_items.value(direction)) return; - // Create map image - auto item = new DivingMapPixmapItem(connection); + // Create map display + auto comboBox = (direction == "dive") ? ui->comboBox_DiveMap : ui->comboBox_EmergeMap; + auto item = new DivingMapPixmapItem(connection, comboBox); scene->addItem(item); diving_map_items.insert(direction, item); - // Display map name in combo box - auto comboBox = (direction == "dive") ? ui->comboBox_DiveMap : ui->comboBox_EmergeMap; - const QSignalBlocker blocker(comboBox); - comboBox->setCurrentText(connection->targetMapName()); + updateDivingMapsVisibility(); +} + +void Editor::renderDivingConnections() { + for (auto item : diving_map_items.values()) + item->updatePixmap(); } void Editor::removeDivingMapPixmap(MapConnection *connection) { @@ -917,7 +902,7 @@ void Editor::removeDivingMapPixmap(MapConnection *connection) { if (!diving_map_items.contains(direction)) return; - // If the diving map being displayed is different than the one being removed we don't need to do anything. + // If the diving map being removed is different than the one that's currently displayed we don't need to do anything. if (diving_map_items.value(direction)->connection() != connection) return; @@ -927,11 +912,6 @@ void Editor::removeDivingMapPixmap(MapConnection *connection) { pixmapItem->scene()->removeItem(pixmapItem); delete pixmapItem; - // Clear map name from combo box - auto comboBox = (direction == "dive") ? ui->comboBox_DiveMap : ui->comboBox_EmergeMap; - const QSignalBlocker blocker(comboBox); - comboBox->setCurrentText(""); - // Reveal any previously-hidden connection (because we only ever display one diving map of each type). // Note: When this occurs as a result of the user clicking the 'X' clear button it seems the QComboBox // doesn't expect the line edit to be immediately repopulated, and the 'X' doesn't reappear. @@ -942,6 +922,7 @@ void Editor::removeDivingMapPixmap(MapConnection *connection) { break; } } + updateDivingMapsVisibility(); } void Editor::updateDiveMap(QString mapName) { @@ -953,27 +934,24 @@ void Editor::updateEmergeMap(QString mapName) { } void Editor::setDivingMapName(QString mapName, QString direction) { - // Only the first Dive/Emerge map (if present) is considered, as in-game. - MapConnection* connection = nullptr; - for (auto i : map->getConnections()) { - if (i->direction() == direction) { - connection = i; - break; - } - } + auto pixmapItem = diving_map_items.value(direction); + MapConnection *connection = pixmapItem ? pixmapItem->connection() : nullptr; + // TODO: Test edit history if (connection) { + if (mapName == connection->targetMapName()) + return; // No change + // Update existing connection if (mapName.isEmpty()) { removeConnection(connection); } else { - connection->setTargetMapName(mapName); + map->editHistory.push(new MapConnectionChangeMap(connection, mapName)); } } else if (!mapName.isEmpty()) { // Create new connection addConnection(new MapConnection(mapName, direction)); } - updateDivingMapsVisibility(); } void Editor::updateDivingMapsVisibility() { @@ -994,58 +972,36 @@ void Editor::updateDivingMapsVisibility() { } } -QPoint Editor::calculateConnectionPosition(MapConnection *connection, const QPixmap &pixmap) { +// Get the 'origin' point for the connection's pixmap, i.e. where it should be positioned in the editor when connection->offset() == 0. +// This differs depending on the connection's direction and the dimensions of its target map or parent map. +QPoint Editor::getConnectionOrigin(MapConnection *connection) { + if (!connection) + return QPoint(0, 0); + + Map *parentMap = connection->parentMap(); + Map *targetMap = connection->targetMap(); const QString direction = connection->direction(); - int offset = connection->offset(); int x = 0, y = 0; - const int mWidth = 16, mHeight = 16; - if (direction == "up") { - x = offset * mWidth; - y = -pixmap.height(); + + if (direction == "right") { + if (parentMap) x = parentMap->getWidth(); } else if (direction == "down") { - x = offset * mWidth; - y = map->getHeight() * mHeight; + if (parentMap) y = parentMap->getHeight(); } else if (direction == "left") { - x = -pixmap.width(); - y = offset * mHeight; - } else if (direction == "right") { - x = map->getWidth() * mWidth; - y = offset * mHeight; + if (targetMap) x = -targetMap->getConnectionRect(direction).width(); + } else if (direction == "up") { + if (targetMap) y = -targetMap->getConnectionRect(direction).height(); } - return QPoint(x, y); + return QPoint(x * 16, y * 16); } void Editor::updateConnectionPixmap(ConnectionPixmapItem* pixmapItem) { - if (!pixmapItem || !pixmapItem->connection) + if (!pixmapItem) return; - pixmapItem->initialOffset = pixmapItem->connection->offset(); - pixmapItem->basePixmap = pixmapItem->connection->getPixmap(); - QPoint pos = calculateConnectionPosition(pixmapItem->connection, pixmapItem->basePixmap); + pixmapItem->setOrigin(getConnectionOrigin(pixmapItem->connection)); + pixmapItem->render(true); // Full render to reflect map changes - const QSignalBlocker blocker(pixmapItem); - pixmapItem->setPixmap(pixmapItem->basePixmap); - pixmapItem->initialX = pos.x(); - pixmapItem->initialY = pos.y(); - pixmapItem->setPos(pos); - pixmapItem->render(); - - maskNonVisibleConnectionTiles(); -} - -void Editor::updateConnectionPixmapPos(ConnectionPixmapItem* pixmapItem) { - if (!pixmapItem || !pixmapItem->connection) - return; - - const QSignalBlocker blocker(pixmapItem); - MapConnection *connection = pixmapItem->connection; - if (MapConnection::isVertical(connection->direction())) { - qreal x = pixmapItem->initialX + (connection->offset() - pixmapItem->initialOffset) * 16; - if (x != pixmapItem->x()) pixmapItem->setX(x); - } else if (MapConnection::isHorizontal(connection->direction())) { - qreal y = pixmapItem->initialY + (connection->offset() - pixmapItem->initialOffset) * 16; - if (y != pixmapItem->y()) pixmapItem->setY(y); - } maskNonVisibleConnectionTiles(); } @@ -1257,9 +1213,14 @@ bool Editor::setMap(QString map_name) { // disconnect previous map's signals so they are not firing // multiple times if set again in the future if (map) { + map->pruneEditHistory(); map->disconnect(this); - for (auto connection : map->getConnections()) - connection->disconnect(); + for (auto connection : map->getConnections()) { + // Disconnect signals used by the display + QObject::disconnect(connection, &MapConnection::targetMapNameChanged, nullptr, nullptr); + QObject::disconnect(connection, &MapConnection::directionChanged, nullptr, nullptr); + QObject::disconnect(connection, &MapConnection::offsetChanged, nullptr, nullptr); + } } if (project) { @@ -1777,19 +1738,20 @@ void Editor::clearMapConnections() { } diving_map_items.clear(); + // Reset to single opacity slider + ui->stackedWidget_DiveMapOpacity->setCurrentIndex(1); + selected_connection_item = nullptr; } void Editor::displayMapConnections() { clearMapConnections(); - for (MapConnection *connection : map->getConnections()) + for (auto connection : map->getConnections()) displayConnection(connection); if (!connection_items.isEmpty()) setSelectedConnectionItem(connection_items.first()); - - updateDivingMapsVisibility(); } void Editor::clearConnectionMask() { @@ -1859,16 +1821,13 @@ void Editor::updateMapBorder() { } void Editor::updateMapConnections() { - for (auto item : connection_items) { - if (!item->connection) - continue; - item->basePixmap = item->connection->getPixmap(); - item->render(); - } + for (auto item : connection_items) + item->render(true); maskNonVisibleConnectionTiles(); } +// TODO: Check first condition, move to Map int Editor::getBorderDrawDistance(int dimension) { // Draw sufficient border blocks to fill the player's view (BORDER_DISTANCE) if (dimension >= BORDER_DISTANCE) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0f1b83df..e95b3bb0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2306,7 +2306,7 @@ void MainWindow::setDivingMapsVisible(bool visible) { if (visible) { // We skip rendering diving maps if this setting is not enabled, // so when we enable it we need to make sure they've rendered. - this->editor->displayDivingConnections(); + this->editor->renderDivingConnections(); } this->editor->updateDivingMapsVisibility(); } @@ -2662,14 +2662,12 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) { } void MainWindow::on_pushButton_AddConnection_clicked() { - if (!this->editor || !this->editor->map) + if (!this->editor || !this->editor->map || !this->editor->project) return; auto dialog = new NewMapConnectionDialog(this, this->editor->map, this->editor->project->mapNames); - if (dialog->exec() == QDialog::Accepted) { - this->editor->addConnection(dialog->result); - this->editor->setSelectedConnection(dialog->result); - } + connect(dialog, &NewMapConnectionDialog::accepted, this->editor, &Editor::addConnection); + dialog->exec(); } void MainWindow::on_pushButton_NewWildMonGroup_clicked() { diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp index c98d3ba7..f8412012 100644 --- a/src/ui/connectionpixmapitem.cpp +++ b/src/ui/connectionpixmapitem.cpp @@ -1,8 +1,27 @@ #include "connectionpixmapitem.h" +#include "editcommands.h" +#include "map.h" #include -void ConnectionPixmapItem::render() { +ConnectionPixmapItem::ConnectionPixmapItem(MapConnection* connection, int x, int y) + : QGraphicsPixmapItem(connection->getPixmap()), + connection(connection) +{ + this->setEditable(true); + this->basePixmap = pixmap(); + this->setOrigin(x, y); +} + +ConnectionPixmapItem::ConnectionPixmapItem(MapConnection* connection, QPoint pos) + : ConnectionPixmapItem(connection, pos.x(), pos.y()) +{} + +// Render additional visual effects on top of the base map image. +void ConnectionPixmapItem::render(bool ignoreCache) { + if (ignoreCache) + this->basePixmap = this->connection->getPixmap(); + QPixmap pixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->basePixmap.height()); this->setZValue(-1); @@ -31,27 +50,23 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari if (change == ItemPositionChange) { QPointF newPos = value.toPointF(); - qreal x, y; - int newOffset = this->initialOffset; + qreal x = this->originX; + qreal y = this->originY; + int newOffset = this->connection->offset(); + + // Restrict movement to the metatile grid and perpendicular to the connection direction. if (MapConnection::isVertical(this->connection->direction())) { - x = round(newPos.x() / 16) * 16; - newOffset += (x - initialX) / 16; - x = newOffset * 16; - } - else { - x = this->initialX; + x = (round(newPos.x() / this->mWidth) * this->mWidth) - this->originX; + newOffset = x / this->mWidth; + } else if (MapConnection::isHorizontal(this->connection->direction())) { + y = (round(newPos.y() / this->mHeight) * this->mHeight) - this->originY; + newOffset = y / this->mHeight; } - if (MapConnection::isHorizontal(this->connection->direction())) { - y = round(newPos.y() / 16) * 16; - newOffset += (y - this->initialY) / 16; - y = newOffset * 16; - } - else { - y = this->initialY; - } + // This is convoluted because of how our edit history works; this would otherwise just be 'this->connection->setOffset(newOffset);' + if (this->connection->parentMap() && newOffset != this->connection->offset()) + this->connection->parentMap()->editHistory.push(new MapConnectionMove(this->connection, newOffset, this->actionId)); - this->connection->setOffset(newOffset); return QPointF(x, y); } else { @@ -59,6 +74,32 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari } } +// If connection->offset changed externally we call this to correct our position. +void ConnectionPixmapItem::updatePos() { + const QSignalBlocker blocker(this); + + qreal x = this->originX; + qreal y = this->originY; + + if (MapConnection::isVertical(this->connection->direction())) { + x += this->connection->offset() * this->mWidth; + } else if (MapConnection::isHorizontal(this->connection->direction())) { + y += this->connection->offset() * this->mHeight; + } + + this->setPos(x, y); +} + +// Set the pixmap's external origin point, i.e. the pixmap's position when connection->offset == 0 +void ConnectionPixmapItem::setOrigin(int x, int y) { + this->originX = x; + this->originY = y; + updatePos(); +} +void ConnectionPixmapItem::setOrigin(QPoint pos) { + this->setOrigin(pos.x(), pos.y()); +} + void ConnectionPixmapItem::setEditable(bool editable) { setFlag(ItemIsMovable, editable); setFlag(ItemSendsGeometryChanges, editable); @@ -82,6 +123,11 @@ void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) { this->setSelected(true); } +void ConnectionPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + this->actionId++; // Distinguish between move actions for the edit history + QGraphicsPixmapItem::mouseReleaseEvent(event); +} + void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { emit connectionItemDoubleClicked(this->connection); } diff --git a/src/ui/connectionslistitem.cpp b/src/ui/connectionslistitem.cpp index 049bb17d..eb7f94a6 100644 --- a/src/ui/connectionslistitem.cpp +++ b/src/ui/connectionslistitem.cpp @@ -1,5 +1,9 @@ #include "connectionslistitem.h" #include "ui_connectionslistitem.h" +#include "editcommands.h" +#include "map.h" + +#include ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connection, const QStringList &mapNames) : QFrame(parent), @@ -18,11 +22,23 @@ ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connec ui->comboBox_Map->setMinimumContentsLength(6); ui->comboBox_Map->addItems(mapNames); ui->comboBox_Map->setFocusedScrollingEnabled(false); // Scrolling could cause rapid changes to many different maps + ui->comboBox_Map->setInsertPolicy(QComboBox::NoInsert); ui->spinBox_Offset->setMinimum(INT_MIN); ui->spinBox_Offset->setMaximum(INT_MAX); + // Invalid map names are not considered a change. If editing finishes with an invalid name, restore the previous name. + connect(ui->comboBox_Map->lineEdit(), &QLineEdit::editingFinished, [this] { + const QSignalBlocker blocker(ui->comboBox_Map); + if (ui->comboBox_Map->findText(ui->comboBox_Map->currentText()) < 0) + ui->comboBox_Map->setTextItem(this->connection->targetMapName()); + }); + + // Distinguish between move actions for the edit history + connect(ui->spinBox_Offset, &QSpinBox::editingFinished, [this] { this->actionId++; }); + this->connection = connection; + this->map = connection->parentMap(); this->updateUI(); } @@ -32,6 +48,9 @@ ConnectionsListItem::~ConnectionsListItem() } void ConnectionsListItem::updateUI() { + if (!this->connection) + return; + const QSignalBlocker blocker1(ui->comboBox_Direction); const QSignalBlocker blocker2(ui->comboBox_Map); const QSignalBlocker blocker3(ui->spinBox_Offset); @@ -56,26 +75,30 @@ void ConnectionsListItem::mousePressEvent(QMouseEvent *) { this->setSelected(true); } +// TODO: This happens by accident a lot. Convert to button? void ConnectionsListItem::mouseDoubleClickEvent(QMouseEvent *) { emit doubleClicked(this->connection); } void ConnectionsListItem::on_comboBox_Direction_currentTextChanged(const QString &direction) { this->setSelected(true); - this->connection->setDirection(direction); + if (this->map) + this->map->editHistory.push(new MapConnectionChangeDirection(this->connection, direction)); } void ConnectionsListItem::on_comboBox_Map_currentTextChanged(const QString &mapName) { this->setSelected(true); - if (ui->comboBox_Map->findText(mapName) >= 0) - this->connection->setTargetMapName(mapName); + if (this->map && ui->comboBox_Map->findText(mapName) >= 0) + this->map->editHistory.push(new MapConnectionChangeMap(this->connection, mapName)); } void ConnectionsListItem::on_spinBox_Offset_valueChanged(int offset) { this->setSelected(true); - this->connection->setOffset(offset); + if (this->map) + this->map->editHistory.push(new MapConnectionMove(this->connection, offset, this->actionId)); } void ConnectionsListItem::on_button_Delete_clicked() { - emit this->removed(this->connection); + if (this->map) + this->map->editHistory.push(new MapConnectionRemove(this->map, this->connection)); } diff --git a/src/ui/divingmappixmapitem.cpp b/src/ui/divingmappixmapitem.cpp new file mode 100644 index 00000000..70a611fa --- /dev/null +++ b/src/ui/divingmappixmapitem.cpp @@ -0,0 +1,46 @@ +#include "divingmappixmapitem.h" +#include "config.h" + +DivingMapPixmapItem::DivingMapPixmapItem(MapConnection *connection, QComboBox *combo) + : QGraphicsPixmapItem(getBasePixmap(connection)) +{ + m_connection = connection; + m_combo = combo; + + setComboText(connection->targetMapName()); + + // Update display if the connected map is swapped. + connect(m_connection, &MapConnection::targetMapNameChanged, this, &DivingMapPixmapItem::onTargetMapChanged); +} + +DivingMapPixmapItem::~DivingMapPixmapItem() { + // Clear map name from combo box + setComboText(""); +} + +QPixmap DivingMapPixmapItem::getBasePixmap(MapConnection* connection) { + if (!connection) + return QPixmap(); + if (!porymapConfig.showDiveEmergeMaps) + return QPixmap(); // Save some rendering time if it won't be displayed + if (connection->targetMapName() == connection->parentMapName()) + return QPixmap(); // If the map is connected to itself then rendering is pointless. + return connection->getPixmap(); +} + +void DivingMapPixmapItem::updatePixmap() { + setPixmap(getBasePixmap(m_connection)); +} + +void DivingMapPixmapItem::onTargetMapChanged() { + updatePixmap(); + setComboText(m_connection->targetMapName()); +} + +void DivingMapPixmapItem::setComboText(const QString &text) { + if (!m_combo) + return; + + const QSignalBlocker blocker(m_combo); + m_combo->setCurrentText(text); +} diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 03cbc2b8..b61d3738 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -192,6 +192,12 @@ bool MapImageExporter::historyItemAppliesToFrame(const QUndoCommand *command) { return this->showCollision; case CommandId::ID_PaintBorder: return this->showBorder; + case CommandId::ID_MapConnectionMove: + case CommandId::ID_MapConnectionChangeDirection: + case CommandId::ID_MapConnectionChangeMap: + case CommandId::ID_MapConnectionAdd: + case CommandId::ID_MapConnectionRemove: + return this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections; case CommandId::ID_EventMove: case CommandId::ID_EventShift: case CommandId::ID_EventCreate: @@ -398,14 +404,15 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { if (!this->mode) { // if showing connections, draw on outside of image QPainter connectionPainter(&pixmap); + // TODO: Is this still the most sensible way to do this (esp. rendering pixmap) for (auto connectionItem : editor->connection_items) { const QString direction = connectionItem->connection->direction(); if ((showUpConnections && direction == "up") || (showDownConnections && direction == "down") || (showLeftConnections && direction == "left") || (showRightConnections && direction == "right")) - connectionPainter.drawImage(connectionItem->initialX + borderWidth, connectionItem->initialY + borderHeight, - connectionItem->basePixmap.toImage()); + connectionPainter.drawImage(connectionItem->x() + borderWidth, connectionItem->y() + borderHeight, + connectionItem->connection->getPixmap().toImage()); } connectionPainter.end(); } diff --git a/src/ui/newmapconnectiondialog.cpp b/src/ui/newmapconnectiondialog.cpp index aa7c05cf..254b4b6e 100644 --- a/src/ui/newmapconnectiondialog.cpp +++ b/src/ui/newmapconnectiondialog.cpp @@ -8,14 +8,13 @@ NewMapConnectionDialog::NewMapConnectionDialog(QWidget *parent, Map* map, const ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - this->result = nullptr; - ui->comboBox_Direction->setEditable(false); ui->comboBox_Direction->setMinimumContentsLength(0); ui->comboBox_Direction->addItems(MapConnection::cardinalDirections); ui->comboBox_Map->setMinimumContentsLength(6); ui->comboBox_Map->addItems(mapNames); + ui->comboBox_Map->setInsertPolicy(QComboBox::NoInsert); // Choose default direction QMap directionCounts; @@ -51,6 +50,11 @@ NewMapConnectionDialog::~NewMapConnectionDialog() } void NewMapConnectionDialog::accept() { - this->result = new MapConnection(ui->comboBox_Map->currentText(), ui->comboBox_Direction->currentText()); + // Invalid map names are not allowed + if (ui->comboBox_Map->findText(ui->comboBox_Map->currentText()) < 0) { + // TODO: Display error message + return; + } + emit accepted(new MapConnection(ui->comboBox_Map->currentText(), ui->comboBox_Direction->currentText())); QDialog::accept(); }