Merge pull request #518 from GriffinRichards/api-warnings

Add new API warnings
This commit is contained in:
GriffinR 2023-02-12 14:25:13 -05:00 committed by GitHub
commit d88ae2b461
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 18 deletions

View file

@ -7,8 +7,12 @@ and this project somewhat adheres to [Semantic Versioning](https://semver.org/sp
The **"Breaking Changes"** listed below are changes that have been made in the decompilation projects (e.g. pokeemerald), which porymap requires in order to work properly. It also includes changes to the scripting API that may change the behavior of existing porymap scripts. If porymap is used with a project or API script that is not up-to-date with the breaking changes, then porymap will likely break or behave improperly.
## [Unreleased]
### Added
- Add `registerToggleAction` to the scripting API
### Changed
- Change encounter tab copy and paste behavior.
- A warning will appear if a custom script fails to load or an action fails to run.
### Fixed
- Fix null characters being unpredictably written to some JSON files.
@ -19,6 +23,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix bug which caused encounter configurator to crash if last field was deleted.
- Fix map render when collision view was active while map changed.
- Fix the updated pokefirered region map graphics appearing in grayscale.
- Fix the API function `registerAction` not correctly handling actions with the same name.
## [5.1.0] - 2023-01-22
### Added

View file

@ -1406,6 +1406,15 @@ All utility functions are callable via the global ``utility`` object.
:param string actionName: name of the action that will be displayed in the ``Tools`` menu
:param string shortcut: optional keyboard shortcut
.. js:function:: utility.registerToggleAction(functionName, actionName, shortcut = "", checked = false)
Registers a JavaScript function to an action that can be manually triggered in Porymap's ``Tools`` menu. Optionally, a keyboard shortcut (e.g. ``"Ctrl+P"``) can also be specified, assuming it doesn't collide with any existing shortcuts used by Porymap. A check mark will be toggled next to the action name each time its activated. Whether the check mark is initially present can be set with ``checked``. The function specified by ``functionName`` must have the ``export`` keyword.
:param string functionName: name of the JavaScript function
:param string actionName: name of the action that will be displayed in the ``Tools`` menu
:param string shortcut: optional keyboard shortcut
:param boolean checked: whether the action initially has a check mark. Defaults to ``false``.
.. js:function:: utility.setTimeout(func, delayMs)
This behaves essentially the same as JavaScript's ``setTimeout()`` that is used in web browsers or NodeJS. The ``func`` argument is a JavaScript function (NOT the name of a function) which will be executed after a delay. This is useful for creating animations or refreshing the overlay at constant intervals.

View file

@ -33,9 +33,7 @@ public:
static void init(MainWindow *mainWindow);
static void populateGlobalObject(MainWindow *mainWindow);
static QJSEngine *getEngine();
static void registerAction(QString functionName, QString actionName);
static int numRegisteredActions();
static void invokeAction(QString actionName);
static void invokeAction(int actionIndex);
static void cb_ProjectOpened(QString projectPath);
static void cb_ProjectClosed(QString projectPath);
static void cb_MetatileChanged(int x, int y, Block prevBlock, Block newBlock);
@ -61,6 +59,7 @@ public:
static QJSValue dialogInput(QJSValue input, bool selectedOk);
private:
MainWindow *mainWindow;
QJSEngine *engine;
QStringList filepaths;
QList<QJSValue> modules;

View file

@ -11,8 +11,9 @@ class ScriptUtility : public QObject
public:
ScriptUtility(MainWindow *mainWindow);
void clearActions();
QString getActionFunctionName(QString actionName);
Q_INVOKABLE void registerAction(QString functionName, QString actionName, QString shortcut = "");
QString getActionFunctionName(int actionIndex);
Q_INVOKABLE bool registerAction(QString functionName, QString actionName, QString shortcut = "");
Q_INVOKABLE bool registerToggleAction(QString functionName, QString actionName, QString shortcut = "", bool checked = false);
Q_INVOKABLE void setTimeout(QJSValue callback, int milliseconds);
Q_INVOKABLE void log(QString message);
Q_INVOKABLE void warn(QString message);
@ -58,7 +59,7 @@ private:
MainWindow *window;
QList<QAction *> registeredActions;
QMap<QString, QString> actionMap;
QHash<int, QString> actionMap;
};
#endif // SCRIPTUTILITY_H

View file

@ -7,22 +7,41 @@ ScriptUtility::ScriptUtility(MainWindow *mainWindow) {
this->window = mainWindow;
}
void ScriptUtility::registerAction(QString functionName, QString actionName, QString shortcut) {
bool ScriptUtility::registerAction(QString functionName, QString actionName, QString shortcut) {
if (!window || !window->ui || !window->ui->menuTools)
return;
return false;
this->actionMap.insert(actionName, functionName);
if (this->actionMap.size() == 1) {
if (functionName.isEmpty() || actionName.isEmpty()) {
logError("Failed to register script action. 'functionName' and 'actionName' must be non-empty.");
return false;
}
if (this->registeredActions.size() == 0) {
QAction *section = window->ui->menuTools->addSection("Custom Actions");
this->registeredActions.append(section);
}
QAction *action = window->ui->menuTools->addAction(actionName, [actionName](){
Scripting::invokeAction(actionName);
const int actionIndex = this->registeredActions.size();
QAction *action = window->ui->menuTools->addAction(actionName, [actionIndex](){
Scripting::invokeAction(actionIndex);
});
if (!shortcut.isEmpty()) {
action->setShortcut(QKeySequence(shortcut));
}
this->actionMap.insert(actionIndex, functionName);
this->registeredActions.append(action);
return true;
}
bool ScriptUtility::registerToggleAction(QString functionName, QString actionName, QString shortcut, bool checked) {
if (!registerAction(functionName, actionName, shortcut))
return false;
QAction *action = this->registeredActions.last();
action->setCheckable(true);
action->setChecked(checked);
return true;
}
void ScriptUtility::clearActions() {
@ -31,8 +50,8 @@ void ScriptUtility::clearActions() {
}
}
QString ScriptUtility::getActionFunctionName(QString actionName) {
return this->actionMap.value(actionName);
QString ScriptUtility::getActionFunctionName(int actionIndex) {
return this->actionMap.value(actionIndex);
}
void ScriptUtility::setTimeout(QJSValue callback, int milliseconds) {

View file

@ -33,6 +33,7 @@ void Scripting::init(MainWindow *mainWindow) {
}
Scripting::Scripting(MainWindow *mainWindow) {
this->mainWindow = mainWindow;
this->engine = new QJSEngine(mainWindow);
this->engine->installExtensions(QJSEngine::ConsoleExtension);
for (QString script : userConfig.getCustomScripts()) {
@ -48,7 +49,16 @@ void Scripting::loadModules(QStringList moduleFiles) {
if (module.isError()) {
QString relativePath = QDir::cleanPath(userConfig.getProjectDir() + QDir::separator() + filepath);
module = this->engine->importModule(relativePath);
if (tryErrorJS(module)) continue;
if (tryErrorJS(module)) {
QMessageBox messageBox(this->mainWindow);
messageBox.setText("Failed to load script");
messageBox.setInformativeText(QString("An error occurred while loading custom script file '%1'").arg(filepath));
messageBox.setDetailedText(getMostRecentError());
messageBox.setIcon(QMessageBox::Warning);
messageBox.addButton(QMessageBox::Ok);
messageBox.exec();
continue;
}
}
logInfo(QString("Successfully loaded custom script file '%1'").arg(filepath));
@ -125,9 +135,9 @@ void Scripting::invokeCallback(CallbackType type, QJSValueList args) {
}
}
void Scripting::invokeAction(QString actionName) {
void Scripting::invokeAction(int actionIndex) {
if (!instance || !instance->scriptUtility) return;
QString functionName = instance->scriptUtility->getActionFunctionName(actionName);
QString functionName = instance->scriptUtility->getActionFunctionName(actionIndex);
if (functionName.isEmpty()) return;
bool foundFunction = false;
@ -141,8 +151,15 @@ void Scripting::invokeAction(QString actionName) {
QJSValue result = callbackFunction.call(QJSValueList());
if (tryErrorJS(result)) continue;
}
if (!foundFunction)
if (!foundFunction) {
logError(QString("Unknown custom script function '%1'").arg(functionName));
QMessageBox messageBox(instance->mainWindow);
messageBox.setText("Failed to run custom action");
messageBox.setInformativeText(getMostRecentError());
messageBox.setIcon(QMessageBox::Warning);
messageBox.addButton(QMessageBox::Ok);
messageBox.exec();
}
}
void Scripting::cb_ProjectOpened(QString projectPath) {