diff --git a/forms/preferenceeditor.ui b/forms/preferenceeditor.ui index d3ec7cea..63017212 100644 --- a/forms/preferenceeditor.ui +++ b/forms/preferenceeditor.ui @@ -104,7 +104,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -159,7 +159,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -209,7 +209,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -253,13 +253,42 @@ - - - <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. Enabling this setting will make Porymap's startup slower.</p></body></html> - - - Autocomplete Script labels using all possible scripts + + + Script label autocomplete + + + + + <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. This is the slowest option for Porymap's project opening.</p></body></html> + + + All possible scripts + + + + + + + <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will include script labels from the current map's scripts file, scripts in-use by the map's other events, and all script files in the <span style=" font-family:'SFMono-Regular','Menlo','Monaco','Consolas','Liberation Mono','Courier New','Courier','monospace'; font-size:11px; color:#e74c3c; background-color:#ffffff;">data_scripts_folders </span>folder.</p></body></html> + + + Current map, and global script files + + + + + + + <html><head/><body><p>If checked, the list of suggestions when typing in an Event's Script field will only include script labels from the current map's scripts file and scripts in-use by the map's other events. This is the fastest option for Porymap's project opening.</p></body></html> + + + Current map only + + + + @@ -294,7 +323,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -323,7 +352,7 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame true @@ -333,13 +362,13 @@ 0 0 - 476 - 343 + 495 + 376 - QLayout::SetMinimumSize + QLayout::SizeConstraint::SetMinimumSize @@ -347,7 +376,7 @@ <html><head/><body><p>When this command is set a button will appear next to the <span style=" font-weight:600; font-style:italic;">Script</span> combo-box in the <span style=" font-weight:600; font-style:italic;">Events</span> tab which executes this command.<span style=" font-weight:600;"> %F</span> will be substituted with the file path of the script and <span style=" font-weight:600;">%L</span> will be substituted with the line number of the script in that file. <span style=" font-weight:600;">%F </span><span style=" font-style:italic;">must</span> be given if <span style=" font-weight:600;">%L</span> is given. If <span style=" font-weight:600;">%F</span> is <span style=" font-style:italic;">not</span> given then the script's file path will be added to the end of the command. If the script can't be found then the current map's scripts file is opened.</p></body></html> - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop true @@ -380,7 +409,7 @@ <html><head/><body><p>This is the command that is executed when clicking <span style=" font-weight:600; font-style:italic;">Open Project in Text Editor</span> in the <span style=" font-weight:600; font-style:italic;">Tools</span> menu. <span style=" font-weight:600;">%D</span> will be substituted with the project's root directory. If <span style=" font-weight:600;">%D</span> is <span style=" font-style:italic;">not</span> specified then the project directory will be added to the end of the command.</p></body></html> - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop true @@ -410,10 +439,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Fixed + QSizePolicy::Policy::Fixed @@ -426,7 +455,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -447,7 +476,7 @@ - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/include/config.h b/include/config.h index 234fa76d..6e9bbfca 100644 --- a/include/config.h +++ b/include/config.h @@ -27,6 +27,12 @@ extern const QVersionNumber porymapVersion; #define CONFIG_BACKWARDS_COMPATABILITY +enum ScriptAutocompleteMode { + MapOnly, + MapAndCommon, + All, +}; + class KeyValueConfigBase { public: @@ -101,7 +107,7 @@ public: this->textEditorGotoLine = ""; this->paletteEditorBitDepth = 24; this->projectSettingsTab = 0; - this->loadAllEventScripts = false; + this->scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly; this->warpBehaviorWarningDisabled = false; this->eventDeleteWarningDisabled = false; this->eventOverlayEnabled = false; @@ -168,7 +174,7 @@ public: QString textEditorGotoLine; int paletteEditorBitDepth; int projectSettingsTab; - bool loadAllEventScripts; + ScriptAutocompleteMode scriptAutocompleteMode; bool warpBehaviorWarningDisabled; bool eventDeleteWarningDisabled; bool eventOverlayEnabled; diff --git a/include/core/map.h b/include/core/map.h index d5e28075..1d460cf1 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -149,7 +149,7 @@ signals: void modified(); void scriptsModified(); void mapDimensionsChanged(const QSize &size); - void openScriptRequested(QString label); + void openScriptRequested(const QString &label); void connectionAdded(MapConnection*); void connectionRemoved(MapConnection*); void layoutChanged(); diff --git a/include/editor.h b/include/editor.h index 414356d1..9de1bc94 100644 --- a/include/editor.h +++ b/include/editor.h @@ -209,7 +209,8 @@ public: public slots: void openMapScripts() const; - void openScript(const QString &scriptLabel) const; + bool openScript(const QString &scriptLabel) const; + bool openScriptInFile(const QString &scriptLabel, const QString &filepath) const; void openProjectInTextEditor() const; void maskNonVisibleConnectionTiles(); void onBorderMetatilesChanged(); diff --git a/include/project.h b/include/project.h index ca7d9bbe..45df8143 100644 --- a/include/project.h +++ b/include/project.h @@ -215,7 +215,11 @@ public: static QString getScriptFileExtension(bool usePoryScript); QString getScriptDefaultString(bool usePoryScript, QString mapName) const; - QStringList getEventScriptsFilepaths() const; + + QStringList getAllEventScriptsFilepaths() const; + QStringList getMapScriptsFilepaths() const; + QStringList getCommonEventScriptsFilepaths() const; + QStringList findScriptsFiles(const QString &searchDir, const QStringList &fileNames = {"*"}) const; void insertGlobalScriptLabels(QStringList &scriptLabels) const; QString getDefaultPrimaryTilesetLabel() const; diff --git a/include/ui/preferenceeditor.h b/include/ui/preferenceeditor.h index 74181c37..24b6253f 100644 --- a/include/ui/preferenceeditor.h +++ b/include/ui/preferenceeditor.h @@ -2,6 +2,7 @@ #define PREFERENCES_H #include +#include "config.h" class NoScrollComboBox; class QAbstractButton; @@ -23,7 +24,7 @@ public: signals: void preferencesSaved(); void themeChanged(const QString &theme); - void scriptSettingsChanged(bool on); + void scriptSettingsChanged(ScriptAutocompleteMode mode); void reloadProjectRequested(); private: diff --git a/src/config.cpp b/src/config.cpp index 4f5188bd..666ec12c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -428,8 +428,13 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { } } else if (key == "project_settings_tab") { this->projectSettingsTab = getConfigInteger(key, value, 0); +#ifdef CONFIG_BACKWARDS_COMPATABILITY + // Old setting replaced by script_autocomplete_mode } else if (key == "load_all_event_scripts") { - this->loadAllEventScripts = getConfigBool(key, value); + this->scriptAutocompleteMode = getConfigBool(key, value) ? ScriptAutocompleteMode::All : ScriptAutocompleteMode::MapOnly; +#endif + } else if (key == "script_autocomplete_mode") { + this->scriptAutocompleteMode = static_cast(getConfigInteger(key, value, ScriptAutocompleteMode::MapOnly, ScriptAutocompleteMode::All)); } else if (key == "warp_behavior_warning_disabled") { this->warpBehaviorWarningDisabled = getConfigBool(key, value); } else if (key == "event_delete_warning_disabled") { @@ -551,7 +556,7 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("text_editor_goto_line", this->textEditorGotoLine); map.insert("palette_editor_bit_depth", QString::number(this->paletteEditorBitDepth)); map.insert("project_settings_tab", QString::number(this->projectSettingsTab)); - map.insert("load_all_event_scripts", QString::number(this->loadAllEventScripts)); + map.insert("script_autocomplete_mode", QString::number(this->scriptAutocompleteMode)); map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled)); map.insert("event_delete_warning_disabled", QString::number(this->eventDeleteWarningDisabled)); map.insert("event_overlay_enabled", QString::number(this->eventOverlayEnabled)); diff --git a/src/editor.cpp b/src/editor.cpp index 5ad15a84..a0635d38 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2355,21 +2355,29 @@ void Editor::openMapScripts() const { openInTextEditor(map->getScriptsFilepath()); } -void Editor::openScript(const QString &scriptLabel) const { +bool Editor::openScript(const QString &scriptLabel) const { // Find the location of scriptLabel. - QStringList scriptPaths(map->getScriptsFilepath()); - scriptPaths << project->getEventScriptsFilepaths(); - int lineNum = 0; - QString scriptPath = scriptPaths.first(); - for (const auto &path : scriptPaths) { - lineNum = ParseUtil::getScriptLineNumber(path, scriptLabel); - if (lineNum != 0) { - scriptPath = path; - break; - } - } + // First, try the current map's scripts file. + if (openScriptInFile(scriptLabel, map->getScriptsFilepath())) + return true; - openInTextEditor(scriptPath, lineNum); + // Script is not in the current map's scripts file. + // Search all possible script files. + const QStringList paths = project->getAllEventScriptsFilepaths(); + for (const auto &path : paths) { + if (openScriptInFile(scriptLabel, path)) + return true; + } + return false; +} + +bool Editor::openScriptInFile(const QString &scriptLabel, const QString &filepath) const { + int lineNum = ParseUtil::getScriptLineNumber(filepath, scriptLabel); + if (lineNum == 0) + return false; + + openInTextEditor(filepath, lineNum); + return true; } void Editor::openMapJson(const QString &mapName) const { diff --git a/src/project.cpp b/src/project.cpp index 75b32474..7188b5ef 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -2970,13 +2970,19 @@ bool Project::readGlobalConstants() { bool Project::readEventScriptLabels() { this->globalScriptLabels.clear(); - if (porymapConfig.loadAllEventScripts) { - for (const auto &filePath : getEventScriptsFilepaths()) - this->globalScriptLabels << ParseUtil::getGlobalScriptLabels(filePath); - - this->globalScriptLabels.sort(Qt::CaseInsensitive); - this->globalScriptLabels.removeDuplicates(); + QStringList paths; + if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::All) { + paths = getAllEventScriptsFilepaths(); + } else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapAndCommon) { + paths = getCommonEventScriptsFilepaths(); } + + for (const auto &path : paths) { + this->globalScriptLabels << ParseUtil::getGlobalScriptLabels(path); + } + this->globalScriptLabels.sort(Qt::CaseInsensitive); + this->globalScriptLabels.removeDuplicates(); + emit eventScriptLabelsRead(); return true; } @@ -3012,30 +3018,46 @@ QString Project::getScriptDefaultString(bool usePoryScript, QString mapName) con return QString("%1_MapScripts::\n\t.byte 0\n").arg(mapName); } -QStringList Project::getEventScriptsFilepaths() const { - QStringList filePaths(QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_event_scripts))); - const QString scriptsDir = QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_scripts_folders)); - const QString mapsDir = QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_map_folders)); +QStringList Project::getAllEventScriptsFilepaths() const { + return getMapScriptsFilepaths() + getCommonEventScriptsFilepaths(); +} - if (projectConfig.usePoryScript) { - QDirIterator it_pory_shared(scriptsDir, {"*.pory"}, QDir::Files); - while (it_pory_shared.hasNext()) - filePaths << it_pory_shared.next(); +// Get the paths for all "scripts.inc" / "scripts.pory" files in the data/maps/*/ folders. +QStringList Project::getMapScriptsFilepaths() const { + return findScriptsFiles(projectConfig.getFilePath(ProjectFilePath::data_map_folders), {"scripts"}); +} - QDirIterator it_pory_maps(mapsDir, {"scripts.pory"}, QDir::Files, QDirIterator::Subdirectories); - while (it_pory_maps.hasNext()) - filePaths << it_pory_maps.next(); +// Get the paths for all "*.inc" / "*.pory" files in the data/scripts/ folder, + data/event_scripts.s. +QStringList Project::getCommonEventScriptsFilepaths() const { + QStringList paths = { QDir::cleanPath(this->root + "/" + projectConfig.getFilePath(ProjectFilePath::data_event_scripts)) }; + return paths + findScriptsFiles(projectConfig.getFilePath(ProjectFilePath::data_scripts_folders)); +} + +QStringList Project::findScriptsFiles(const QString &searchDir, const QStringList &fileNames) const { + QStringList paths; + + QString dir = searchDir; + if (!dir.startsWith(this->root)) { + dir = QDir::cleanPath(this->root + "/" + dir); } - QDirIterator it_inc_shared(scriptsDir, {"*.inc"}, QDir::Files); - while (it_inc_shared.hasNext()) - filePaths << it_inc_shared.next(); + QStringList filters; + for (const auto &s : fileNames) { + filters.append(s + getScriptFileExtension(false)); + if (projectConfig.usePoryScript) { + filters.append(s + getScriptFileExtension(true)); + } + } - QDirIterator it_inc_maps(mapsDir, {"scripts.inc"}, QDir::Files, QDirIterator::Subdirectories); - while (it_inc_maps.hasNext()) - filePaths << it_inc_maps.next(); - - return filePaths; + // TODO: Filter out .inc files that are generated by a .pory file. + // They won't cause problems for the user, but it'll create extra parsing work later. + if (!filters.isEmpty()) { + QDirIterator it(dir, filters, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); + while (it.hasNext()) { + paths.append(it.next()); + } + } + return paths; } void Project::loadEventPixmap(Event *event, bool forceLoad) { diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index 6457e564..a1af9e97 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -201,8 +201,8 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj QStringList scripts = map->getScriptLabels(this->event->getEventGroup()); populateDropdown(combo, scripts); - // Depending on the settings, the autocomplete may also contain all global scripts. - if (project && porymapConfig.loadAllEventScripts) { + // Depending on the settings, the autocomplete may also contain scripts from outside the map. + if (project && porymapConfig.scriptAutocompleteMode != ScriptAutocompleteMode::MapOnly) { project->insertGlobalScriptLabels(scripts); } diff --git a/src/ui/preferenceeditor.cpp b/src/ui/preferenceeditor.cpp index 4f4b0929..5765ba01 100644 --- a/src/ui/preferenceeditor.cpp +++ b/src/ui/preferenceeditor.cpp @@ -1,6 +1,5 @@ #include "preferenceeditor.h" #include "ui_preferenceeditor.h" -#include "config.h" #include "noscrollcombobox.h" #include "message.h" @@ -76,7 +75,14 @@ void PreferenceEditor::updateFields() { ui->checkBox_OpenRecentProject->setChecked(porymapConfig.reopenOnLaunch); ui->checkBox_CheckForUpdates->setChecked(porymapConfig.checkForUpdates); ui->checkBox_DisableEventWarning->setChecked(porymapConfig.eventDeleteWarningDisabled); - ui->checkBox_AutocompleteAllScripts->setChecked(porymapConfig.loadAllEventScripts); + + if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapOnly) { + ui->radioButton_AutocompleteMapScripts->setChecked(true); + } else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapAndCommon) { + ui->radioButton_AutocompleteCommonScripts->setChecked(true); + } else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::All) { + ui->radioButton_AutocompleteAllScripts->setChecked(true); + } auto logTypeEnd = porymapConfig.statusBarLogTypes.end(); ui->checkBox_StatusErrors->setChecked(porymapConfig.statusBarLogTypes.find(LogType::LOG_ERROR) != logTypeEnd); @@ -95,11 +101,18 @@ void PreferenceEditor::saveFields() { porymapConfig.theme = themeSelector->currentText(); changedTheme = true; } - bool loadAllEventScripts = ui->checkBox_AutocompleteAllScripts->isChecked(); - if (loadAllEventScripts != porymapConfig.loadAllEventScripts) { - porymapConfig.loadAllEventScripts = loadAllEventScripts; - emit scriptSettingsChanged(loadAllEventScripts); + + auto scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly; + if (ui->radioButton_AutocompleteCommonScripts->isChecked()) { + scriptAutocompleteMode = ScriptAutocompleteMode::MapAndCommon; + } else if (ui->radioButton_AutocompleteAllScripts->isChecked()) { + scriptAutocompleteMode = ScriptAutocompleteMode::All; } + if (scriptAutocompleteMode != porymapConfig.scriptAutocompleteMode) { + porymapConfig.scriptAutocompleteMode = scriptAutocompleteMode; + emit scriptSettingsChanged(scriptAutocompleteMode); + } + porymapConfig.eventSelectionShapeMode = ui->radioButton_OnSprite->isChecked() ? QGraphicsPixmapItem::MaskShape : QGraphicsPixmapItem::BoundingRectShape; porymapConfig.textEditorOpenFolder = ui->lineEdit_TextEditorOpenFolder->text(); porymapConfig.textEditorGotoLine = ui->lineEdit_TextEditorGotoLine->text();