From d9b37ecb0be98359988749dccd4b059ed183df24 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 5 Jul 2022 14:38:23 -0400 Subject: [PATCH 1/6] Prevent paste exceeding object limit, warn when copying healspots --- include/editor.h | 2 +- src/editor.cpp | 6 +++--- src/mainwindow.cpp | 23 +++++++++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/editor.h b/include/editor.h index 11efabda..c44fd960 100644 --- a/include/editor.h +++ b/include/editor.h @@ -151,6 +151,7 @@ public: void shouldReselectEvents(); void scaleMapView(int); void openInTextEditor(const QString &path, int lineNum = 0) const; + bool eventLimitReached(QString event_type); public slots: void openMapScripts() const; @@ -176,7 +177,6 @@ private: void updateEncounterFields(EncounterFields newFields); QString getMovementPermissionText(uint16_t collision, uint16_t elevation); QString getMetatileDisplayMessage(uint16_t metatileId); - bool eventLimitReached(Map *, QString); bool startDetachedProcess(const QString &command, const QString &workingDirectory = QString(), qint64 *pid = nullptr) const; diff --git a/src/editor.cpp b/src/editor.cpp index d15b3bbc..f4890106 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2031,7 +2031,7 @@ void Editor::duplicateSelectedEvents() { for (int i = 0; i < selected_events->length(); i++) { Event *original = selected_events->at(i)->event; QString eventType = original->get("event_type"); - if (eventLimitReached(map, eventType)) { + if (eventLimitReached(eventType)) { logWarn(QString("Skipping duplication, the map limit for events of type '%1' has been reached.").arg(eventType)); continue; } @@ -2045,7 +2045,7 @@ void Editor::duplicateSelectedEvents() { } DraggablePixmapItem* Editor::addNewEvent(QString event_type) { - if (project && map && !event_type.isEmpty() && !eventLimitReached(map, event_type)) { + if (project && map && !event_type.isEmpty() && !eventLimitReached(event_type)) { Event *event = Event::createNewEvent(event_type, map->name, project); event->put("map_name", map->name); if (event_type == EventType::HealLocation) { @@ -2061,7 +2061,7 @@ DraggablePixmapItem* Editor::addNewEvent(QString event_type) { } // Currently only object events have an explicit limit -bool Editor::eventLimitReached(Map *map, QString event_type) +bool Editor::eventLimitReached(QString event_type) { if (project && map && !event_type.isEmpty()) { if (Event::typeToGroup(event_type) == EventGroup::Object) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 73ef97c4..91bd32bc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1516,6 +1516,7 @@ void MainWindow::copy() { if (type == EventType::HealLocation) { // no copy on heal locations + logWarn(QString("Copying events of type '%1' is not allowed.").arg(type)); continue; } @@ -1527,11 +1528,11 @@ void MainWindow::copy() { eventsArray.append(eventJson); } - - copyObject["events"] = eventsArray; - setClipboardData(copyObject); - logInfo("Copied currently selected events to clipboard"); - + if (!eventsArray.isEmpty()) { + copyObject["events"] = eventsArray; + setClipboardData(copyObject); + logInfo("Copied currently selected events to clipboard"); + } break; } } @@ -1616,6 +1617,11 @@ void MainWindow::paste() { // paste the event to the map QString type = event["event_type"].toString(); + if (editor->eventLimitReached(type)) { + logWarn(QString("Skipping paste, the map limit for events of type '%1' has been reached.").arg(type)); + continue; + } + Event *pasteEvent = Event::createNewEvent(type, editor->map->name, editor->project); for (auto key : event.toObject().keys()) @@ -1647,9 +1653,10 @@ void MainWindow::paste() { newEvents.append(pasteEvent); } - editor->map->editHistory.push(new EventPaste(this->editor, editor->map, newEvents)); - updateObjects(); - + if (!newEvents.isEmpty()) { + editor->map->editHistory.push(new EventPaste(this->editor, editor->map, newEvents)); + updateObjects(); + } break; } } From 22a42eaac16ec62c395a47db0fca9c761b7ee217 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 5 Jul 2022 14:50:17 -0400 Subject: [PATCH 2/6] Select newly-pasted events --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 91bd32bc..3aea04ec 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1654,8 +1654,8 @@ void MainWindow::paste() { } if (!newEvents.isEmpty()) { - editor->map->editHistory.push(new EventPaste(this->editor, editor->map, newEvents)); updateObjects(); + editor->map->editHistory.push(new EventPaste(this->editor, editor->map, newEvents)); } break; } From a76c1eb14d6402c1f88b95f9e9b25b853924b0ed Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 5 Jul 2022 15:03:12 -0400 Subject: [PATCH 3/6] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf35d79..34eff35f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Overhauled the region map editor, adding support for tilemaps, and significant customization. Also now supports pokefirered. - Metatiles are always rendered accurately with 3 layers, and the unused layer is not assumed to be transparent. - `object_event_graphics_info.h` can now be parsed correctly if it uses structs with attributes. +- The selection is no longer reset when pasting events. The newly pasted events are selected instead. ### Fixed - Fix cursor tile outline not updating at the end of a dragged selection. @@ -31,6 +32,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Fix cursor tile and player view outlines not updating immediately when toggled in Collision view. - Fix selected space not updating while painting in Collision view. - Fix the map music dropdown being empty when importing a map from Advance Map. +- Fix object events added by pasting ignoring the map event limit. ## [4.5.0] - 2021-12-26 ### Added From c8b28be0b4f6d932a23267d0a6a95cc34fece1f1 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 14 Jul 2022 16:10:04 -0400 Subject: [PATCH 4/6] Add message boxes and user input windows to API --- include/mainwindow.h | 8 ++++++ include/scripting.h | 1 + src/mainwindow_scriptapi.cpp | 49 ++++++++++++++++++++++++++++++++++++ src/scripting.cpp | 7 ++++++ 4 files changed, 65 insertions(+) diff --git a/include/mainwindow.h b/include/mainwindow.h index 1174060b..851a3e07 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -149,6 +149,14 @@ public: Q_INVOKABLE void log(QString message); Q_INVOKABLE void warn(QString message); Q_INVOKABLE void error(QString message); + void runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon); + Q_INVOKABLE void showMessage(QString text, QString informativeText = "", QString detailedText = ""); + Q_INVOKABLE void showWarning(QString text, QString informativeText = "", QString detailedText = ""); + Q_INVOKABLE void showError(QString text, QString informativeText = "", QString detailedText = ""); + Q_INVOKABLE bool showQuestion(QString text, QString informativeText = "", QString detailedText = ""); + Q_INVOKABLE QJSValue getInputText(QString title, QString label, QString defaultValue = ""); + Q_INVOKABLE QJSValue getInputNumber(QString title, QString label, double defaultValue = 0, double min = INT_MIN, double max = INT_MAX, int decimals = 0, double step = 1); + Q_INVOKABLE QJSValue getInputItem(QString title, QString label, QStringList items, int defaultValue = 0, bool editable = false); Q_INVOKABLE QList getMetatileLayerOrder(); Q_INVOKABLE void setMetatileLayerOrder(QList order); Q_INVOKABLE QList getMetatileLayerOpacity(); diff --git a/include/scripting.h b/include/scripting.h index 176f69e9..b73e4049 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -33,6 +33,7 @@ public: static QJSValue position(int x, int y); static QJSEngine *getEngine(); static QImage getImage(QString filepath); + static QJSValue dialogInput(QJSValue input, bool selectedOk); static void init(MainWindow *mainWindow); static void registerAction(QString functionName, QString actionName); static int numRegisteredActions(); diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index 5875bb08..a2527cdd 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -785,6 +785,55 @@ void MainWindow::error(QString message) { logError(message); } +void MainWindow::runMessageBox(QString text, QString informativeText, QString detailedText, QMessageBox::Icon icon) { + QMessageBox messageBox(this); + messageBox.setText(text); + messageBox.setInformativeText(informativeText); + messageBox.setDetailedText(detailedText); + messageBox.setIcon(icon); + messageBox.exec(); +} + +void MainWindow::showMessage(QString text, QString informativeText, QString detailedText) { + this->runMessageBox(text, informativeText, detailedText, QMessageBox::Information); +} + +void MainWindow::showWarning(QString text, QString informativeText, QString detailedText) { + this->runMessageBox(text, informativeText, detailedText, QMessageBox::Warning); +} + +void MainWindow::showError(QString text, QString informativeText, QString detailedText) { + this->runMessageBox(text, informativeText, detailedText, QMessageBox::Critical); +} + +bool MainWindow::showQuestion(QString text, QString informativeText, QString detailedText) { + QMessageBox messageBox(this); + messageBox.setText(text); + messageBox.setInformativeText(informativeText); + messageBox.setDetailedText(detailedText); + messageBox.setIcon(QMessageBox::Question); + messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + return messageBox.exec() == QMessageBox::Yes; +} + +QJSValue MainWindow::getInputText(QString title, QString label, QString defaultValue) { + bool ok; + QString input = QInputDialog::getText(this, title, label, QLineEdit::Normal, defaultValue, &ok); + return Scripting::dialogInput(input, ok); +} + +QJSValue MainWindow::getInputNumber(QString title, QString label, double defaultValue, double min, double max, int decimals, double step) { + bool ok; + double input = QInputDialog::getDouble(this, title, label, defaultValue, min, max, decimals, &ok, Qt::WindowFlags(), step); + return Scripting::dialogInput(input, ok); +} + +QJSValue MainWindow::getInputItem(QString title, QString label, QStringList items, int defaultValue, bool editable) { + bool ok; + QString input = QInputDialog::getItem(this, title, label, items, defaultValue, editable, &ok); + return Scripting::dialogInput(input, ok); +} + QList MainWindow::getMetatileLayerOrder() { if (!this->editor || !this->editor->map) return QList(); diff --git a/src/scripting.cpp b/src/scripting.cpp index ef757464..a030ba84 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -262,6 +262,13 @@ QJSValue Scripting::fromTile(Tile tile) { return obj; } +QJSValue Scripting::dialogInput(QJSValue input, bool selectedOk) { + QJSValue obj = instance->engine->newObject(); + obj.setProperty("input", input); + obj.setProperty("ok", selectedOk); + return obj; +} + QJSEngine *Scripting::getEngine() { return instance->engine; } From 597d03979ffe282bb503e77d1a34b069964d538b Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 14 Jul 2022 16:53:15 -0400 Subject: [PATCH 5/6] Update manual --- docsrc/manual/scripting-capabilities.rst | 67 +++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index d86208cc..cee4b9a1 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -1229,9 +1229,74 @@ These are some miscellaneous functions that can be very useful when building cus :param string message: the message to log - .. js:function:: map.error(message) Logs a message to the Porymap log file with the prefix ``[ERROR]``. :param string message: the message to log + +.. js:function:: map.showMessage(text, informativeText, detailedText) + + Displays a message box with an "Information" icon and an ``OK`` button. Execution stops while the window is open. + + :param string text: the main message text + :param string informativeText: smaller text below the main message. Defaults to ``""`` + :param string detailedText: text hidden behind a "Show Details" box. Defaults to ``""`` + +.. js:function:: map.showWarning(text, informativeText, detailedText) + + Displays a message box with a "Warning" icon and an ``OK`` button. Execution stops while the window is open. + + :param string text: the main message text + :param string informativeText: smaller text below the main message. Defaults to ``""`` + :param string detailedText: text hidden behind a "Show Details" box. Defaults to ``""`` + +.. js:function:: map.showError(text, informativeText, detailedText) + + Displays a message box with a "Critical" icon and an ``OK`` button. Execution stops while the window is open. + + :param string text: the main message text + :param string informativeText: smaller text below the main message. Defaults to ``""`` + :param string detailedText: text hidden behind a "Show Details" box. Defaults to ``""`` + +.. js:function:: map.showQuestion(text, informativeText, detailedText) + + Displays a message box with a "Question" icon and a ``Yes`` and a ``No`` button. Execution stops while the window is open. + + :param string text: the main message text + :param string informativeText: smaller text below the main message. Defaults to ``""`` + :param string detailedText: text hidden behind a "Show Details" box. Defaults to ``""`` + :returns boolean: ``true`` if ``Yes`` was selected, ``false`` if ``No`` was selected or if the window was closed without selection + +.. js:function:: map.getInputText(title, label, default) + + Displays a text input dialog with an ``OK`` and a ``Cancel`` button. Execution stops while the window is open. + + :param string title: the text in the window title bar + :param string label: the text adjacent to the input entry area + :param string default: the text in the input entry area when the window is opened. Defaults to ``""`` + :returns {input, ok}: ``input`` will be the input text and ``ok`` will be ``true`` if ``OK`` was selected. ``input`` will be ``""`` and ``ok`` will be ``false`` if ``Cancel`` was selected or if the window was closed without selection. + +.. js:function:: map.getInputNumber(title, label, default, min, max, decimals, step) + + Displays a number input dialog with an ``OK`` and a ``Cancel`` button. Execution stops while the window is open. + + :param string title: the text in the window title bar + :param string label: the text adjacent to the input entry area + :param number default: the number in the input entry area when the window is opened. Defaults to ``0`` + :param number min: the minimum allowable input value. Defaults to ``-2147483648`` + :param number max: the maximum allowable input value. Defaults to ``2147483647`` + :param number decimals: the number of decimals used for the input number. Defaults to ``0`` + :param number step: the increment by which the input number will change when the spinner is used. Defaults to ``1`` + :returns {input, ok}: ``input`` will be the input number and ``ok`` will be ``true`` if ``OK`` was selected. ``input`` will be ``default`` and ``ok`` will be ``false`` if ``Cancel`` was selected or if the window was closed without selection. + +.. js:function:: map.getInputItem(title, label, items, default, editable) + + Displays a text input dialog with an items dropdown and an ``OK`` and a ``Cancel`` button. Execution stops while the window is open. + + :param string title: the text in the window title bar + :param string label: the text adjacent to the input entry area + :param array items: an array of text items that will populate the dropdown + :param number default: the index of the item to select by default. Defaults to ``0`` + :param boolean editable: whether the user is allowed to enter their own text instead. Defaults to ``false`` + :returns {input, ok}: ``input`` will be the input text and ``ok`` will be ``true`` if ``OK`` was selected. ``input`` will be the text of the item at ``default`` and ``ok`` will be ``false`` if ``Cancel`` was selected or if the window was closed without selection. From fc944591dcbadd5e9af9738fd551cff27f43d8a3 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 14 Jul 2022 16:58:55 -0400 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39682e8b..5d6b5f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Add Copy/Paste for metatiles in the Tileset Editor. - Add ability to set the opacity of the scripting overlay. - Add ability to get/set map header properties and read tile pixel data via the API. +- Add ability to display message boxes and user input windows via the API. - Add button to copy the full metatile label to the clipboard in the Tileset Editor. - Add option to not open the most recent project on launch.