diff --git a/include/core/editcommands.h b/include/core/editcommands.h index 9a54063c..5dc4bf0c 100644 --- a/include/core/editcommands.h +++ b/include/core/editcommands.h @@ -14,7 +14,7 @@ class Map; class Layout; class Blockdata; class Event; -class DraggablePixmapItem; +class EventPixmapItem; class Editor; enum CommandId { diff --git a/include/core/events.h b/include/core/events.h index 7c74514f..9da9b531 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -19,7 +19,7 @@ class EventFrame; class ObjectFrame; class CloneObjectFrame; class WarpFrame; -class DraggablePixmapItem; +class EventPixmapItem; class Event; class ObjectEvent; @@ -79,9 +79,13 @@ public: None, }; - // all event groups except warps have IDs that start at 1 + // Normally we refer to events using their index in the list of that group's events. + // Object events often get referred to with a special "local ID", which is really just the index + 1. + // We use this local ID number in the index spinner for object events instead of the actual index. + // This distinction is only really important for object and warp events, because these are normally + // the only two groups of events that need to be explicitly referred to. static int getIndexOffset(Event::Group group) { - return (group == Event::Group::Warp) ? 0 : 1; + return (group == Event::Group::Object) ? 1 : 0; } static Event::Group typeToGroup(Event::Type type) { @@ -149,13 +153,13 @@ public: QJsonObject getCustomAttributes() const { return this->customAttributes; } void setCustomAttributes(const QJsonObject &newCustomAttributes) { this->customAttributes = newCustomAttributes; } - virtual void loadPixmap(Project *project); + virtual QPixmap loadPixmap(Project *project); void setPixmap(QPixmap newPixmap) { this->pixmap = newPixmap; } QPixmap getPixmap() const { return this->pixmap; } - void setPixmapItem(DraggablePixmapItem *item); - DraggablePixmapItem *getPixmapItem() const { return this->pixmapItem; } + void setPixmapItem(EventPixmapItem *item); + EventPixmapItem *getPixmapItem() const { return this->pixmapItem; } void setUsesDefaultPixmap(bool newUsesDefaultPixmap) { this->usesDefaultPixmap = newUsesDefaultPixmap; } bool getUsesDefaultPixmap() const { return this->usesDefaultPixmap; } @@ -194,7 +198,7 @@ protected: QJsonObject customAttributes; QPixmap pixmap; - DraggablePixmapItem *pixmapItem = nullptr; + EventPixmapItem *pixmapItem = nullptr; QPointer eventFrame; @@ -229,7 +233,7 @@ public: virtual QSet getExpectedFields() override; - virtual void loadPixmap(Project *project) override; + virtual QPixmap loadPixmap(Project *project) override; void setGfx(QString newGfx) { this->gfx = newGfx; } QString getGfx() const { return this->gfx; } @@ -296,17 +300,17 @@ public: virtual QSet getExpectedFields() override; - virtual void loadPixmap(Project *project) override; + virtual QPixmap loadPixmap(Project *project) override; void setTargetMap(QString newTargetMap) { this->targetMap = newTargetMap; } QString getTargetMap() const { return this->targetMap; } - void setTargetID(int newTargetID) { this->targetID = newTargetID; } - int getTargetID() const { return this->targetID; } + void setTargetID(QString newTargetID) { this->targetID = newTargetID; } + QString getTargetID() const { return this->targetID; } private: QString targetMap; - int targetID = 0; + QString targetID; }; diff --git a/include/core/map.h b/include/core/map.h index 43b0619f..84791fa3 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -72,6 +72,8 @@ public: void resetEvents(); QList getEvents(Event::Group group = Event::Group::None) const; Event* getEvent(Event::Group group, int index) const; + Event* getEvent(Event::Group group, const QString &idName) const; + QStringList getEventIdNames(Event::Group group) const; int getNumEvents(Event::Group group = Event::Group::None) const; QStringList getScriptLabels(Event::Group group = Event::Group::None); QString getScriptsFilePath() const; diff --git a/include/editor.h b/include/editor.h index 58588b0e..432084e8 100644 --- a/include/editor.h +++ b/include/editor.h @@ -30,7 +30,7 @@ #include "mapruler.h" #include "encountertablemodel.h" -class DraggablePixmapItem; +class EventPixmapItem; class MetatilesPixmapItem; class Editor : public QObject @@ -109,7 +109,7 @@ public: void toggleBorderVisibility(bool visible, bool enableScriptCallback = true); void updateCustomMapAttributes(); - DraggablePixmapItem *addEventPixmapItem(Event *event); + EventPixmapItem *addEventPixmapItem(Event *event); void removeEventPixmapItem(Event *event); bool canAddEvents(const QList &events); void selectMapEvent(Event *event, bool toggle = false); @@ -118,13 +118,16 @@ public: void duplicateSelectedEvents(); void redrawAllEvents(); void redrawEvents(const QList &events); - void redrawEventPixmapItem(DraggablePixmapItem *item); + void redrawEventPixmapItem(EventPixmapItem *item); + void updateEventPixmapItemZValue(EventPixmapItem *item); qreal getEventOpacity(const Event *event) const; void setPlayerViewRect(const QRectF &rect); void updateCursorRectPos(int x, int y); void setCursorRectVisible(bool visible); + void onEventDragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition); + void onEventReleased(Event *event, const QPoint &position); void updateWarpEventWarning(Event *event); void updateWarpEventWarnings(); @@ -175,10 +178,7 @@ public: static QList> collisionIcons; int eventShiftActionId = 0; - - void eventsView_onMousePress(QMouseEvent *event); - - bool selectingEvent = false; + int eventMoveActionId = 0; void deleteSelectedEvents(); void shouldReselectEvents(); @@ -186,6 +186,22 @@ public: static void openInTextEditor(const QString &path, int lineNum = 0); void setCollisionGraphics(); + enum ZValue { + MapBorder = -4, + MapConnectionInactive = -3, + MapConnectionActive = -2, + MapConnectionMask = -1, + + // Event pixmaps set their z value to be their y position on the map. + // Their y value is int16_t, so we have enough space to allocate the + // full range + 1 for the selected event (which should always be on top). + EventMinimum = 1, + EventMaximum = EventMinimum + 0x10000, + + Ruler, + ResizeLayoutPopup + }; + public slots: void openMapScripts() const; void openScript(const QString &scriptLabel) const; @@ -253,11 +269,11 @@ private slots: signals: void eventsChanged(); + void openEventMap(Event*); void openConnectedMap(MapConnection*); void wildMonTableOpened(EncounterTableModel*); void wildMonTableClosed(); void wildMonTableEdited(); - void warpEventDoubleClicked(QString, int, Event::Group); void currentMetatilesSelectionChanged(); void mapRulerStatusChanged(const QString &); void tilesetUpdated(QString); diff --git a/include/mainwindow.h b/include/mainwindow.h index 21dcce5a..683df61f 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -177,7 +177,7 @@ private slots: void on_action_Save_Project_triggered(); bool save(bool currentOnly = false); - void openWarpMap(QString map_name, int event_id, Event::Group event_group); + void openEventMap(Event *event); void duplicate(); void setClipboardData(poryjson::Json::object); @@ -197,8 +197,7 @@ private slots: void onMapLoaded(Map *map); void onMapRulerStatusChanged(const QString &); void applyUserShortcuts(); - void markMapEdited(); - void markSpecificMapEdited(Map*); + void markMapEdited(Map*); void markLayoutEdited(); void on_actionNew_Tileset_triggered(); diff --git a/include/ui/draggablepixmapitem.h b/include/ui/draggablepixmapitem.h deleted file mode 100644 index aeda4daf..00000000 --- a/include/ui/draggablepixmapitem.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef DRAGGABLEPIXMAPITEM_H -#define DRAGGABLEPIXMAPITEM_H - -#include -#include -#include -#include - -#include - -#include "events.h" - -class Editor; - -class DraggablePixmapItem : public QObject, public QGraphicsPixmapItem { - Q_OBJECT -public: - DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {} - - DraggablePixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) { - this->event = event; - event->setPixmapItem(this); - this->editor = editor; - updatePosition(); - } - - Event *event = nullptr; - - void updatePosition(); - void move(int dx, int dy); - void moveTo(const QPoint &pos); - void emitPositionChanged(); - void updatePixmap(); - -private: - Editor *editor = nullptr; - QPoint lastPos; - bool active = false; - bool releaseSelectionQueued = false; - -signals: - void positionChanged(Event *event); - void xChanged(int); - void yChanged(int); - void elevationChanged(int); - void spriteChanged(QPixmap pixmap); - void onPropertyChanged(QString key, QString value); - -public slots: - void set_x(int x) { - event->setX(x); - updatePosition(); - } - void set_y(int y) { - event->setY(y); - updatePosition(); - } - void set_elevation(int z) { - event->setElevation(z); - updatePosition(); - } - -protected: - void mousePressEvent(QGraphicsSceneMouseEvent*); - void mouseMoveEvent(QGraphicsSceneMouseEvent*); - void mouseReleaseEvent(QGraphicsSceneMouseEvent*); - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*); -}; - -#endif // DRAGGABLEPIXMAPITEM_H diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h index 763d6baf..09eae50b 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -57,7 +57,9 @@ protected: bool initialized = false; bool connected = false; + void populateDropdown(NoScrollComboBox * combo, const QStringList &items); void populateScriptDropdown(NoScrollComboBox * combo, Project * project); + void populateIdNameDropdown(NoScrollComboBox * combo, Project * project, const QString &mapName, Event::Group group); private: Event *event; @@ -78,6 +80,7 @@ public: virtual void populate(Project *project) override; public: + QLineEdit *line_edit_local_id; NoScrollComboBox *combo_sprite; NoScrollComboBox *combo_movement; NoScrollSpinBox *spinner_radius_x; @@ -108,12 +111,15 @@ public: virtual void populate(Project *project) override; public: + QLineEdit *line_edit_local_id; NoScrollComboBox *combo_sprite; - NoScrollSpinBox *spinner_target_id; + NoScrollComboBox *combo_target_id; NoScrollComboBox *combo_target_map; private: CloneObjectEvent *clone; + + void tryInvalidateIdDropdown(Map *map); }; @@ -131,12 +137,15 @@ public: virtual void populate(Project *project) override; public: + QLineEdit *line_edit_id; NoScrollComboBox *combo_dest_map; NoScrollComboBox *combo_dest_warp; QPushButton *warning; private: WarpEvent *warp; + + void tryInvalidateIdDropdown(Map *map); }; @@ -275,6 +284,8 @@ public: private: HealLocationEvent *healLocation; + + void tryInvalidateIdDropdown(Map *map); }; #endif // EVENTRAMES_H diff --git a/include/ui/eventpixmapitem.h b/include/ui/eventpixmapitem.h new file mode 100644 index 00000000..a28232ba --- /dev/null +++ b/include/ui/eventpixmapitem.h @@ -0,0 +1,58 @@ +#ifndef EVENTPIXMAPITEM_H +#define EVENTPIXMAPITEM_H + +#include +#include +#include +#include + +#include + +#include "events.h" + +class Project; + +class EventPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + explicit EventPixmapItem(Event *event); + + void render(Project *project); + + bool isSelected() const { return m_selected; } + void setSelected(bool selected) { m_selected = selected; } + + Event * getEvent() const { return m_event; } + + void move(int dx, int dy); + void moveTo(int x, int y); + void moveTo(const QPoint &pos); + +private: + QPixmap m_basePixmap; + Event *const m_event = nullptr; + QPoint m_lastPos; + bool m_active = false; + bool m_selected = false; + bool m_releaseSelectionQueued = false; + + void updatePixelPosition(); + +signals: + void xChanged(int x); + void yChanged(int y); + void posChanged(int x, int y); + void rendered(const QPixmap &pixmap); + void selected(Event *event, bool toggle); + void dragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition); + void released(Event *event, const QPoint &position); + void doubleClicked(Event *event); + +protected: + virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(m_event); } +}; + +#endif // EVENTPIXMAPITEM_H diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h index 0ca36239..c587d2a9 100644 --- a/include/ui/graphicsview.h +++ b/include/ui/graphicsview.h @@ -45,25 +45,4 @@ protected: virtual void keyPressEvent(QKeyEvent *event) override; }; -class Editor; - -// TODO: This should just be MapView. It makes map-based assumptions, and no other class inherits GraphicsView. -class GraphicsView : public QGraphicsView -{ -public: - GraphicsView() : QGraphicsView() {} - GraphicsView(QWidget *parent) : QGraphicsView(parent) {} - -public: -// GraphicsView_Object object; - Editor *editor; -protected: - virtual void mousePressEvent(QMouseEvent *event) override; - virtual void mouseMoveEvent(QMouseEvent *event) override; - virtual void mouseReleaseEvent(QMouseEvent *event) override; - virtual void moveEvent(QMoveEvent *event) override; -}; - -//Q_DECLARE_METATYPE(GraphicsView) - #endif // GRAPHICSVIEW_H diff --git a/include/ui/mapview.h b/include/ui/mapview.h index aa271757..d53e5cce 100644 --- a/include/ui/mapview.h +++ b/include/ui/mapview.h @@ -5,13 +5,17 @@ #include "graphicsview.h" #include "overlay.h" -class MapView : public GraphicsView +class Editor; + +class MapView : public QGraphicsView { Q_OBJECT public: - MapView() : GraphicsView() {} - MapView(QWidget *parent) : GraphicsView(parent) {} + MapView() : QGraphicsView() {} + MapView(QWidget *parent) : QGraphicsView(parent) {} + + Editor *editor; Overlay * getOverlay(int layer); void clearOverlayMap(); @@ -73,6 +77,7 @@ public: protected: virtual void drawForeground(QPainter *painter, const QRectF &rect) override; virtual void keyPressEvent(QKeyEvent*) override; + virtual void moveEvent(QMoveEvent *event) override; private: QMap overlayMap; diff --git a/porymap.pro b/porymap.pro index 1cdffddf..d8505e11 100644 --- a/porymap.pro +++ b/porymap.pro @@ -73,7 +73,7 @@ SOURCES += src/core/advancemapparser.cpp \ src/ui/customscriptseditor.cpp \ src/ui/customscriptslistitem.cpp \ src/ui/divingmappixmapitem.cpp \ - src/ui/draggablepixmapitem.cpp \ + src/ui/eventpixmapitem.cpp \ src/ui/bordermetatilespixmapitem.cpp \ src/ui/collisionpixmapitem.cpp \ src/ui/connectionpixmapitem.cpp \ @@ -184,7 +184,7 @@ HEADERS += include/core/advancemapparser.h \ include/ui/customscriptseditor.h \ include/ui/customscriptslistitem.h \ include/ui/divingmappixmapitem.h \ - include/ui/draggablepixmapitem.h \ + include/ui/eventpixmapitem.h \ include/ui/bordermetatilespixmapitem.h \ include/ui/collisionpixmapitem.h \ include/ui/connectionpixmapitem.h \ diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp index 8850448d..684d98c7 100644 --- a/src/core/editcommands.cpp +++ b/src/core/editcommands.cpp @@ -1,5 +1,5 @@ #include "editcommands.h" -#include "draggablepixmapitem.h" +#include "eventpixmapitem.h" #include "bordermetatilespixmapitem.h" #include "editor.h" diff --git a/src/core/events.cpp b/src/core/events.cpp index 0175556e..22315211 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -20,8 +20,7 @@ Event* Event::create(Event::Type type) { } Event::~Event() { - if (this->eventFrame) - this->eventFrame->deleteLater(); + delete this->eventFrame; } EventFrame *Event::getEventFrame() { @@ -34,7 +33,7 @@ void Event::destroyEventFrame() { this->eventFrame = nullptr; } -void Event::setPixmapItem(DraggablePixmapItem *item) { +void Event::setPixmapItem(EventPixmapItem *item) { this->pixmapItem = item; if (this->eventFrame) { this->eventFrame->invalidateConnections(); @@ -105,7 +104,7 @@ QString Event::typeToString(Event::Type type) { {Event::Type::CloneObject, "Clone Object"}, {Event::Type::Warp, "Warp"}, {Event::Type::Trigger, "Trigger"}, - {Event::Type::WeatherTrigger, "Weather"}, + {Event::Type::WeatherTrigger, "Weather Trigger"}, {Event::Type::Sign, "Sign"}, {Event::Type::HiddenItem, "Hidden Item"}, {Event::Type::SecretBase, "Secret Base"}, @@ -114,9 +113,10 @@ QString Event::typeToString(Event::Type type) { return typeToStringMap.value(type); } -void Event::loadPixmap(Project *project) { +QPixmap Event::loadPixmap(Project *project) { this->pixmap = project->getEventPixmap(this->getEventGroup()); this->usesDefaultPixmap = true; + return this->pixmap; } @@ -152,12 +152,13 @@ EventFrame *ObjectEvent::createEventFrame() { OrderedJson::object ObjectEvent::buildEventJson(Project *) { OrderedJson::object objectJson; - if (projectConfig.eventCloneObjectEnabled) { - objectJson["type"] = Event::typeToJsonKey(Event::Type::Object); - } QString idName = this->getIdName(); if (!idName.isEmpty()) objectJson["local_id"] = idName; + + if (projectConfig.eventCloneObjectEnabled) { + objectJson["type"] = Event::typeToJsonKey(Event::Type::Object); + } objectJson["graphics_id"] = this->getGfx(); objectJson["x"] = this->getX(); objectJson["y"] = this->getY(); @@ -224,13 +225,13 @@ QSet ObjectEvent::getExpectedFields() { return expectedFields; } -void ObjectEvent::loadPixmap(Project *project) { +QPixmap ObjectEvent::loadPixmap(Project *project) { this->pixmap = project->getEventPixmap(this->gfx, this->movement); if (!this->pixmap.isNull()) { this->usesDefaultPixmap = false; - } else { - Event::loadPixmap(project); + return this->pixmap; } + return Event::loadPixmap(project); } @@ -261,10 +262,11 @@ EventFrame *CloneObjectEvent::createEventFrame() { OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) { OrderedJson::object cloneJson; - cloneJson["type"] = Event::typeToJsonKey(Event::Type::CloneObject); QString idName = this->getIdName(); if (!idName.isEmpty()) cloneJson["local_id"] = idName; + + cloneJson["type"] = Event::typeToJsonKey(Event::Type::CloneObject); cloneJson["graphics_id"] = this->getGfx(); cloneJson["x"] = this->getX(); cloneJson["y"] = this->getY(); @@ -281,7 +283,7 @@ bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) { this->setY(readInt(&json, "y")); this->setIdName(readString(&json, "local_id")); this->setGfx(readString(&json, "graphics_id")); - this->setTargetID(readInt(&json, "target_local_id")); + this->setTargetID(readString(&json, "target_local_id")); // Log a warning if "target_map" isn't a known map ID, but don't overwrite user data. const QString mapConstant = readString(&json, "target_map"); @@ -295,7 +297,7 @@ bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) { void CloneObjectEvent::setDefaultValues(Project *project) { this->setGfx(project->gfxDefines.key(0, "0")); - this->setTargetID(1); + this->setTargetID(QString::number(Event::getIndexOffset(Event::Group::Object))); if (this->getMap()) this->setTargetMap(this->getMap()->name()); } @@ -312,11 +314,10 @@ QSet CloneObjectEvent::getExpectedFields() { return expectedFields; } -void CloneObjectEvent::loadPixmap(Project *project) { +QPixmap CloneObjectEvent::loadPixmap(Project *project) { // Try to get the targeted object to clone - int eventIndex = this->targetID - 1; Map *clonedMap = project->loadMap(this->targetMap); - Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, eventIndex) : nullptr; + Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, this->targetID) : nullptr; if (clonedEvent && clonedEvent->getEventType() == Event::Type::Object) { // Get graphics data from cloned object @@ -328,7 +329,7 @@ void CloneObjectEvent::loadPixmap(Project *project) { this->gfx = project->gfxDefines.key(0, "0"); this->movement = project->movementTypes.value(0, "0"); } - ObjectEvent::loadPixmap(project); + return ObjectEvent::loadPixmap(project); } diff --git a/src/core/map.cpp b/src/core/map.cpp index 9ecba8e6..3cd087e1 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -195,6 +195,49 @@ Event* Map::getEvent(Event::Group group, int index) const { return m_events[group].value(index, nullptr); } +Event* Map::getEvent(Event::Group group, const QString &idName) const { + if (idName.isEmpty()) + return nullptr; + + bool idIsNumber; + int id = idName.toInt(&idIsNumber, 0); + if (idIsNumber) + return getEvent(group, id - Event::getIndexOffset(group)); + + auto events = getEvents(group); + for (const auto &event : events) { + if (event->getIdName() == idName) { + return event; + } + } + return nullptr; +} + +// Returns a list of ID names for the given event group (or all events, if no group is given). +// For events with no explicit ID name, their index string is given instead. +QStringList Map::getEventIdNames(Event::Group group) const { + QList groups; + if (group == Event::Group::None) { + groups = Event::groups(); + } else { + groups.append(group); + } + + QStringList idNames; + for (const auto &group : groups) { + const auto events = m_events[group]; + int indexOffset = Event::getIndexOffset(group); + for (int i = 0; i < events.length(); i++) { + QString idName = events.at(i)->getIdName(); + if (idName.isEmpty()) { + idName = QString::number(i + indexOffset); + } + idNames.append(idName); + } + } + return idNames; +} + int Map::getNumEvents(Event::Group group) const { if (group == Event::Group::None) { // Total number of events diff --git a/src/editor.cpp b/src/editor.cpp index 2d789bb8..0ebc2cb2 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1,5 +1,5 @@ #include "editor.h" -#include "draggablepixmapitem.h" +#include "eventpixmapitem.h" #include "imageproviders.h" #include "log.h" #include "connectionslistitem.h" @@ -1345,7 +1345,6 @@ void Editor::setStraightPathCursorMode(QGraphicsSceneMouseEvent *event) { } void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item) { - // TODO: add event tab event painting tool buttons stuff here if (!item->getEditsEnabled()) { return; } @@ -1419,8 +1418,11 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i if (event && event->getPixmapItem()) event->getPixmapItem()->moveTo(pos); } - } else if (eventEditAction == EditAction::Select) { - // do nothing here, at least for now + } else if (eventEditAction == EditAction::Select && event->type() == QEvent::GraphicsSceneMousePress) { + if (!(event->modifiers() & Qt::ControlModifier) && this->selectedEvents.length() > 1) { + // User is clearing group selection by clicking on the background + selectMapEvent(this->selectedEvents.first()); + } } else if (eventEditAction == EditAction::Shift) { static QPoint selection_origin; @@ -1538,7 +1540,7 @@ bool Editor::displayLayout() { scene->installEventFilter(filter); connect(filter, &MapSceneEventFilter::wheelZoom, this, &Editor::onWheelZoom); scene->installEventFilter(this->map_ruler); - this->map_ruler->setZValue(1000); + this->map_ruler->setZValue(ZValue::Ruler); scene->addItem(this->map_ruler); } @@ -1723,9 +1725,6 @@ void Editor::clearMapEvents() { if (events_group->scene()) { events_group->scene()->removeItem(events_group); } - // events_group does not own its children, the childrens' parent - // is set to the group's parent (and our group has no parent). - qDeleteAll(events_group->childItems()); delete events_group; events_group = nullptr; } @@ -1745,9 +1744,15 @@ void Editor::displayMapEvents() { events_group->setHandlesChildEvents(false); } -DraggablePixmapItem *Editor::addEventPixmapItem(Event *event) { +EventPixmapItem *Editor::addEventPixmapItem(Event *event) { this->project->loadEventPixmap(event); - auto item = new DraggablePixmapItem(event, this); + auto item = new EventPixmapItem(event); + connect(item, &EventPixmapItem::doubleClicked, this, &Editor::openEventMap); + connect(item, &EventPixmapItem::dragged, this, &Editor::onEventDragged); + connect(item, &EventPixmapItem::released, this, &Editor::onEventReleased); + connect(item, &EventPixmapItem::selected, this, &Editor::selectMapEvent); + connect(item, &EventPixmapItem::posChanged, [this, event] { updateWarpEventWarning(event); }); + connect(item, &EventPixmapItem::yChanged, [this, item] { updateEventPixmapItemZValue(item); }); redrawEventPixmapItem(item); this->events_group->addToGroup(item); return item; @@ -1821,6 +1826,7 @@ void Editor::maskNonVisibleConnectionTiles() { QBrush brush(ui->graphicsView_Map->palette().color(QPalette::Active, QPalette::Base)); connection_mask = scene->addPath(mask, pen, brush); + connection_mask->setZValue(ZValue::MapConnectionMask); } void Editor::clearMapBorder() { @@ -1843,7 +1849,7 @@ void Editor::displayMapBorder() { QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); item->setX(x * 16); item->setY(y * 16); - item->setZValue(-3); + item->setZValue(ZValue::MapBorder); scene->addItem(item); borderItems.append(item); } @@ -2013,32 +2019,59 @@ qreal Editor::getEventOpacity(const Event *event) const { return event->getUsesDefaultPixmap() ? 0.7 : 1.0; } -void Editor::redrawEventPixmapItem(DraggablePixmapItem *item) { - if (item && item->event && !item->event->getPixmap().isNull()) { - item->setOpacity(getEventOpacity(item->event)); - project->loadEventPixmap(item->event, true); - item->setPixmap(item->event->getPixmap()); - item->setShapeMode(porymapConfig.eventSelectionShapeMode); +void Editor::redrawEventPixmapItem(EventPixmapItem *item) { + if (!item) return; + Event *event = item->getEvent(); + if (!event) return; - if (this->editMode == EditMode::Events) { - if (this->selectedEvents.contains(item->event)) { - // Draw the selection rectangle - QImage image = item->pixmap().toImage(); - QPainter painter(&image); - painter.setPen(QColor(255, 0, 255)); - painter.drawRect(0, 0, image.width() - 1, image.height() - 1); - painter.end(); - item->setPixmap(QPixmap::fromImage(image)); - } - item->setAcceptedMouseButtons(Qt::AllButtons); - } else { - // Can't interact with event pixmaps outside of event editing mode. - // We could do setEnabled(false), but rather than ignoring the mouse events this - // would reject them, which would prevent painting on the map behind the events. - item->setAcceptedMouseButtons(Qt::NoButton); - } - item->updatePosition(); + if (this->editMode == EditMode::Events) { + item->setAcceptedMouseButtons(Qt::AllButtons); + item->setSelected(this->selectedEvents.contains(event)); + } else { + // Can't interact with event pixmaps outside of event editing mode. + // We could do setEnabled(false), but rather than ignoring the mouse events this + // would reject them, which would prevent painting on the map behind the events. + item->setAcceptedMouseButtons(Qt::NoButton); + item->setSelected(false); } + updateEventPixmapItemZValue(item); + item->setOpacity(getEventOpacity(event)); + item->setShapeMode(porymapConfig.eventSelectionShapeMode); + item->render(project); +} + +void Editor::updateEventPixmapItemZValue(EventPixmapItem *item) { + if (!item) return; + Event *event = item->getEvent(); + if (!event) return; + + if (item->isSelected()) { + item->setZValue(ZValue::EventMaximum); + } else { + item->setZValue(event->getY() + ((ZValue::EventMaximum - ZValue::EventMinimum) / 2)); + } +} + +void Editor::onEventDragged(Event *event, const QPoint &oldPosition, const QPoint &newPosition) { + if (!this->map || !this->map_item) + return; + + this->map_item->hoveredMapMetatileChanged(newPosition); + + // Drag all the other selected events (if any) with it + QList draggedEvents; + if (this->selectedEvents.contains(event)) { + draggedEvents = this->selectedEvents; + } else { + draggedEvents.append(event); + } + + QPoint moveDistance = newPosition - oldPosition; + this->map->commit(new EventMove(draggedEvents, moveDistance.x(), moveDistance.y(), this->eventMoveActionId)); +} + +void Editor::onEventReleased(Event *, const QPoint &) { + this->eventMoveActionId++; } // Warp events display a warning if they're not positioned on a metatile with a warp behavior. @@ -2323,32 +2356,6 @@ bool Editor::startDetachedProcess(const QString &command, const QString &working return process.startDetached(pid); } -// It doesn't seem to be possible to prevent the mousePress event -// from triggering both event's DraggablePixmapItem and the background mousePress. -// Since the DraggablePixmapItem's event fires first, we can set a temp -// variable "selectingEvent" so that we can detect whether or not the user -// is clicking on the background instead of an event. -void Editor::eventsView_onMousePress(QMouseEvent *event) { - // make sure we are in event editing mode - if (map_item && this->editMode != EditMode::Events) { - return; - } - if (this->eventEditAction == EditAction::Paint && event->buttons() & Qt::RightButton) { - this->eventEditAction = EditAction::Select; - this->settings->mapCursor = QCursor(); - this->cursorMapTileRect->setSingleTileMode(); - this->ui->toolButton_Paint->setChecked(false); - this->ui->toolButton_Select->setChecked(true); - } - - bool multiSelect = event->modifiers() & Qt::ControlModifier; - if (!selectingEvent && !multiSelect && this->selectedEvents.length() > 1) { - // User is clearing group selection by clicking on the background - this->selectMapEvent(this->selectedEvents.first()); - } - selectingEvent = false; -} - void Editor::setCollisionTabSpinBoxes(uint16_t collision, uint16_t elevation) { const QSignalBlocker blocker1(ui->spinBox_SelectedCollision); const QSignalBlocker blocker2(ui->spinBox_SelectedElevation); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3860c456..5a0169a8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,7 +10,7 @@ #include "customattributesframe.h" #include "scripting.h" #include "adjustingstackedwidget.h" -#include "draggablepixmapitem.h" +#include "eventpixmapitem.h" #include "editcommands.h" #include "flowlayout.h" #include "shortcut.h" @@ -345,9 +345,9 @@ void MainWindow::initEditor() { this->editor = new Editor(ui); connect(this->editor, &Editor::eventsChanged, this, &MainWindow::updateEvents); connect(this->editor, &Editor::openConnectedMap, this, &MainWindow::onOpenConnectedMap); - connect(this->editor, &Editor::warpEventDoubleClicked, this, &MainWindow::openWarpMap); + connect(this->editor, &Editor::openEventMap, this, &MainWindow::openEventMap); connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged); - connect(this->editor, &Editor::wildMonTableEdited, this, &MainWindow::markMapEdited); + connect(this->editor, &Editor::wildMonTableEdited, [this] { markMapEdited(this->editor->map); }); connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged); connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated); connect(ui->newEventToolButton, &NewEventToolButton::newEventAdded, this->editor, &Editor::addNewEvent); @@ -528,11 +528,7 @@ void MainWindow::updateWindowTitle() { } } -void MainWindow::markMapEdited() { - if (editor) markSpecificMapEdited(editor->map); -} - -void MainWindow::markSpecificMapEdited(Map* map) { +void MainWindow::markMapEdited(Map* map) { if (!map) return; map->setHasUnsavedDataChanges(true); @@ -956,8 +952,6 @@ bool MainWindow::setMap(QString map_name) { updateMapList(); resetMapListFilters(); - connect(editor->map, &Map::modified, this, &MainWindow::markMapEdited, Qt::UniqueConnection); - // If the map's MAPSEC / layout changes, update the map's position in the map list. // These are doing more work than necessary, rather than rebuilding the entire list they should find and relocate the appropriate row. connect(editor->map, &Map::layoutChanged, this, &MainWindow::rebuildMapList_Layouts, Qt::UniqueConnection); @@ -1070,20 +1064,57 @@ void MainWindow::refreshCollisionSelector() { on_horizontalSlider_CollisionZoom_valueChanged(ui->horizontalSlider_CollisionZoom->value()); } -void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_group) { - // Open the destination map. - if (!userSetMap(map_name)) +// Some events (like warps) have data that refers to an event on a different map. +// This function opens that map, and selects the event it's referring to. +void MainWindow::openEventMap(Event *sourceEvent) { + if (!sourceEvent || !this->editor->map) return; + + QString targetMapName; + QString targetEventIdName; + Event::Group targetEventGroup; + + Event::Type eventType = sourceEvent->getEventType(); + if (eventType == Event::Type::Warp) { + // Warp events open to their destination warp event. + WarpEvent *warp = dynamic_cast(sourceEvent); + targetMapName = warp->getDestinationMap(); + targetEventIdName = warp->getDestinationWarpID(); + targetEventGroup = Event::Group::Warp; + } else if (eventType == Event::Type::CloneObject) { + // Clone object events open to their target object event. + CloneObjectEvent *clone = dynamic_cast(sourceEvent); + targetMapName = clone->getTargetMap(); + targetEventIdName = clone->getTargetID(); + targetEventGroup = Event::Group::Object; + } else if (eventType == Event::Type::SecretBase) { + // Secret Bases open to their secret base entrance + const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix); + SecretBaseEvent *base = dynamic_cast(sourceEvent); + + // Extract the map name from the secret base ID. + QString baseId = base->getBaseID(); + targetMapName = this->editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_"))); + + // Just select the first warp. Normally the only warp event on every secret base map is the entrance/exit, so this is usually correct. + // The warp IDs for secret bases are specified in the project's C code, not in the map data, so we don't have an easy way to read the actual IDs. + targetEventIdName = "0"; + targetEventGroup = Event::Group::Warp; + } else if (eventType == Event::Type::HealLocation && projectConfig.healLocationRespawnDataEnabled) { + // Heal location events open to their respawn NPC + HealLocationEvent *heal = dynamic_cast(sourceEvent); + targetMapName = heal->getRespawnMapName(); + targetEventIdName = heal->getRespawnNPC(); + targetEventGroup = Event::Group::Object; + } else { + // Other event types have no target map to open. + return; + } + if (!userSetMap(targetMapName)) return; - // Select the target event. - int index = event_id - Event::getIndexOffset(event_group); - Event* event = this->editor->map->getEvent(event_group, index); - if (event) { - this->editor->selectMapEvent(event); - } else { - // Can still warp to this map, but can't select the specified event - logWarn(QString("%1 %2 doesn't exist on map '%3'").arg(Event::groupToString(event_group)).arg(event_id).arg(map_name)); - } + // Map opened successfully, now try to select the targeted event on that map. + Event *targetEvent = this->editor->map->getEvent(targetEventGroup, targetEventIdName); + this->editor->selectMapEvent(targetEvent); } void MainWindow::displayMapProperties() { @@ -1123,7 +1154,7 @@ void MainWindow::on_comboBox_LayoutSelector_currentTextChanged(const QString &te } this->editor->map->setLayout(layout); setMap(this->editor->map->name()); - markMapEdited(); + markMapEdited(this->editor->map); } void MainWindow::onLayoutSelectorEditingFinished() { @@ -2488,7 +2519,7 @@ void MainWindow::onOpenConnectedMap(MapConnection *connection) { } void MainWindow::onMapLoaded(Map *map) { - connect(map, &Map::modified, [this, map] { this->markSpecificMapEdited(map); }); + connect(map, &Map::modified, [this, map] { markMapEdited(map); }); } void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) { diff --git a/src/project.cpp b/src/project.cpp index d66fe6c8..43713c9d 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -159,6 +159,11 @@ void Project::clearTilesetCache() { } Map* Project::loadMap(const QString &mapName) { + if (mapName == getDynamicMapName()) { + // Silently ignored, caller is expected to handle this if they want this to be an error. + return nullptr; + } + Map* map = this->maps.value(mapName); if (!map) { logError(QString("Unknown map name '%1'.").arg(mapName)); diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp index 7ea5f820..c6e606b0 100644 --- a/src/ui/connectionpixmapitem.cpp +++ b/src/ui/connectionpixmapitem.cpp @@ -1,6 +1,7 @@ #include "connectionpixmapitem.h" #include "editcommands.h" #include "map.h" +#include "editor.h" #include @@ -30,7 +31,7 @@ void ConnectionPixmapItem::render(bool ignoreCache) { this->basePixmap = this->connection->render(); QPixmap pixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->basePixmap.height()); - this->setZValue(-1); + this->setZValue(Editor::ZValue::MapConnectionActive); // When editing is inactive the current selection is ignored, all connections should appear normal. if (this->getEditable()) { @@ -42,7 +43,7 @@ void ConnectionPixmapItem::render(bool ignoreCache) { painter.end(); } else { // Darken the image - this->setZValue(-2); + this->setZValue(Editor::ZValue::MapConnectionInactive); QPainter painter(&pixmap); int alpha = static_cast(255 * 0.25); painter.fillRect(0, 0, pixmap.width(), pixmap.height(), QColor(0, 0, 0, alpha)); diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp deleted file mode 100644 index f2fdeb91..00000000 --- a/src/ui/draggablepixmapitem.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "draggablepixmapitem.h" -#include "editor.h" -#include "editcommands.h" -#include "mapruler.h" -#include "metatile.h" - -static unsigned currentActionId = 0; - - -void DraggablePixmapItem::updatePosition() { - int x = this->event->getPixelX(); - int y = this->event->getPixelY(); - setX(x); - setY(y); - if (this->editor->selectedEvents.contains(this->event)) { - setZValue(event->getY() + 1); - } else { - setZValue(event->getY()); - } - editor->updateWarpEventWarning(event); -} - -void DraggablePixmapItem::emitPositionChanged() { - emit xChanged(event->getX()); - emit yChanged(event->getY()); - emit elevationChanged(event->getElevation()); -} - -void DraggablePixmapItem::updatePixmap() { - editor->project->loadEventPixmap(event, true); - this->updatePosition(); - editor->redrawEventPixmapItem(this); - emit spriteChanged(event->getPixmap()); -} - -void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { - if (this->active) - return; - this->active = true; - this->lastPos = Metatile::coordFromPixmapCoord(mouse->scenePos()); - - bool selectionToggle = mouse->modifiers() & Qt::ControlModifier; - if (selectionToggle || !this->editor->selectedEvents.contains(this->event)) { - // User is either toggling this selection on/off as part of a group selection, - // or they're newly selecting just this item. - this->editor->selectMapEvent(this->event, selectionToggle); - } else { - // This item is already selected and the user isn't toggling the selection, so there are 4 possibilities: - // 1. This is the only selected event, and the selection is pointless. - // 2. This is the only selected event, and they want to drag the item around. - // 3. There's a group selection, and they want to start a new selection with just this item. - // 4. There's a group selection, and they want to drag the group around. - // 'selectMapEvent' will immediately clear the rest of the selection, which supports #1-3 but prevents #4. - // To support #4 we set the flag below, and we only call 'selectMapEvent' on mouse release if no move occurred. - this->releaseSelectionQueued = true; - } - this->editor->selectingEvent = true; -} - -void DraggablePixmapItem::move(int dx, int dy) { - event->setX(event->getX() + dx); - event->setY(event->getY() + dy); - updatePosition(); - emitPositionChanged(); -} - -void DraggablePixmapItem::moveTo(const QPoint &pos) { - event->setX(pos.x()); - event->setY(pos.y()); - updatePosition(); - emitPositionChanged(); -} - -void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { - if (!this->active) - return; - - QPoint pos = Metatile::coordFromPixmapCoord(mouse->scenePos()); - if (pos == this->lastPos) - return; - - QPoint moveDistance = pos - this->lastPos; - this->lastPos = pos; - emit this->editor->map_item->hoveredMapMetatileChanged(pos); - - QList selectedEvents; - if (this->editor->selectedEvents.contains(this->event)) { - selectedEvents = this->editor->selectedEvents; - } else { - selectedEvents.append(this->event); - } - editor->map->commit(new EventMove(selectedEvents, moveDistance.x(), moveDistance.y(), currentActionId)); - this->releaseSelectionQueued = false; -} - -void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { - if (!this->active) - return; - this->active = false; - currentActionId++; - if (this->releaseSelectionQueued) { - this->releaseSelectionQueued = false; - if (Metatile::coordFromPixmapCoord(mouse->scenePos()) == this->lastPos) - this->editor->selectMapEvent(this->event); - } -} - -// Events with properties that specify a map will open that map when double-clicked. -void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { - Event::Type eventType = this->event->getEventType(); - if (eventType == Event::Type::Warp) { - WarpEvent *warp = dynamic_cast(this->event); - QString destMap = warp->getDestinationMap(); - int warpId = ParseUtil::gameStringToInt(warp->getDestinationWarpID()); - emit editor->warpEventDoubleClicked(destMap, warpId, Event::Group::Warp); - } - else if (eventType == Event::Type::CloneObject) { - CloneObjectEvent *clone = dynamic_cast(this->event); - emit editor->warpEventDoubleClicked(clone->getTargetMap(), clone->getTargetID(), Event::Group::Object); - } - else if (eventType == Event::Type::SecretBase) { - const QString mapPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix); - SecretBaseEvent *base = dynamic_cast(this->event); - QString baseId = base->getBaseID(); - QString destMap = editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_"))); - emit editor->warpEventDoubleClicked(destMap, 0, Event::Group::Warp); - } - else if (eventType == Event::Type::HealLocation && projectConfig.healLocationRespawnDataEnabled) { - HealLocationEvent *heal = dynamic_cast(this->event); - const QString localIdName = heal->getRespawnNPC(); - int localId = 0; // TODO: Get value from localIdName - emit editor->warpEventDoubleClicked(heal->getRespawnMapName(), localId, Event::Group::Object); - } -} diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index d3cfdc80..95a31557 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -1,7 +1,7 @@ #include "eventframes.h" #include "customattributesframe.h" #include "editcommands.h" -#include "draggablepixmapitem.h" +#include "eventpixmapitem.h" #include using std::numeric_limits; @@ -74,6 +74,7 @@ void EventFrame::setup() { this->label_id = new QLabel("event_type"); l_vbox_1->addWidget(this->label_id); l_vbox_1->addLayout(l_layout_xyz); + this->label_id->setText(Event::typeToString(this->event->getEventType())); // icon / pixmap label this->label_icon = new QLabel(this); @@ -113,7 +114,7 @@ void EventFrame::connectSignals(MainWindow *) { } }); - connect(this->event->getPixmapItem(), &DraggablePixmapItem::xChanged, this->spinner_x, &NoScrollSpinBox::setValue); + connect(this->event->getPixmapItem(), &EventPixmapItem::xChanged, this->spinner_x, &NoScrollSpinBox::setValue); this->spinner_y->disconnect(); connect(this->spinner_y, QOverload::of(&QSpinBox::valueChanged), [this](int value) { @@ -122,7 +123,7 @@ void EventFrame::connectSignals(MainWindow *) { this->event->getMap()->commit(new EventMove(QList() << this->event, 0, delta, this->spinner_y->getActionId())); } }); - connect(this->event->getPixmapItem(), &DraggablePixmapItem::yChanged, this->spinner_y, &NoScrollSpinBox::setValue); + connect(this->event->getPixmapItem(), &EventPixmapItem::yChanged, this->spinner_y, &NoScrollSpinBox::setValue); this->spinner_z->disconnect(); connect(this->spinner_z, QOverload::of(&QSpinBox::valueChanged), [this](int value) { @@ -172,19 +173,31 @@ void EventFrame::setActive(bool active) { this->blockSignals(!active); } +void EventFrame::populateDropdown(NoScrollComboBox * combo, const QStringList &items) { + // Set the items in the combo box. This may be called after the frame is initialized + // if the frame needs to be repopulated, so ensure the text in the combo is preserved + // and that we don't accidentally fire 'currentTextChanged'. + const QSignalBlocker b(combo); + const QString savedText = combo->currentText(); + combo->clear(); + combo->addItems(items); + combo->setCurrentText(savedText); +} + void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * project) { // The script dropdown and autocomplete are populated with scripts used by the map's events and from its scripts file. if (!this->event->getMap()) return; QStringList scripts = this->event->getMap()->getScriptLabels(this->event->getEventGroup()); - combo->addItems(scripts); + populateDropdown(combo, scripts); // Depending on the settings, the autocomplete may also contain all global scripts. if (porymapConfig.loadAllEventScripts) { project->insertGlobalScriptLabels(scripts); } + // Note: Because 'combo' is the parent, the old QCompleter will be deleted when a new one is set. auto completer = new QCompleter(scripts, combo); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); @@ -197,14 +210,32 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj combo->setCompleter(completer); // If the project changes the script labels, update the EventFrame. + // TODO: At the moment this only happens when the user changes script settings (i.e. when 'porymapConfig.loadAllEventScripts' changes). + // This should ultimately be connected to a file watcher so that we can also update the dropdown when the scripts file changes. connect(project, &Project::eventScriptLabelsRead, this, &EventFrame::invalidateValues, Qt::UniqueConnection); } +void EventFrame::populateIdNameDropdown(NoScrollComboBox * combo, Project * project, const QString &mapName, Event::Group group) { + if (!project->mapNames.contains(mapName)) + return; + + Map *map = project->loadMap(mapName); + if (map) populateDropdown(combo, map->getEventIdNames(group)); +} + void ObjectFrame::setup() { EventFrame::setup(); - this->label_id->setText("Object"); + // local id + QFormLayout *l_form_local_id = new QFormLayout(); + this->line_edit_local_id = new QLineEdit(this); + static const QString line_edit_local_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this object in scripts. " + "If no name is given you can refer to this object using its 'object id' number."); + this->line_edit_local_id->setToolTip(line_edit_local_id_toolTip); + this->line_edit_local_id->setPlaceholderText("LOCALID_MY_NPC"); + l_form_local_id->addRow("Local ID", this->line_edit_local_id); + this->layout_contents->addLayout(l_form_local_id); // sprite combo QFormLayout *l_form_sprite = new QFormLayout(); @@ -294,21 +325,29 @@ void ObjectFrame::connectSignals(MainWindow *window) { if (this->connected) return; EventFrame::connectSignals(window); + Project *project = window->editor->project; + + // local id + this->line_edit_local_id->disconnect(); + connect(this->line_edit_local_id, &QLineEdit::textChanged, [this](const QString &text) { + this->object->setIdName(text); + this->object->modify(); + }); // sprite update this->combo_sprite->disconnect(); - connect(this->combo_sprite, &QComboBox::currentTextChanged, [this](const QString &text) { + connect(this->combo_sprite, &QComboBox::currentTextChanged, [this, project](const QString &text) { this->object->setGfx(text); - this->object->getPixmapItem()->updatePixmap(); + this->object->getPixmapItem()->render(project); this->object->modify(); }); - connect(this->object->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); + connect(this->object->getPixmapItem(), &EventPixmapItem::rendered, this->label_icon, &QLabel::setPixmap); // movement this->combo_movement->disconnect(); - connect(this->combo_movement, &QComboBox::currentTextChanged, [this](const QString &text) { + connect(this->combo_movement, &QComboBox::currentTextChanged, [this, project](const QString &text) { this->object->setMovement(text); - this->object->getPixmapItem()->updatePixmap(); + this->object->getPixmapItem()->render(project); this->object->modify(); }); @@ -366,6 +405,9 @@ void ObjectFrame::initialize() { const QSignalBlocker blocker(this); EventFrame::initialize(); + // local id + this->line_edit_local_id->setText(this->object->getIdName()); + // sprite this->combo_sprite->setTextItem(this->object->getGfx()); @@ -397,12 +439,11 @@ void ObjectFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_sprite->addItems(project->gfxDefines.keys()); - this->combo_movement->addItems(project->movementTypes); - this->combo_flag->addItems(project->flagNames); - this->combo_trainer_type->addItems(project->trainerTypes); - - this->populateScriptDropdown(this->combo_script, project); + populateDropdown(this->combo_sprite, project->gfxDefines.keys()); + populateDropdown(this->combo_movement, project->movementTypes); + populateDropdown(this->combo_flag, project->flagNames); + populateDropdown(this->combo_trainer_type, project->trainerTypes); + populateScriptDropdown(this->combo_script, project); } @@ -410,13 +451,25 @@ void ObjectFrame::populate(Project *project) { void CloneObjectFrame::setup() { EventFrame::setup(); - this->label_id->setText("Clone Object"); - this->spinner_z->setEnabled(false); + // local id + QFormLayout *l_form_local_id = new QFormLayout(); + this->line_edit_local_id = new QLineEdit(this); + static const QString line_edit_local_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this object in scripts. " + "If no name is given you can refer to this object using its 'object id' number."); + this->line_edit_local_id->setToolTip(line_edit_local_id_toolTip); + this->line_edit_local_id->setPlaceholderText("LOCALID_MY_CLONE_NPC"); + l_form_local_id->addRow("Local ID", this->line_edit_local_id); + this->layout_contents->addLayout(l_form_local_id); + // sprite combo (edits disabled) QFormLayout *l_form_sprite = new QFormLayout(); this->combo_sprite = new NoScrollComboBox(this); + static const QString combo_sprite_toolTip = Util::toHtmlParagraph("The sprite graphics to use for this object. This is updated automatically " + "to match the target object, and so can't be edited. By default the games " + "will get the graphics directly from the target object, so this field is ignored."); + this->combo_sprite->setToolTip(combo_sprite_toolTip); l_form_sprite->addRow("Sprite", this->combo_sprite); this->combo_sprite->setEnabled(false); this->layout_contents->addLayout(l_form_sprite); @@ -429,12 +482,12 @@ void CloneObjectFrame::setup() { l_form_dest_map->addRow("Target Map", this->combo_target_map); this->layout_contents->addLayout(l_form_dest_map); - // clone local id spinbox + // clone local id combo QFormLayout *l_form_dest_id = new QFormLayout(); - this->spinner_target_id = new NoScrollSpinBox(this); - static const QString spinner_target_id_toolTip = Util::toHtmlParagraph("event_object ID of the object being cloned."); - this->spinner_target_id->setToolTip(spinner_target_id_toolTip); - l_form_dest_id->addRow("Target Local ID", this->spinner_target_id); + this->combo_target_id = new NoScrollComboBox(this); + static const QString combo_target_id_toolTip = Util::toHtmlParagraph("The Local ID name or number of the object being cloned."); + this->combo_target_id->setToolTip(combo_target_id_toolTip); + l_form_dest_id->addRow("Target Local ID", this->combo_target_id); this->layout_contents->addLayout(l_form_dest_id); // custom attributes @@ -445,27 +498,48 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { if (this->connected) return; EventFrame::connectSignals(window); + Project *project = window->editor->project; + + // local id + this->line_edit_local_id->disconnect(); + connect(this->line_edit_local_id, &QLineEdit::textChanged, [this](const QString &text) { + this->clone->setIdName(text); + this->clone->modify(); + }); // update icon displayed in frame with target - connect(this->clone->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); + connect(this->clone->getPixmapItem(), &EventPixmapItem::rendered, this->label_icon, &QLabel::setPixmap); // target map this->combo_target_map->disconnect(); - connect(this->combo_target_map, &QComboBox::currentTextChanged, [this](const QString &text) { - this->clone->setTargetMap(text); - this->clone->getPixmapItem()->updatePixmap(); + connect(this->combo_target_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) { + this->clone->setTargetMap(mapName); + this->clone->getPixmapItem()->render(project); + this->combo_sprite->setCurrentText(this->clone->getGfx()); + this->clone->modify(); + populateIdNameDropdown(this->combo_target_id, project, mapName, Event::Group::Object); + }); + connect(window, &MainWindow::mapOpened, this, &CloneObjectFrame::tryInvalidateIdDropdown, Qt::UniqueConnection); + + // target id + this->combo_target_id->disconnect(); + connect(this->combo_target_id, &QComboBox::currentTextChanged, [this, project](const QString &text) { + this->clone->setTargetID(text); + this->clone->getPixmapItem()->render(project); this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); }); - // target id - this->spinner_target_id->disconnect(); - connect(this->spinner_target_id, QOverload::of(&QSpinBox::valueChanged), [this](int value) { - this->clone->setTargetID(value); - this->clone->getPixmapItem()->updatePixmap(); - this->combo_sprite->setCurrentText(this->clone->getGfx()); - this->clone->modify(); - }); + // This frame type displays map names, so when a new map is created we need to repopulate it. + connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection); +} + +void CloneObjectFrame::tryInvalidateIdDropdown(Map *map) { + // If the clone's target map is opened then the names in this frame's ID dropdown may be changed. + // Make sure we update the frame next time it's opened. + if (map && this->clone && map->name() == this->clone->getTargetMap()) { + invalidateValues(); + } } void CloneObjectFrame::initialize() { @@ -474,13 +548,14 @@ void CloneObjectFrame::initialize() { const QSignalBlocker blocker(this); EventFrame::initialize(); + // local id + this->line_edit_local_id->setText(this->clone->getIdName()); + // sprite this->combo_sprite->setCurrentText(this->clone->getGfx()); // target id - this->spinner_target_id->setMinimum(1); - this->spinner_target_id->setMaximum(126); - this->spinner_target_id->setValue(this->clone->getTargetID()); + this->combo_target_id->setCurrentText(this->clone->getTargetID()); // target map this->combo_target_map->setTextItem(this->clone->getTargetMap()); @@ -492,13 +567,22 @@ void CloneObjectFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_target_map->addItems(project->mapNames); + populateDropdown(this->combo_target_map, project->mapNames); + populateIdNameDropdown(this->combo_target_id, project, this->clone->getTargetMap(), Event::Group::Object); } void WarpFrame::setup() { EventFrame::setup(); - this->label_id->setText("Warp"); + // ID + QFormLayout *l_form_id = new QFormLayout(); + this->line_edit_id = new QLineEdit(this); + static const QString line_edit_id_toolTip = Util::toHtmlParagraph("An optional, unique name to use to refer to this warp from other warps. " + "If no name is given you can refer to this warp using its 'warp id' number."); + this->line_edit_id->setToolTip(line_edit_id_toolTip); + this->line_edit_id->setPlaceholderText("WARP_ID_MY_WARP"); + l_form_id->addRow("ID", this->line_edit_id); + this->layout_contents->addLayout(l_form_id); // desination map combo QFormLayout *l_form_dest_map = new QFormLayout(); @@ -534,13 +618,23 @@ void WarpFrame::connectSignals(MainWindow *window) { if (this->connected) return; EventFrame::connectSignals(window); + Project *project = window->editor->project; + + // id + this->line_edit_id->disconnect(); + connect(this->line_edit_id, &QLineEdit::textChanged, [this](const QString &text) { + this->warp->setIdName(text); + this->warp->modify(); + }); // dest map this->combo_dest_map->disconnect(); - connect(this->combo_dest_map, &QComboBox::currentTextChanged, [this](const QString &text) { - this->warp->setDestinationMap(text); + connect(this->combo_dest_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) { + this->warp->setDestinationMap(mapName); this->warp->modify(); + populateIdNameDropdown(this->combo_dest_warp, project, mapName, Event::Group::Warp); }); + connect(window, &MainWindow::mapOpened, this, &WarpFrame::tryInvalidateIdDropdown, Qt::UniqueConnection); // dest id this->combo_dest_warp->disconnect(); @@ -552,6 +646,17 @@ void WarpFrame::connectSignals(MainWindow *window) { // warning this->warning->disconnect(); connect(this->warning, &QPushButton::clicked, window, &MainWindow::onWarpBehaviorWarningClicked); + + // This frame type displays map names, so when a new map is created we need to repopulate it. + connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection); +} + +void WarpFrame::tryInvalidateIdDropdown(Map *map) { + // If the warps's target map is opened then the names in this frame's ID dropdown may be changed. + // Make sure we update the frame next time it's opened. + if (map && this->warp && map->name() == this->warp->getDestinationMap()) { + invalidateValues(); + } } void WarpFrame::initialize() { @@ -560,6 +665,9 @@ void WarpFrame::initialize() { const QSignalBlocker blocker(this); EventFrame::initialize(); + // id + this->line_edit_id->setText(this->warp->getIdName()); + // dest map this->combo_dest_map->setTextItem(this->warp->getDestinationMap()); @@ -573,7 +681,8 @@ void WarpFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_dest_map->addItems(project->mapNames); + populateDropdown(this->combo_dest_map, project->mapNames); + populateIdNameDropdown(this->combo_dest_warp, project, this->warp->getDestinationMap(), Event::Group::Warp); } @@ -581,8 +690,6 @@ void WarpFrame::populate(Project *project) { void TriggerFrame::setup() { EventFrame::setup(); - this->label_id->setText("Trigger"); - // script combo QFormLayout *l_form_script = new QFormLayout(); this->combo_script = new NoScrollComboBox(this); @@ -661,10 +768,8 @@ void TriggerFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - // var combo - this->combo_var->addItems(project->varNames); - - this->populateScriptDropdown(this->combo_script, project); + populateDropdown(this->combo_var, project->varNames); + populateScriptDropdown(this->combo_script, project); } @@ -672,8 +777,6 @@ void TriggerFrame::populate(Project *project) { void WeatherTriggerFrame::setup() { EventFrame::setup(); - this->label_id->setText("Weather Trigger"); - // weather combo QFormLayout *l_form_weather = new QFormLayout(); this->combo_weather = new NoScrollComboBox(this); @@ -715,8 +818,7 @@ void WeatherTriggerFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - // weather - this->combo_weather->addItems(project->coordEventWeatherNames); + populateDropdown(this->combo_weather, project->coordEventWeatherNames); } @@ -724,8 +826,6 @@ void WeatherTriggerFrame::populate(Project *project) { void SignFrame::setup() { EventFrame::setup(); - this->label_id->setText("Sign"); - // facing dir combo QFormLayout *l_form_facing_dir = new QFormLayout(); this->combo_facing_dir = new NoScrollComboBox(this); @@ -785,10 +885,8 @@ void SignFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - // facing dir - this->combo_facing_dir->addItems(project->bgEventFacingDirections); - - this->populateScriptDropdown(this->combo_script, project); + populateDropdown(this->combo_facing_dir, project->bgEventFacingDirections); + populateScriptDropdown(this->combo_script, project); } @@ -796,8 +894,6 @@ void SignFrame::populate(Project *project) { void HiddenItemFrame::setup() { EventFrame::setup(); - this->label_id->setText("Hidden Item"); - // item combo QFormLayout *l_form_item = new QFormLayout(); this->combo_item = new NoScrollComboBox(this); @@ -905,8 +1001,8 @@ void HiddenItemFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_item->addItems(project->itemNames); - this->combo_flag->addItems(project->flagNames); + populateDropdown(this->combo_item, project->itemNames); + populateDropdown(this->combo_flag, project->flagNames); } @@ -914,8 +1010,6 @@ void HiddenItemFrame::populate(Project *project) { void SecretBaseFrame::setup() { EventFrame::setup(); - this->label_id->setText("Secret Base"); - this->spinner_z->setEnabled(false); // item combo @@ -959,7 +1053,7 @@ void SecretBaseFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_base_id->addItems(project->secretBaseIds); + populateDropdown(this->combo_base_id, project->secretBaseIds); } @@ -967,8 +1061,6 @@ void SecretBaseFrame::populate(Project *project) { void HealLocationFrame::setup() { EventFrame::setup(); - this->label_id->setText("Heal Location"); - this->hideable_label_z->setVisible(false); this->spinner_z->setVisible(false); @@ -996,7 +1088,8 @@ void HealLocationFrame::setup() { QFormLayout *l_form_respawn_npc = new QFormLayout(hideable_respawn_npc); l_form_respawn_npc->setContentsMargins(0, 0, 0, 0); this->combo_respawn_npc = new NoScrollComboBox(hideable_respawn_npc); - static const QString combo_respawn_npc_toolTip = Util::toHtmlParagraph("event_object ID of the NPC the player interacts with upon respawning after whiteout."); + static const QString combo_respawn_npc_toolTip = Util::toHtmlParagraph("The Local ID name or number of the NPC the player " + "interacts with upon respawning after whiteout."); this->combo_respawn_npc->setToolTip(combo_respawn_npc_toolTip); l_form_respawn_npc->addRow("Respawn NPC", this->combo_respawn_npc); this->layout_contents->addWidget(hideable_respawn_npc); @@ -1009,6 +1102,7 @@ void HealLocationFrame::connectSignals(MainWindow *window) { if (this->connected) return; EventFrame::connectSignals(window); + Project *project = window->editor->project; this->line_edit_id->disconnect(); connect(this->line_edit_id, &QLineEdit::textChanged, [this](const QString &text) { @@ -1017,16 +1111,29 @@ void HealLocationFrame::connectSignals(MainWindow *window) { }); this->combo_respawn_map->disconnect(); - connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this](const QString &text) { - this->healLocation->setRespawnMapName(text); + connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) { + this->healLocation->setRespawnMapName(mapName); this->healLocation->modify(); + populateIdNameDropdown(this->combo_respawn_npc, project, mapName, Event::Group::Object); }); + connect(window, &MainWindow::mapOpened, this, &HealLocationFrame::tryInvalidateIdDropdown, Qt::UniqueConnection); this->combo_respawn_npc->disconnect(); connect(this->combo_respawn_npc, &QComboBox::currentTextChanged, [this](const QString &text) { this->healLocation->setRespawnNPC(text); this->healLocation->modify(); }); + + // This frame type displays map names, so when a new map is created we need to repopulate it. + connect(project, &Project::mapCreated, this, &EventFrame::invalidateValues, Qt::UniqueConnection); +} + +void HealLocationFrame::tryInvalidateIdDropdown(Map *map) { + // If the heal locations's target map is opened then the names in this frame's ID dropdown may be changed. + // Make sure we update the frame next time it's opened. + if (map && this->healLocation && map->name() == this->healLocation->getRespawnMapName()) { + invalidateValues(); + } } void HealLocationFrame::initialize() { @@ -1050,7 +1157,8 @@ void HealLocationFrame::populate(Project *project) { const QSignalBlocker blocker(this); EventFrame::populate(project); - this->combo_respawn_map->addItems(project->mapNames); - // TODO: We should dynamically populate combo_respawn_npc with the local IDs of the respawn_map - // Same for warp IDs. + if (projectConfig.healLocationRespawnDataEnabled) { + populateDropdown(this->combo_respawn_map, project->mapNames); + populateIdNameDropdown(this->combo_respawn_npc, project, this->healLocation->getRespawnMapName(), Event::Group::Object); + } } diff --git a/src/ui/eventpixmapitem.cpp b/src/ui/eventpixmapitem.cpp new file mode 100644 index 00000000..bc7bb964 --- /dev/null +++ b/src/ui/eventpixmapitem.cpp @@ -0,0 +1,115 @@ +#include "eventpixmapitem.h" +#include "project.h" +#include "editcommands.h" +#include "mapruler.h" +#include "metatile.h" + +EventPixmapItem::EventPixmapItem(Event *event) + : QGraphicsPixmapItem(event->getPixmap()), + m_basePixmap(pixmap()), + m_event(event) +{ + m_event->setPixmapItem(this); + updatePixelPosition(); +} + +void EventPixmapItem::render(Project *project) { + if (!m_event) + return; + + m_basePixmap = m_event->loadPixmap(project); + + // If the base pixmap changes, the event's pixel position may change. + updatePixelPosition(); + + QPixmap pixmap = m_basePixmap; + if (m_selected) { + // Draw the selection rectangle + QPainter painter(&pixmap); + painter.setPen(Qt::magenta); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + } + setPixmap(pixmap); + emit rendered(m_basePixmap); +} + +void EventPixmapItem::move(int dx, int dy) { + moveTo(m_event->getX() + dx, + m_event->getY() + dy); +} + +void EventPixmapItem::moveTo(const QPoint &pos) { + moveTo(pos.x(), pos.y()); +} + +void EventPixmapItem::moveTo(int x, int y) { + bool changed = false; + if (m_event->getX() != x) { + m_event->setX(x); + emit xChanged(x); + changed = true; + } + if (m_event->getY() != y) { + m_event->setY(y); + emit yChanged(y); + changed = true; + } + if (changed) { + updatePixelPosition(); + emit posChanged(x, y); + } +} + +void EventPixmapItem::updatePixelPosition() { + setPos(m_event->getPixelX(), m_event->getPixelY()); +} + +void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { + if (m_active) + return; + m_active = true; + m_lastPos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); + + bool selectionToggle = mouseEvent->modifiers() & Qt::ControlModifier; + if (selectionToggle || !m_selected) { + // User is either toggling this selection on/off as part of a group selection, + // or they're newly selecting just this item. + m_selected = (selectionToggle) ? !m_selected : true; + emit selected(m_event, selectionToggle); + } else { + // This item is already selected and the user isn't toggling the selection, so there are 4 possibilities: + // 1. This is the only selected event, and the selection is pointless. + // 2. This is the only selected event, and they want to drag the item around. + // 3. There's a group selection, and they want to start a new selection with just this item. + // 4. There's a group selection, and they want to drag the group around. + // 'selectMapEvent' will immediately clear the rest of the selection, which supports #1-3 but prevents #4. + // To support #4 we set the flag below, and we only call 'selectMapEvent' on mouse release if no move occurred. + m_releaseSelectionQueued = true; + } + mouseEvent->accept(); +} + +void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { + if (!m_active || !m_selected) + return; + + QPoint pos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); + if (pos == m_lastPos) + return; + + m_releaseSelectionQueued = false; + emit dragged(m_event, m_lastPos, pos); + m_lastPos = pos; +} + +void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { + if (!m_active) + return; + m_active = false; + if (m_releaseSelectionQueued) { + m_releaseSelectionQueued = false; + if (Metatile::coordFromPixmapCoord(mouseEvent->scenePos()) == m_lastPos) + emit selected(m_event, false); + } + emit released(m_event, m_lastPos); +} diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index 1297102e..c8fc4495 100644 --- a/src/ui/graphicsview.cpp +++ b/src/ui/graphicsview.cpp @@ -2,22 +2,7 @@ #include "mapview.h" #include "editor.h" -void GraphicsView::mousePressEvent(QMouseEvent *event) { - QGraphicsView::mousePressEvent(event); - if (editor) { - editor->eventsView_onMousePress(event); - } -} - -void GraphicsView::mouseMoveEvent(QMouseEvent *event) { - QGraphicsView::mouseMoveEvent(event); -} - -void GraphicsView::mouseReleaseEvent(QMouseEvent *event) { - QGraphicsView::mouseReleaseEvent(event); -} - -void GraphicsView::moveEvent(QMoveEvent *event) { +void MapView::moveEvent(QMoveEvent *event) { QGraphicsView::moveEvent(event); QLabel *label_MapRulerStatus = findChild("label_MapRulerStatus", Qt::FindDirectChildrenOnly); if (label_MapRulerStatus && label_MapRulerStatus->isVisible()) diff --git a/src/ui/layoutpixmapitem.cpp b/src/ui/layoutpixmapitem.cpp index f53cf275..3417a4ce 100644 --- a/src/ui/layoutpixmapitem.cpp +++ b/src/ui/layoutpixmapitem.cpp @@ -714,19 +714,20 @@ void LayoutPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { } void LayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - this->paint_tile_initial_x = this->straight_path_initial_x = pos.x(); - this->paint_tile_initial_y = this->straight_path_initial_y = pos.y(); + this->metatilePos = Metatile::coordFromPixmapCoord(event->pos()); + this->paint_tile_initial_x = this->straight_path_initial_x = this->metatilePos.x(); + this->paint_tile_initial_y = this->straight_path_initial_y = this->metatilePos.y(); emit startPaint(event, this); emit mouseEvent(event, this); } void LayoutPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); - if (pos != this->metatilePos) { - this->metatilePos = pos; - emit this->hoveredMapMetatileChanged(pos); - } + if (pos == this->metatilePos) + return; + + this->metatilePos = pos; + emit hoveredMapMetatileChanged(pos); emit mouseEvent(event, this); } diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 76c124d6..732d3e83 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -110,8 +110,7 @@ void MapImageExporter::setModeSpecificUi() { } if (m_mode == ImageExporterMode::Timelapse) { - // TODO: At the moment edit history for events (and the DraggablePixmapItem class) - // explicitly depend on the editor and assume their map is currently open. + // TODO: At the moment edit history for events explicitly depend on the editor and assume their map is currently open. // Other edit commands rely on this more subtly, like triggering API callbacks or // spending time rendering their layout (which can make creating timelapses very slow). // Until this is resolved, the selected map/layout must remain the same as in the editor. diff --git a/src/ui/movablerect.cpp b/src/ui/movablerect.cpp index ade48afd..e02222df 100644 --- a/src/ui/movablerect.cpp +++ b/src/ui/movablerect.cpp @@ -40,7 +40,6 @@ ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int heig : QObject(parent), MovableRect(enabled, QRect(0, 0, width * 16, height * 16), color) { - setZValue(0xFFFFFFFF); // ensure on top of view setAcceptHoverEvents(true); setFlags(this->flags() | QGraphicsItem::ItemIsMovable); } diff --git a/src/ui/resizelayoutpopup.cpp b/src/ui/resizelayoutpopup.cpp index 00790ca8..2dc78d47 100644 --- a/src/ui/resizelayoutpopup.cpp +++ b/src/ui/resizelayoutpopup.cpp @@ -139,6 +139,7 @@ void ResizeLayoutPopup::setupLayoutView() { static bool layoutSizeRectVisible = true; this->outline = new ResizableRect(this, &layoutSizeRectVisible, this->editor->layout->getWidth(), this->editor->layout->getHeight(), qRgb(255, 0, 255)); + this->outline->setZValue(Editor::ZValue::ResizeLayoutPopup); // Ensure on top of view this->outline->setLimit(cover->rect().toAlignedRect()); connect(outline, &ResizableRect::rectUpdated, [=](QRect rect){ // Note: this extra limit check needs access to the project values, so it is done here and not ResizableRect::mouseMoveEvent