From ecad60843c9245ce2ceb885d30d91763a9de3e37 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 7 Apr 2025 10:41:57 -0400 Subject: [PATCH 01/14] Remove old DraggablePixmapItem signals/slots --- include/ui/draggablepixmapitem.h | 24 ++++-------------------- src/ui/draggablepixmapitem.cpp | 1 - 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/include/ui/draggablepixmapitem.h b/include/ui/draggablepixmapitem.h index aeda4daf..debf1e41 100644 --- a/include/ui/draggablepixmapitem.h +++ b/include/ui/draggablepixmapitem.h @@ -42,29 +42,13 @@ 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*); + virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override; }; #endif // DRAGGABLEPIXMAPITEM_H diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index f2fdeb91..31068fee 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -23,7 +23,6 @@ void DraggablePixmapItem::updatePosition() { void DraggablePixmapItem::emitPositionChanged() { emit xChanged(event->getX()); emit yChanged(event->getY()); - emit elevationChanged(event->getElevation()); } void DraggablePixmapItem::updatePixmap() { From 2d827f62f786f2de2b8ad7a4e08a41a23dc01fcc Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 7 Apr 2025 11:44:54 -0400 Subject: [PATCH 02/14] Support event lookup by ID name --- include/core/events.h | 6 ++-- include/core/map.h | 1 + include/editor.h | 2 +- include/mainwindow.h | 2 +- include/ui/draggablepixmapitem.h | 5 +-- src/core/events.cpp | 7 ++-- src/core/map.cpp | 15 +++++++++ src/editor.cpp | 1 + src/mainwindow.cpp | 57 ++++++++++++++++++++++++++------ src/project.cpp | 11 ++++-- src/ui/draggablepixmapitem.cpp | 28 ---------------- src/ui/eventframes.cpp | 5 ++- 12 files changed, 88 insertions(+), 52 deletions(-) diff --git a/include/core/events.h b/include/core/events.h index 22b76434..cff233cc 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -300,12 +300,12 @@ public: 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 c2078134..a3bb1bbf 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -76,6 +76,7 @@ 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; 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 4c7bff02..b56f42d4 100644 --- a/include/editor.h +++ b/include/editor.h @@ -251,11 +251,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 d8fe18a3..e3cdd62f 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -177,7 +177,7 @@ private slots: void on_action_Save_Project_triggered(); void 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); diff --git a/include/ui/draggablepixmapitem.h b/include/ui/draggablepixmapitem.h index debf1e41..5c617099 100644 --- a/include/ui/draggablepixmapitem.h +++ b/include/ui/draggablepixmapitem.h @@ -42,13 +42,14 @@ signals: void positionChanged(Event *event); void xChanged(int); void yChanged(int); - void spriteChanged(QPixmap pixmap); + void spriteChanged(const QPixmap &pixmap); + 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; + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(this->event); } }; #endif // DRAGGABLEPIXMAPITEM_H diff --git a/src/core/events.cpp b/src/core/events.cpp index ebb1ac49..e132f277 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -275,7 +275,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"); @@ -289,7 +289,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()); } @@ -308,9 +308,8 @@ QSet CloneObjectEvent::getExpectedFields() { void 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 diff --git a/src/core/map.cpp b/src/core/map.cpp index 330132a1..5e5d443f 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -194,6 +194,21 @@ 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 { + 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; +} + 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 19acbe60..282ec52c 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1699,6 +1699,7 @@ void Editor::displayMapEvents() { DraggablePixmapItem *Editor::addEventPixmapItem(Event *event) { this->project->loadEventPixmap(event); auto item = new DraggablePixmapItem(event, this); + connect(item, &DraggablePixmapItem::doubleClicked, this, &Editor::openEventMap); redrawEventPixmapItem(item); this->events_group->addToGroup(item); return item; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 28d7f996..237ca5c4 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -338,7 +338,7 @@ 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::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged); @@ -1055,19 +1055,56 @@ 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); + QString baseId = base->getBaseID(); + targetMapName = this->editor->project->mapConstantsToMapNames.value(mapPrefix + baseId.left(baseId.lastIndexOf("_"))); + 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); + // Map opened successfully, now try to select the targeted event on that map. + Event* targetEvent = this->editor->map->getEvent(targetEventGroup, targetEventIdName); + if (targetEvent) { + this->editor->selectMapEvent(targetEvent); } 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)); + logWarn(QString("%1 '%2' doesn't exist on map '%3'").arg(Event::groupToString(targetEventGroup)).arg(targetEventIdName).arg(targetMapName)); } } diff --git a/src/project.cpp b/src/project.cpp index 6f225ff6..e02db858 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -162,8 +162,10 @@ void Project::clearTilesetCache() { Map* Project::loadMap(const QString &mapName) { Map* map = this->maps.value(mapName); - if (!map) + if (!map) { + logError(QString("Unknown map name '%1'.").arg(mapName)); return nullptr; + } if (isMapLoaded(map)) return map; @@ -445,7 +447,12 @@ bool Project::loadLayout(Layout *layout) { Layout *Project::loadLayout(QString layoutId) { Layout *layout = this->mapLayouts.value(layoutId); - if (!layout || !loadLayout(layout)) { + if (!layout) { + logError(QString("Unknown layout ID '%1'.").arg(layoutId)); + return nullptr; + } + + if (!loadLayout(layout)) { logError(QString("Failed to load layout '%1'").arg(layoutId)); return nullptr; } diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index 31068fee..53cdd90a 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -103,31 +103,3 @@ void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { 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 82334722..c69d35da 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -453,13 +453,16 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { }); // target id + // TODO: Replace spinner with combo box populated with local IDs from target map. 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(); }); + */ } void CloneObjectFrame::initialize() { @@ -474,7 +477,7 @@ void CloneObjectFrame::initialize() { // target id this->spinner_target_id->setMinimum(1); this->spinner_target_id->setMaximum(126); - this->spinner_target_id->setValue(this->clone->getTargetID()); + //this->spinner_target_id->setValue(this->clone->getTargetID()); // target map this->combo_target_map->setTextItem(this->clone->getTargetMap()); From 2256ded6c285f2d12eeefe99b7a6e3cd50b42862 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 7 Apr 2025 12:31:45 -0400 Subject: [PATCH 03/14] Some event frame updates for local IDs --- include/ui/eventframes.h | 2 +- src/core/events.cpp | 2 +- src/core/map.cpp | 3 +++ src/mainwindow.cpp | 5 +++++ src/ui/eventframes.cpp | 48 ++++++++++++++-------------------------- 5 files changed, 26 insertions(+), 34 deletions(-) diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h index 763d6baf..a5ae6765 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -109,7 +109,7 @@ public: public: NoScrollComboBox *combo_sprite; - NoScrollSpinBox *spinner_target_id; + NoScrollComboBox *combo_target_id; NoScrollComboBox *combo_target_map; private: diff --git a/src/core/events.cpp b/src/core/events.cpp index e132f277..8c5e5077 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -99,7 +99,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"}, diff --git a/src/core/map.cpp b/src/core/map.cpp index 5e5d443f..fa69427b 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -195,6 +195,9 @@ Event* Map::getEvent(Event::Group group, int index) const { } 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) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 237ca5c4..1ed4ca04 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1081,8 +1081,13 @@ void MainWindow::openEventMap(Event *sourceEvent) { // 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) { diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index c69d35da..ea0cccae 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -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); @@ -204,8 +205,6 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj void ObjectFrame::setup() { EventFrame::setup(); - this->label_id->setText("Object"); - // sprite combo QFormLayout *l_form_sprite = new QFormLayout(); this->combo_sprite = new NoScrollComboBox(this); @@ -406,8 +405,6 @@ void ObjectFrame::populate(Project *project) { void CloneObjectFrame::setup() { EventFrame::setup(); - this->label_id->setText("Clone Object"); - this->spinner_z->setEnabled(false); // sprite combo (edits disabled) @@ -424,11 +421,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); - this->spinner_target_id->setToolTip("event_object ID of the object being cloned."); - l_form_dest_id->addRow("Target Local ID", this->spinner_target_id); + this->combo_target_id = new NoScrollComboBox(this); + // TODO: Once object events have a real local ID input field, this tool tip should be updated to reflect the name of that field + this->combo_target_id->setToolTip("event_object ID of the object being cloned."); + l_form_dest_id->addRow("Target Local ID", this->combo_target_id); this->layout_contents->addLayout(l_form_dest_id); // custom attributes @@ -450,19 +448,17 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { this->clone->getPixmapItem()->updatePixmap(); this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); + // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. }); // target id - // TODO: Replace spinner with combo box populated with local IDs from target map. - this->spinner_target_id->disconnect(); - /* - connect(this->spinner_target_id, QOverload::of(&QSpinBox::valueChanged), [this](int value) { - this->clone->setTargetID(value); + this->combo_target_id->disconnect(); + connect(this->combo_target_id, &QComboBox::currentTextChanged, [this](const QString &text) { + this->clone->setTargetID(text); this->clone->getPixmapItem()->updatePixmap(); this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); }); - */ } void CloneObjectFrame::initialize() { @@ -475,9 +471,7 @@ void CloneObjectFrame::initialize() { 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()); @@ -490,13 +484,12 @@ void CloneObjectFrame::populate(Project *project) { EventFrame::populate(project); this->combo_target_map->addItems(project->mapNames); + // TODO: Populate combo_target_id with local IDs from target map. } void WarpFrame::setup() { EventFrame::setup(); - this->label_id->setText("Warp"); - // desination map combo QFormLayout *l_form_dest_map = new QFormLayout(); this->combo_dest_map = new NoScrollComboBox(this); @@ -537,6 +530,7 @@ void WarpFrame::connectSignals(MainWindow *window) { connect(this->combo_dest_map, &QComboBox::currentTextChanged, [this](const QString &text) { this->warp->setDestinationMap(text); this->warp->modify(); + // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. }); // dest id @@ -571,6 +565,7 @@ void WarpFrame::populate(Project *project) { EventFrame::populate(project); this->combo_dest_map->addItems(project->mapNames); + // TODO: Populate combo_dest_warp with local IDs from target map. } @@ -578,8 +573,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); @@ -666,8 +659,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); @@ -717,8 +708,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); @@ -788,8 +777,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); @@ -902,8 +889,6 @@ void HiddenItemFrame::populate(Project *project) { void SecretBaseFrame::setup() { EventFrame::setup(); - this->label_id->setText("Secret Base"); - this->spinner_z->setEnabled(false); // item combo @@ -955,8 +940,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); @@ -982,6 +965,7 @@ 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); + // TODO: Once object events have a real local ID input field, this tool tip should be updated to reflect the name of that field this->combo_respawn_npc->setToolTip("event_object ID of the NPC the player interacts with\n" "upon respawning after whiteout."); l_form_respawn_npc->addRow("Respawn NPC", this->combo_respawn_npc); @@ -1006,6 +990,7 @@ void HealLocationFrame::connectSignals(MainWindow *window) { connect(this->combo_respawn_map, &QComboBox::currentTextChanged, [this](const QString &text) { this->healLocation->setRespawnMapName(text); this->healLocation->modify(); + // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. }); this->combo_respawn_npc->disconnect(); @@ -1038,5 +1023,4 @@ void HealLocationFrame::populate(Project *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. } From 17949055f6d2fc364da091a5ace49619759fd0fe Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 7 Apr 2025 13:49:26 -0400 Subject: [PATCH 04/14] Fix local ID being reordered in output JSON --- src/core/events.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/events.cpp b/src/core/events.cpp index 8c5e5077..2dce4192 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -146,12 +146,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(); @@ -255,10 +256,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(); From 374a2b67b83a985ef038546fde1685fbb89c75ae Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 4 Apr 2025 23:33:38 -0400 Subject: [PATCH 05/14] Remove some redundant event pixmap loading --- src/editor.cpp | 49 +++++++++++++++++++--------------- src/ui/draggablepixmapitem.cpp | 7 ----- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index 282ec52c..9afc761b 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1987,31 +1987,36 @@ qreal Editor::getEventOpacity(const Event *event) const { } 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); + if (!item || !item->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); + project->loadEventPixmap(item->event, true); + + QPixmap pixmap = item->event->getPixmap(); + if (pixmap.isNull()) + return; + + qreal zValue = item->event->getY(); + if (this->editMode == EditMode::Events) { + if (this->selectedEvents.contains(item->event)) { + // Draw the selection rectangle + QPainter painter(&pixmap); + painter.setPen(Qt::magenta); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + zValue++; } - item->updatePosition(); + 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->setPixmap(pixmap); + item->setZValue(zValue); + item->setOpacity(getEventOpacity(item->event)); + item->setShapeMode(porymapConfig.eventSelectionShapeMode); + item->updatePosition(); } // Warp events display a warning if they're not positioned on a metatile with a warp behavior. diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index 53cdd90a..a73a7ace 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -12,11 +12,6 @@ void DraggablePixmapItem::updatePosition() { 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); } @@ -26,8 +21,6 @@ void DraggablePixmapItem::emitPositionChanged() { } void DraggablePixmapItem::updatePixmap() { - editor->project->loadEventPixmap(event, true); - this->updatePosition(); editor->redrawEventPixmapItem(this); emit spriteChanged(event->getPixmap()); } From c53e9fcb284bafe9e77e33c5cadd1ce5243a839d Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 7 Apr 2025 21:11:58 -0400 Subject: [PATCH 06/14] Remove incorrect comment --- src/editor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/editor.cpp b/src/editor.cpp index a18e59da..93085d63 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1673,9 +1673,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; } From 714cce670fdbb2682e3f49f8402f10635ee02d8a Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 8 Apr 2025 12:42:23 -0400 Subject: [PATCH 07/14] DraggaglePixmapItem -> EventPixmapItem --- include/core/editcommands.h | 2 +- include/core/events.h | 8 ++++---- include/editor.h | 6 +++--- ...draggablepixmapitem.h => eventpixmapitem.h} | 12 ++++++------ porymap.pro | 4 ++-- src/core/editcommands.cpp | 2 +- src/core/events.cpp | 2 +- src/editor.cpp | 14 +++++++------- src/mainwindow.cpp | 2 +- src/ui/eventframes.cpp | 10 +++++----- ...gablepixmapitem.cpp => eventpixmapitem.cpp} | 18 +++++++++--------- src/ui/mapimageexporter.cpp | 2 +- 12 files changed, 41 insertions(+), 41 deletions(-) rename include/ui/{draggablepixmapitem.h => eventpixmapitem.h} (77%) rename src/ui/{draggablepixmapitem.cpp => eventpixmapitem.cpp} (85%) 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 2b2f5e40..3963e022 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; @@ -154,8 +154,8 @@ public: 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 +194,7 @@ protected: QJsonObject customAttributes; QPixmap pixmap; - DraggablePixmapItem *pixmapItem = nullptr; + EventPixmapItem *pixmapItem = nullptr; QPointer eventFrame; diff --git a/include/editor.h b/include/editor.h index a4d3d6c3..60ead193 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 @@ -107,7 +107,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); @@ -116,7 +116,7 @@ public: void duplicateSelectedEvents(); void redrawAllEvents(); void redrawEvents(const QList &events); - void redrawEventPixmapItem(DraggablePixmapItem *item); + void redrawEventPixmapItem(EventPixmapItem *item); qreal getEventOpacity(const Event *event) const; void updateCursorRectPos(int x, int y); diff --git a/include/ui/draggablepixmapitem.h b/include/ui/eventpixmapitem.h similarity index 77% rename from include/ui/draggablepixmapitem.h rename to include/ui/eventpixmapitem.h index 5c617099..18813bc0 100644 --- a/include/ui/draggablepixmapitem.h +++ b/include/ui/eventpixmapitem.h @@ -1,5 +1,5 @@ -#ifndef DRAGGABLEPIXMAPITEM_H -#define DRAGGABLEPIXMAPITEM_H +#ifndef EVENTPIXMAPITEM_H +#define EVENTPIXMAPITEM_H #include #include @@ -12,12 +12,12 @@ class Editor; -class DraggablePixmapItem : public QObject, public QGraphicsPixmapItem { +class EventPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: - DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {} + EventPixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {} - DraggablePixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) { + EventPixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) { this->event = event; event->setPixmapItem(this); this->editor = editor; @@ -52,4 +52,4 @@ protected: virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(this->event); } }; -#endif // DRAGGABLEPIXMAPITEM_H +#endif // EVENTPIXMAPITEM_H diff --git a/porymap.pro b/porymap.pro index 35fd6af2..7374ac30 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 fbd4e568..694919c0 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -34,7 +34,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(); diff --git a/src/editor.cpp b/src/editor.cpp index 93085d63..f618361c 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" @@ -1692,10 +1692,10 @@ 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); - connect(item, &DraggablePixmapItem::doubleClicked, this, &Editor::openEventMap); + auto item = new EventPixmapItem(event, this); + connect(item, &EventPixmapItem::doubleClicked, this, &Editor::openEventMap); redrawEventPixmapItem(item); this->events_group->addToGroup(item); return item; @@ -1971,7 +1971,7 @@ qreal Editor::getEventOpacity(const Event *event) const { return event->getUsesDefaultPixmap() ? 0.7 : 1.0; } -void Editor::redrawEventPixmapItem(DraggablePixmapItem *item) { +void Editor::redrawEventPixmapItem(EventPixmapItem *item) { if (!item || !item->event) return; @@ -2287,8 +2287,8 @@ bool Editor::startDetachedProcess(const QString &command, const QString &working } // 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 +// from triggering both event's EventPixmapItem and the background mousePress. +// Since the EventPixmapItem'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) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9bea0f4a..67f48be7 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" diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index d392017b..c8682aa7 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; @@ -114,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) { @@ -123,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) { @@ -297,7 +297,7 @@ void ObjectFrame::connectSignals(MainWindow *window) { this->object->getPixmapItem()->updatePixmap(); this->object->modify(); }); - connect(this->object->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); + connect(this->object->getPixmapItem(), &EventPixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); // movement this->combo_movement->disconnect(); @@ -439,7 +439,7 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { EventFrame::connectSignals(window); // update icon displayed in frame with target - connect(this->clone->getPixmapItem(), &DraggablePixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); + connect(this->clone->getPixmapItem(), &EventPixmapItem::spriteChanged, this->label_icon, &QLabel::setPixmap); // target map this->combo_target_map->disconnect(); diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/eventpixmapitem.cpp similarity index 85% rename from src/ui/draggablepixmapitem.cpp rename to src/ui/eventpixmapitem.cpp index a73a7ace..cc0d76e1 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/eventpixmapitem.cpp @@ -1,4 +1,4 @@ -#include "draggablepixmapitem.h" +#include "eventpixmapitem.h" #include "editor.h" #include "editcommands.h" #include "mapruler.h" @@ -7,7 +7,7 @@ static unsigned currentActionId = 0; -void DraggablePixmapItem::updatePosition() { +void EventPixmapItem::updatePosition() { int x = this->event->getPixelX(); int y = this->event->getPixelY(); setX(x); @@ -15,17 +15,17 @@ void DraggablePixmapItem::updatePosition() { editor->updateWarpEventWarning(event); } -void DraggablePixmapItem::emitPositionChanged() { +void EventPixmapItem::emitPositionChanged() { emit xChanged(event->getX()); emit yChanged(event->getY()); } -void DraggablePixmapItem::updatePixmap() { +void EventPixmapItem::updatePixmap() { editor->redrawEventPixmapItem(this); emit spriteChanged(event->getPixmap()); } -void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { if (this->active) return; this->active = true; @@ -49,21 +49,21 @@ void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { this->editor->selectingEvent = true; } -void DraggablePixmapItem::move(int dx, int dy) { +void EventPixmapItem::move(int dx, int dy) { event->setX(event->getX() + dx); event->setY(event->getY() + dy); updatePosition(); emitPositionChanged(); } -void DraggablePixmapItem::moveTo(const QPoint &pos) { +void EventPixmapItem::moveTo(const QPoint &pos) { event->setX(pos.x()); event->setY(pos.y()); updatePosition(); emitPositionChanged(); } -void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { if (!this->active) return; @@ -85,7 +85,7 @@ void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { this->releaseSelectionQueued = false; } -void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { if (!this->active) return; this->active = false; diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 39607bca..fe2865b0 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -96,7 +96,7 @@ void MapImageExporter::setModeSpecificUi() { } if (m_mode == ImageExporterMode::Timelapse) { - // TODO: At the moment edit history for events (and the DraggablePixmapItem class) + // TODO: At the moment edit history for events (and the EventPixmapItem class) // 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). From 3d47d6b7e71c594a843c3406e2efff1674abb383 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 8 Apr 2025 13:12:10 -0400 Subject: [PATCH 08/14] Remove some Editor usage from EventPixmapItem --- include/editor.h | 7 ++-- include/ui/eventpixmapitem.h | 4 ++- include/ui/graphicsview.h | 21 ------------ include/ui/mapview.h | 11 +++++-- src/editor.cpp | 59 +++++++++++++++++----------------- src/ui/eventpixmapitem.cpp | 62 +++++++++++++++--------------------- src/ui/graphicsview.cpp | 17 +--------- src/ui/layoutpixmapitem.cpp | 15 +++++---- 8 files changed, 78 insertions(+), 118 deletions(-) diff --git a/include/editor.h b/include/editor.h index 60ead193..07a5dc68 100644 --- a/include/editor.h +++ b/include/editor.h @@ -122,6 +122,8 @@ public: 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(); @@ -172,10 +174,7 @@ public: static QList> collisionIcons; int eventShiftActionId = 0; - - void eventsView_onMousePress(QMouseEvent *event); - - bool selectingEvent = false; + int eventMoveActionId = 0; void deleteSelectedEvents(); void shouldReselectEvents(); diff --git a/include/ui/eventpixmapitem.h b/include/ui/eventpixmapitem.h index 18813bc0..c44fc6bf 100644 --- a/include/ui/eventpixmapitem.h +++ b/include/ui/eventpixmapitem.h @@ -39,10 +39,12 @@ private: bool releaseSelectionQueued = false; signals: - void positionChanged(Event *event); void xChanged(int); void yChanged(int); void spriteChanged(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: diff --git a/include/ui/graphicsview.h b/include/ui/graphicsview.h index 92771cf7..cac812b2 100644 --- a/include/ui/graphicsview.h +++ b/include/ui/graphicsview.h @@ -32,25 +32,4 @@ signals: void clicked(QMouseEvent *event); }; -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/src/editor.cpp b/src/editor.cpp index f618361c..cf9dd632 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1289,7 +1289,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; } @@ -1363,8 +1362,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; @@ -1696,6 +1698,9 @@ EventPixmapItem *Editor::addEventPixmapItem(Event *event) { this->project->loadEventPixmap(event); auto item = new EventPixmapItem(event, this); 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); redrawEventPixmapItem(item); this->events_group->addToGroup(item); return item; @@ -2004,6 +2009,28 @@ void Editor::redrawEventPixmapItem(EventPixmapItem *item) { item->updatePosition(); } +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. void Editor::updateWarpEventWarning(Event *event) { if (porymapConfig.warpBehaviorWarningDisabled) @@ -2286,32 +2313,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 EventPixmapItem and the background mousePress. -// Since the EventPixmapItem'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/ui/eventpixmapitem.cpp b/src/ui/eventpixmapitem.cpp index cc0d76e1..1face67c 100644 --- a/src/ui/eventpixmapitem.cpp +++ b/src/ui/eventpixmapitem.cpp @@ -4,8 +4,19 @@ #include "mapruler.h" #include "metatile.h" -static unsigned currentActionId = 0; +void EventPixmapItem::move(int dx, int dy) { + event->setX(event->getX() + dx); + event->setY(event->getY() + dy); + updatePosition(); + emitPositionChanged(); +} +void EventPixmapItem::moveTo(const QPoint &pos) { + event->setX(pos.x()); + event->setY(pos.y()); + updatePosition(); + emitPositionChanged(); +} void EventPixmapItem::updatePosition() { int x = this->event->getPixelX(); @@ -25,17 +36,17 @@ void EventPixmapItem::updatePixmap() { emit spriteChanged(event->getPixmap()); } -void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { if (this->active) return; this->active = true; - this->lastPos = Metatile::coordFromPixmapCoord(mouse->scenePos()); + this->lastPos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); - bool selectionToggle = mouse->modifiers() & Qt::ControlModifier; + bool selectionToggle = mouseEvent->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); + emit selected(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. @@ -46,53 +57,30 @@ void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { // 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; + mouseEvent->accept(); } -void EventPixmapItem::move(int dx, int dy) { - event->setX(event->getX() + dx); - event->setY(event->getY() + dy); - updatePosition(); - emitPositionChanged(); -} - -void EventPixmapItem::moveTo(const QPoint &pos) { - event->setX(pos.x()); - event->setY(pos.y()); - updatePosition(); - emitPositionChanged(); -} - -void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { if (!this->active) return; - QPoint pos = Metatile::coordFromPixmapCoord(mouse->scenePos()); + QPoint pos = Metatile::coordFromPixmapCoord(mouseEvent->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; + emit dragged(this->event, this->lastPos, pos); + this->lastPos = pos; } -void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { +void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { 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); + if (Metatile::coordFromPixmapCoord(mouseEvent->scenePos()) == this->lastPos) + emit selected(this->event, false); } + emit released(this->event, this->lastPos); } diff --git a/src/ui/graphicsview.cpp b/src/ui/graphicsview.cpp index 68479e98..6c4ccdb3 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); } From d992a29e3646df0d52885351770658a5d192bd42 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 17 Apr 2025 18:00:38 -0400 Subject: [PATCH 09/14] Add input fields for LOCALID --- include/core/events.h | 8 ++- include/core/map.h | 1 + include/ui/eventframes.h | 4 ++ src/core/map.cpp | 25 +++++++++ src/mainwindow.cpp | 9 +-- src/project.cpp | 5 ++ src/ui/eventframes.cpp | 116 +++++++++++++++++++++++++++++++++------ 7 files changed, 141 insertions(+), 27 deletions(-) diff --git a/include/core/events.h b/include/core/events.h index 3963e022..4a00c501 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -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) { diff --git a/include/core/map.h b/include/core/map.h index 07fce0e5..1f8b5da5 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -77,6 +77,7 @@ public: 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/ui/eventframes.h b/include/ui/eventframes.h index a5ae6765..0cbce712 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -58,6 +58,7 @@ protected: bool connected = false; void populateScriptDropdown(NoScrollComboBox * combo, Project * project); + void populateIdNameDropdown(NoScrollComboBox * combo, Project * project, const QString &mapName, Event::Group group); private: Event *event; @@ -78,6 +79,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,6 +110,7 @@ public: virtual void populate(Project *project) override; public: + QLineEdit *line_edit_local_id; NoScrollComboBox *combo_sprite; NoScrollComboBox *combo_target_id; NoScrollComboBox *combo_target_map; @@ -131,6 +134,7 @@ public: virtual void populate(Project *project) override; public: + QLineEdit *line_edit_id; NoScrollComboBox *combo_dest_map; NoScrollComboBox *combo_dest_warp; QPushButton *warning; diff --git a/src/core/map.cpp b/src/core/map.cpp index c61c1e8b..caff07db 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -212,6 +212,31 @@ Event* Map::getEvent(Event::Group group, const QString &idName) const { 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/mainwindow.cpp b/src/mainwindow.cpp index b8de0b63..f72c2c89 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1112,13 +1112,8 @@ void MainWindow::openEventMap(Event *sourceEvent) { return; // Map opened successfully, now try to select the targeted event on that map. - Event* targetEvent = this->editor->map->getEvent(targetEventGroup, targetEventIdName); - if (targetEvent) { - this->editor->selectMapEvent(targetEvent); - } 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(targetEventGroup)).arg(targetEventIdName).arg(targetMapName)); - } + Event *targetEvent = this->editor->map->getEvent(targetEventGroup, targetEventIdName); + this->editor->selectMapEvent(targetEvent); } void MainWindow::displayMapProperties() { diff --git a/src/project.cpp b/src/project.cpp index e02db858..300af774 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -161,6 +161,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/eventframes.cpp b/src/ui/eventframes.cpp index 3b5d71fa..cba5a78d 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -173,6 +173,11 @@ void EventFrame::setActive(bool active) { this->blockSignals(!active); } +// TODO: For populateScriptDropdown and populateIdNameDropdown, it would be nice to connect them to the source of their items +// and update them automatically when the source changes, i.e. for the script dropdown, invalidating the list when the +// the script file changes, and for the ID name dropdown invalidating the list when an event ID name chnages (or perhaps +// more simply invalidating it when the target map is opened). + 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()) @@ -201,10 +206,31 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj 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) + return; + + combo->clear(); + combo->addItems(map->getEventIdNames(group)); +} + void ObjectFrame::setup() { EventFrame::setup(); + // local id + QFormLayout *l_form_local_id = new QFormLayout(); + this->line_edit_local_id = new QLineEdit(this); + this->line_edit_local_id->setToolTip("An optional, unique name to use to refer to this object in scripts.\n" + "If no game is given you can refer to this object using its 'object id' number."); + 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(); this->combo_sprite = new NoScrollComboBox(this); @@ -290,6 +316,13 @@ void ObjectFrame::connectSignals(MainWindow *window) { EventFrame::connectSignals(window); + // 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) { @@ -361,6 +394,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()); @@ -407,9 +443,21 @@ void CloneObjectFrame::setup() { this->spinner_z->setEnabled(false); + // local id + QFormLayout *l_form_local_id = new QFormLayout(); + this->line_edit_local_id = new QLineEdit(this); + this->line_edit_local_id->setToolTip("An optional, unique name to use to refer to this object in scripts.\n" + "If no game is given you can refer to this object using its 'object id' number."); + 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); + this->combo_sprite->setToolTip("The sprite graphics to use for this object. This is updated automatically\n" + "to match the target object, and so can't be edited. By default the games\n" + "will get the graphics directly from the target object, so this field is ignored."); l_form_sprite->addRow("Sprite", this->combo_sprite); this->combo_sprite->setEnabled(false); this->layout_contents->addLayout(l_form_sprite); @@ -424,8 +472,7 @@ void CloneObjectFrame::setup() { // clone local id combo QFormLayout *l_form_dest_id = new QFormLayout(); this->combo_target_id = new NoScrollComboBox(this); - // TODO: Once object events have a real local ID input field, this tool tip should be updated to reflect the name of that field - this->combo_target_id->setToolTip("event_object ID of the object being cloned."); + this->combo_target_id->setToolTip("The Local ID name or number of the object being cloned."); l_form_dest_id->addRow("Target Local ID", this->combo_target_id); this->layout_contents->addLayout(l_form_dest_id); @@ -437,18 +484,26 @@ 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(), &EventPixmapItem::spriteChanged, 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); + connect(this->combo_target_map, &QComboBox::currentTextChanged, [this, project](const QString &mapName) { + this->clone->setTargetMap(mapName); this->clone->getPixmapItem()->updatePixmap(); this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); - // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. + populateIdNameDropdown(this->combo_target_id, project, mapName, Event::Group::Object); }); // target id @@ -467,6 +522,9 @@ 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()); @@ -484,12 +542,21 @@ void CloneObjectFrame::populate(Project *project) { EventFrame::populate(project); this->combo_target_map->addItems(project->mapNames); - // TODO: Populate combo_target_id with local IDs from target map. + populateIdNameDropdown(this->combo_target_id, project, this->clone->getTargetMap(), Event::Group::Object); } void WarpFrame::setup() { EventFrame::setup(); + // ID + QFormLayout *l_form_id = new QFormLayout(); + this->line_edit_id = new QLineEdit(this); + this->line_edit_id->setToolTip("An optional, unique name to use to refer to this warp from other warps.\n" + "If no game is given you can refer to this warp using its 'warp id' number."); + 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(); this->combo_dest_map = new NoScrollComboBox(this); @@ -524,13 +591,21 @@ 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(); - // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. + populateIdNameDropdown(this->combo_dest_warp, project, mapName, Event::Group::Warp); }); // dest id @@ -551,6 +626,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()); @@ -565,7 +643,7 @@ void WarpFrame::populate(Project *project) { EventFrame::populate(project); this->combo_dest_map->addItems(project->mapNames); - // TODO: Populate combo_dest_warp with local IDs from target map. + populateIdNameDropdown(this->combo_dest_warp, project, this->warp->getDestinationMap(), Event::Group::Warp); } @@ -965,9 +1043,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); - // TODO: Once object events have a real local ID input field, this tool tip should be updated to reflect the name of that field - this->combo_respawn_npc->setToolTip("event_object ID of the NPC the player interacts with\n" - "upon respawning after whiteout."); + this->combo_respawn_npc->setToolTip("The Local ID name or number of the NPC the player\n" + "interacts with upon respawning after whiteout."); l_form_respawn_npc->addRow("Respawn NPC", this->combo_respawn_npc); this->layout_contents->addWidget(hideable_respawn_npc); @@ -979,6 +1056,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) { @@ -987,10 +1065,10 @@ 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(); - // TODO: If this field changes to the name of a valid map then the available items in the ID combo box should be refreshed. + populateIdNameDropdown(this->combo_respawn_npc, project, mapName, Event::Group::Object); }); this->combo_respawn_npc->disconnect(); @@ -1021,6 +1099,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 + if (projectConfig.healLocationRespawnDataEnabled) { + this->combo_respawn_map->addItems(project->mapNames); + populateIdNameDropdown(this->combo_respawn_npc, project, this->healLocation->getRespawnMapName(), Event::Group::Object); + } } From 0f4028ab928c3e639b4d76440fbb5084c2eca1dd Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 18 Apr 2025 12:08:30 -0400 Subject: [PATCH 10/14] Add missing event frame invalidation --- include/mainwindow.h | 3 +- include/ui/eventframes.h | 7 +++ src/mainwindow.cpp | 14 ++---- src/ui/eventframes.cpp | 99 +++++++++++++++++++++++++++------------- 4 files changed, 79 insertions(+), 44 deletions(-) diff --git a/include/mainwindow.h b/include/mainwindow.h index 832d51ee..4d33847d 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -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/eventframes.h b/include/ui/eventframes.h index 0cbce712..09eae50b 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -57,6 +57,7 @@ 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); @@ -117,6 +118,8 @@ public: private: CloneObjectEvent *clone; + + void tryInvalidateIdDropdown(Map *map); }; @@ -141,6 +144,8 @@ public: private: WarpEvent *warp; + + void tryInvalidateIdDropdown(Map *map); }; @@ -279,6 +284,8 @@ public: private: HealLocationEvent *healLocation; + + void tryInvalidateIdDropdown(Map *map); }; #endif // EVENTRAMES_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f72c2c89..e44b967d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -343,7 +343,7 @@ void MainWindow::initEditor() { connect(this->editor, &Editor::openConnectedMap, this, &MainWindow::onOpenConnectedMap); 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); @@ -523,11 +523,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); @@ -949,8 +945,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); @@ -1153,7 +1147,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() { @@ -2520,7 +2514,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/ui/eventframes.cpp b/src/ui/eventframes.cpp index cba5a78d..40b2cdf2 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -173,10 +173,16 @@ void EventFrame::setActive(bool active) { this->blockSignals(!active); } -// TODO: For populateScriptDropdown and populateIdNameDropdown, it would be nice to connect them to the source of their items -// and update them automatically when the source changes, i.e. for the script dropdown, invalidating the list when the -// the script file changes, and for the ID name dropdown invalidating the list when an event ID name chnages (or perhaps -// more simply invalidating it when the target map is opened). +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. @@ -184,13 +190,14 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj 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); @@ -203,6 +210,8 @@ 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); } @@ -211,11 +220,7 @@ void EventFrame::populateIdNameDropdown(NoScrollComboBox * combo, Project * proj return; Map *map = project->loadMap(mapName); - if (!map) - return; - - combo->clear(); - combo->addItems(map->getEventIdNames(group)); + if (map) populateDropdown(combo, map->getEventIdNames(group)); } @@ -428,12 +433,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); } @@ -505,6 +509,7 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { 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(); @@ -514,6 +519,17 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { 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() { @@ -541,7 +557,7 @@ 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); } @@ -607,6 +623,7 @@ void WarpFrame::connectSignals(MainWindow *window) { 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(); @@ -618,6 +635,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() { @@ -642,7 +670,7 @@ 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); } @@ -726,10 +754,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); } @@ -777,8 +803,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); } @@ -844,10 +869,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); } @@ -958,8 +981,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); } @@ -1010,7 +1033,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); } @@ -1070,12 +1093,24 @@ void HealLocationFrame::connectSignals(MainWindow *window) { 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() { @@ -1100,7 +1135,7 @@ void HealLocationFrame::populate(Project *project) { EventFrame::populate(project); if (projectConfig.healLocationRespawnDataEnabled) { - this->combo_respawn_map->addItems(project->mapNames); + populateDropdown(this->combo_respawn_map, project->mapNames); populateIdNameDropdown(this->combo_respawn_npc, project, this->healLocation->getRespawnMapName(), Event::Group::Object); } } From e26be84d9128a64842fa054ae21b4021e7274142 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 18 Apr 2025 13:00:17 -0400 Subject: [PATCH 11/14] Fix z value for events, separate EventPixmapItem from Editor --- include/core/events.h | 6 +- include/editor.h | 17 +++++ include/ui/eventpixmapitem.h | 43 +++++++------ src/core/events.cpp | 13 ++-- src/editor.cpp | 49 +++++++------- src/ui/connectionpixmapitem.cpp | 5 +- src/ui/eventframes.cpp | 19 +++--- src/ui/eventpixmapitem.cpp | 110 ++++++++++++++++++++------------ src/ui/mapimageexporter.cpp | 3 +- src/ui/movablerect.cpp | 1 - src/ui/resizelayoutpopup.cpp | 1 + 11 files changed, 159 insertions(+), 108 deletions(-) diff --git a/include/core/events.h b/include/core/events.h index 4a00c501..9da9b531 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -153,7 +153,7 @@ 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; } @@ -233,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; } @@ -300,7 +300,7 @@ 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; } diff --git a/include/editor.h b/include/editor.h index 07a5dc68..fe01014f 100644 --- a/include/editor.h +++ b/include/editor.h @@ -117,6 +117,7 @@ public: void redrawAllEvents(); void redrawEvents(const QList &events); void redrawEventPixmapItem(EventPixmapItem *item); + void updateEventPixmapItemZValue(EventPixmapItem *item); qreal getEventOpacity(const Event *event) const; void updateCursorRectPos(int x, int y); @@ -182,6 +183,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; diff --git a/include/ui/eventpixmapitem.h b/include/ui/eventpixmapitem.h index c44fc6bf..a28232ba 100644 --- a/include/ui/eventpixmapitem.h +++ b/include/ui/eventpixmapitem.h @@ -10,38 +10,39 @@ #include "events.h" -class Editor; +class Project; class EventPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: - EventPixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) {} - - EventPixmapItem(Event *event, Editor *editor) : QGraphicsPixmapItem(event->getPixmap()) { - this->event = event; - event->setPixmapItem(this); - this->editor = editor; - updatePosition(); - } + explicit EventPixmapItem(Event *event); - Event *event = nullptr; + void render(Project *project); + + bool isSelected() const { return m_selected; } + void setSelected(bool selected) { m_selected = selected; } + + Event * getEvent() const { return m_event; } - void updatePosition(); void move(int dx, int dy); + void moveTo(int x, int y); void moveTo(const QPoint &pos); - void emitPositionChanged(); - void updatePixmap(); private: - Editor *editor = nullptr; - QPoint lastPos; - bool active = false; - bool releaseSelectionQueued = false; + 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); - void yChanged(int); - void spriteChanged(const QPixmap &pixmap); + 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); @@ -51,7 +52,7 @@ protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent*) override; virtual void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; - virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(this->event); } + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override { emit doubleClicked(m_event); } }; #endif // EVENTPIXMAPITEM_H diff --git a/src/core/events.cpp b/src/core/events.cpp index 694919c0..ad50a60e 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -114,9 +114,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; } @@ -225,13 +226,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); } @@ -314,7 +315,7 @@ QSet CloneObjectEvent::getExpectedFields() { return expectedFields; } -void CloneObjectEvent::loadPixmap(Project *project) { +QPixmap CloneObjectEvent::loadPixmap(Project *project) { // Try to get the targeted object to clone Map *clonedMap = project->loadMap(this->targetMap); Event *clonedEvent = clonedMap ? clonedMap->getEvent(Event::Group::Object, this->targetID) : nullptr; @@ -329,7 +330,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/editor.cpp b/src/editor.cpp index 6230771a..ec87c6f3 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1484,7 +1484,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); } @@ -1696,11 +1696,13 @@ void Editor::displayMapEvents() { EventPixmapItem *Editor::addEventPixmapItem(Event *event) { this->project->loadEventPixmap(event); - auto item = new EventPixmapItem(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; @@ -1781,6 +1783,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() { @@ -1806,7 +1809,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); } @@ -1977,36 +1980,36 @@ qreal Editor::getEventOpacity(const Event *event) const { } void Editor::redrawEventPixmapItem(EventPixmapItem *item) { - if (!item || !item->event) - return; + if (!item) return; + Event *event = item->getEvent(); + if (!event) return; - project->loadEventPixmap(item->event, true); - - QPixmap pixmap = item->event->getPixmap(); - if (pixmap.isNull()) - return; - - qreal zValue = item->event->getY(); if (this->editMode == EditMode::Events) { - if (this->selectedEvents.contains(item->event)) { - // Draw the selection rectangle - QPainter painter(&pixmap); - painter.setPen(Qt::magenta); - painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); - zValue++; - } 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); } - item->setPixmap(pixmap); - item->setZValue(zValue); - item->setOpacity(getEventOpacity(item->event)); + updateEventPixmapItemZValue(item); + item->setOpacity(getEventOpacity(event)); item->setShapeMode(porymapConfig.eventSelectionShapeMode); - item->updatePosition(); + 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) { diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp index f1bceac5..f023aa22 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 @@ -31,7 +32,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()) { @@ -43,7 +44,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/eventframes.cpp b/src/ui/eventframes.cpp index 40b2cdf2..86461b83 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -320,6 +320,7 @@ 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(); @@ -330,18 +331,18 @@ void ObjectFrame::connectSignals(MainWindow *window) { // 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(), &EventPixmapItem::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(); }); @@ -498,13 +499,13 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { }); // update icon displayed in frame with target - connect(this->clone->getPixmapItem(), &EventPixmapItem::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, project](const QString &mapName) { this->clone->setTargetMap(mapName); - this->clone->getPixmapItem()->updatePixmap(); + 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); @@ -513,9 +514,9 @@ void CloneObjectFrame::connectSignals(MainWindow *window) { // target id this->combo_target_id->disconnect(); - connect(this->combo_target_id, &QComboBox::currentTextChanged, [this](const QString &text) { + connect(this->combo_target_id, &QComboBox::currentTextChanged, [this, project](const QString &text) { this->clone->setTargetID(text); - this->clone->getPixmapItem()->updatePixmap(); + this->clone->getPixmapItem()->render(project); this->combo_sprite->setCurrentText(this->clone->getGfx()); this->clone->modify(); }); diff --git a/src/ui/eventpixmapitem.cpp b/src/ui/eventpixmapitem.cpp index 1face67c..7c0b43d3 100644 --- a/src/ui/eventpixmapitem.cpp +++ b/src/ui/eventpixmapitem.cpp @@ -1,52 +1,80 @@ #include "eventpixmapitem.h" -#include "editor.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) { - event->setX(event->getX() + dx); - event->setY(event->getY() + dy); - updatePosition(); - emitPositionChanged(); + moveTo(m_event->getX() + dx, + m_event->getY() + dy); } void EventPixmapItem::moveTo(const QPoint &pos) { - event->setX(pos.x()); - event->setY(pos.y()); - updatePosition(); - emitPositionChanged(); + moveTo(pos.x(), pos.y()); } -void EventPixmapItem::updatePosition() { - int x = this->event->getPixelX(); - int y = this->event->getPixelY(); - setX(x); - setY(y); - editor->updateWarpEventWarning(event); +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::emitPositionChanged() { - emit xChanged(event->getX()); - emit yChanged(event->getY()); -} - -void EventPixmapItem::updatePixmap() { - editor->redrawEventPixmapItem(this); - emit spriteChanged(event->getPixmap()); +void EventPixmapItem::updatePixelPosition() { + setPos(m_event->getPixelX(), m_event->getPixelY()); } void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { - if (this->active) + if (m_active) return; - this->active = true; - this->lastPos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); + m_active = true; + m_lastPos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); bool selectionToggle = mouseEvent->modifiers() & Qt::ControlModifier; - if (selectionToggle || !this->editor->selectedEvents.contains(this->event)) { + 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. - emit selected(this->event, selectionToggle); + 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. @@ -55,32 +83,32 @@ void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { // 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; + m_releaseSelectionQueued = true; } mouseEvent->accept(); } void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { - if (!this->active) + if (!m_active) return; QPoint pos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); - if (pos == this->lastPos) + if (pos == m_lastPos) return; - this->releaseSelectionQueued = false; - emit dragged(this->event, this->lastPos, pos); - this->lastPos = pos; + m_releaseSelectionQueued = false; + emit dragged(m_event, m_lastPos, pos); + m_lastPos = pos; } void EventPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { - if (!this->active) + if (!m_active) return; - this->active = false; - if (this->releaseSelectionQueued) { - this->releaseSelectionQueued = false; - if (Metatile::coordFromPixmapCoord(mouseEvent->scenePos()) == this->lastPos) - emit selected(this->event, false); + m_active = false; + if (m_releaseSelectionQueued) { + m_releaseSelectionQueued = false; + if (Metatile::coordFromPixmapCoord(mouseEvent->scenePos()) == m_lastPos) + emit selected(m_event, false); } - emit released(this->event, this->lastPos); + emit released(m_event, m_lastPos); } diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 3272acfe..f036d215 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -113,8 +113,7 @@ void MapImageExporter::setModeSpecificUi() { } if (m_mode == ImageExporterMode::Timelapse) { - // TODO: At the moment edit history for events (and the EventPixmapItem 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 fde7f820..d867c598 100644 --- a/src/ui/movablerect.cpp +++ b/src/ui/movablerect.cpp @@ -27,7 +27,6 @@ ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int heig : QObject(parent), MovableRect(enabled, 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 5629d8e9..2b4f6c0b 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 From 84882a5fadb58b0cab2cc5ec4711656a40560da5 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sun, 20 Apr 2025 09:24:32 -0400 Subject: [PATCH 12/14] Prevent dragging events that aren't selected --- src/ui/eventpixmapitem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/eventpixmapitem.cpp b/src/ui/eventpixmapitem.cpp index 7c0b43d3..bc7bb964 100644 --- a/src/ui/eventpixmapitem.cpp +++ b/src/ui/eventpixmapitem.cpp @@ -74,6 +74,7 @@ void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { 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: @@ -89,7 +90,7 @@ void EventPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { } void EventPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { - if (!m_active) + if (!m_active || !m_selected) return; QPoint pos = Metatile::coordFromPixmapCoord(mouseEvent->scenePos()); From 10aa9a623f20ddd1c13120b4ea6f941729772366 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 23 Apr 2025 02:33:55 -0400 Subject: [PATCH 13/14] Update new tool tips --- src/ui/eventframes.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index 07b77c0b..95a31557 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -230,8 +230,9 @@ void ObjectFrame::setup() { // local id QFormLayout *l_form_local_id = new QFormLayout(); this->line_edit_local_id = new QLineEdit(this); - this->line_edit_local_id->setToolTip("An optional, unique name to use to refer to this object in scripts.\n" - "If no game is given you can refer to this object using its 'object id' number."); + 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); @@ -455,8 +456,9 @@ void CloneObjectFrame::setup() { // local id QFormLayout *l_form_local_id = new QFormLayout(); this->line_edit_local_id = new QLineEdit(this); - this->line_edit_local_id->setToolTip("An optional, unique name to use to refer to this object in scripts.\n" - "If no game is given you can refer to this object using its 'object id' number."); + 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); @@ -464,9 +466,10 @@ void CloneObjectFrame::setup() { // sprite combo (edits disabled) QFormLayout *l_form_sprite = new QFormLayout(); this->combo_sprite = new NoScrollComboBox(this); - this->combo_sprite->setToolTip("The sprite graphics to use for this object. This is updated automatically\n" - "to match the target object, and so can't be edited. By default the games\n" - "will get the graphics directly from the target object, so this field is ignored."); + 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); @@ -574,8 +577,9 @@ void WarpFrame::setup() { // ID QFormLayout *l_form_id = new QFormLayout(); this->line_edit_id = new QLineEdit(this); - this->line_edit_id->setToolTip("An optional, unique name to use to refer to this warp from other warps.\n" - "If no game is given you can refer to this warp using its 'warp id' number."); + 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); From dedf0d3e5748dc356f3965ce74d78557fed8f317 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 23 Apr 2025 02:50:50 -0400 Subject: [PATCH 14/14] Fix crash on project switch --- src/core/events.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/events.cpp b/src/core/events.cpp index c902db32..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() {