From 463803292fbff75cf25324292d2eee1cee6efb33 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sun, 22 Feb 2026 03:04:02 -0500 Subject: [PATCH] New JSON config cleanup and testing --- docsrc/manual/scripting-capabilities.rst | 2 +- forms/newlayoutdialog.ui | 8 ++-- forms/newlayoutform.ui | 14 +++++++ forms/newmapdialog.ui | 14 +++++-- include/config.h | 6 --- include/ui/eventfilters.h | 2 +- include/ui/projectsettingseditor.h | 1 + src/config.cpp | 20 ++++------ src/config/legacy.cpp | 10 ++--- src/core/basegame.cpp | 13 +++---- src/mainwindow.cpp | 3 +- src/ui/eventfilters.cpp | 7 ++-- src/ui/newlayoutdialog.cpp | 8 +--- src/ui/projectsettingseditor.cpp | 27 ++++++++------ src/ui/regionmapeditor.cpp | 47 ++++++------------------ 15 files changed, 84 insertions(+), 98 deletions(-) diff --git a/docsrc/manual/scripting-capabilities.rst b/docsrc/manual/scripting-capabilities.rst index 7c5452b1..8673a4a6 100644 --- a/docsrc/manual/scripting-capabilities.rst +++ b/docsrc/manual/scripting-capabilities.rst @@ -2289,7 +2289,7 @@ All constants are accessible via the global ``constants`` object. .. js:attribute:: constants.base_game_version - The string value of the config setting ``base_game_version``. This will either be ``pokeruby``, ``pokefirered``, or ``pokeemerald``. + The string value of the config setting ``base_game_version``. This will either be ``pokeruby``, ``pokefirered``, ``pokeemerald``, or an empty string. .. js:attribute:: constants.version.major diff --git a/forms/newlayoutdialog.ui b/forms/newlayoutdialog.ui index 980b5351..303e11a1 100644 --- a/forms/newlayoutdialog.ui +++ b/forms/newlayoutdialog.ui @@ -6,8 +6,8 @@ 0 0 - 264 - 173 + 387 + 357 @@ -36,8 +36,8 @@ 0 0 - 240 - 109 + 363 + 293 diff --git a/forms/newlayoutform.ui b/forms/newlayoutform.ui index b3c0649b..86107fb6 100644 --- a/forms/newlayoutform.ui +++ b/forms/newlayoutform.ui @@ -2,6 +2,14 @@ NewLayoutForm + + + 0 + 0 + 196 + 331 + + Form @@ -168,6 +176,9 @@ QComboBox::InsertPolicy::NoInsert + + QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon + @@ -204,6 +215,9 @@ QComboBox::InsertPolicy::NoInsert + + QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon + diff --git a/forms/newmapdialog.ui b/forms/newmapdialog.ui index ec8c2612..9479b6dd 100644 --- a/forms/newmapdialog.ui +++ b/forms/newmapdialog.ui @@ -6,8 +6,8 @@ 0 0 - 559 - 614 + 467 + 428 @@ -39,8 +39,8 @@ 0 0 - 535 - 550 + 443 + 364 @@ -129,6 +129,9 @@ QComboBox::InsertPolicy::NoInsert + + QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon + @@ -171,6 +174,9 @@ QComboBox::InsertPolicy::NoInsert + + QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon + diff --git a/include/config.h b/include/config.h index 6a6618bd..75ca58d0 100644 --- a/include/config.h +++ b/include/config.h @@ -48,13 +48,7 @@ public: { }; virtual ~KeyValueConfigBase() {}; - // Writes the contents of the config to disk. - // Returns true if saving was successful, false otherwise. virtual bool save(); - - // Loads the contents of the config from disk. - // Returns true if saving was successful, false otherwise. - // A successful load includes initializing an empty or non-existing file. virtual bool load(); virtual QJsonObject toJson(); diff --git a/include/ui/eventfilters.h b/include/ui/eventfilters.h index f14d9b3b..d39e8474 100644 --- a/include/ui/eventfilters.h +++ b/include/ui/eventfilters.h @@ -37,5 +37,5 @@ public: bool eventFilter(QObject *obj, QEvent *event) override; private: bool m_loggingEnabled = true; - QSet m_wasShown; + QSet m_shown; }; diff --git a/include/ui/projectsettingseditor.h b/include/ui/projectsettingseditor.h index f14e74cf..34eb4207 100644 --- a/include/ui/projectsettingseditor.h +++ b/include/ui/projectsettingseditor.h @@ -71,6 +71,7 @@ private: void addNewGlobalConstant(); void addGlobalConstant(const QString &name, const QString &expression); QMap getGlobalConstants(); + BaseGame::Version getBaseGameVersion() const; private slots: void dialogButtonClicked(QAbstractButton *button); diff --git a/src/config.cpp b/src/config.cpp index 0321c7e2..e2515350 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -434,10 +434,8 @@ void ProjectConfig::setVersionSpecificDefaults(BaseGame::Version version) { 0x9B, // MB_SECRET_BASE_SPOT_BLUE_CAVE_OPEN 0x9D, // MB_SECRET_BASE_SPOT_TREE_RIGHT_OPEN }; - if (this->baseGameVersion == BaseGame::Version::pokeruby) { - this->mapAllowFlagsEnabled = false; - } } + this->mapAllowFlagsEnabled = (this->baseGameVersion != BaseGame::Version::pokeruby); } bool ProjectConfig::save() { @@ -472,6 +470,7 @@ QJsonObject ProjectConfig::getDefaultJson() const { return defaultConfig.toJson(); } +// TODO: Replace with a new prompt that allows choosing either the defaults for each version, or customizing settings. void ProjectConfig::initializeFromEmpty() { const QString dirName = QDir(projectDir()).dirName(); BaseGame::Version version = BaseGame::stringToVersion(dirName); @@ -485,21 +484,18 @@ void ProjectConfig::initializeFromEmpty() { QFormLayout form(&dialog); - QComboBox *baseGameVersionComboBox = new QComboBox(); - // TODO: Populate dynamically, same as project settings editor - baseGameVersionComboBox->addItem("pokeruby", BaseGame::Version::pokeruby); - baseGameVersionComboBox->addItem("pokefirered", BaseGame::Version::pokefirered); - baseGameVersionComboBox->addItem("pokeemerald", BaseGame::Version::pokeemerald); - form.addRow(new QLabel("Game Version"), baseGameVersionComboBox); - - // TODO: Add an 'Advanced' button to open the project settings window (with some settings disabled) + auto comboBox = new QComboBox(); + comboBox->addItem(BaseGame::versionToString(BaseGame::Version::pokeruby), BaseGame::Version::pokeruby); + comboBox->addItem(BaseGame::versionToString(BaseGame::Version::pokefirered), BaseGame::Version::pokefirered); + comboBox->addItem(BaseGame::versionToString(BaseGame::Version::pokeemerald), BaseGame::Version::pokeemerald); + form.addRow(new QLabel("Game Version"), comboBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); QObject::connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); form.addRow(&buttonBox); if (dialog.exec() == QDialog::Accepted) { - this->baseGameVersion = static_cast(baseGameVersionComboBox->currentData().toInt()); + this->baseGameVersion = static_cast(comboBox->currentData().toInt()); } else { logWarn(QString("No base_game_version selected, using default '%1'").arg(BaseGame::versionToString(this->baseGameVersion))); } diff --git a/src/config/legacy.cpp b/src/config/legacy.cpp index e28776de..1407cc26 100644 --- a/src/config/legacy.cpp +++ b/src/config/legacy.cpp @@ -156,9 +156,9 @@ bool PorymapConfig::parseLegacyKeyValue(const QString &key, const QString &value } else if (key == "text_editor_goto_line") { this->textEditorGotoLine = value; } else if (key == "palette_editor_bit_depth") { - this->paletteEditorBitDepth = toInt(value, 15, 24, 24); - if (this->paletteEditorBitDepth != 15 && this->paletteEditorBitDepth != 24){ - this->paletteEditorBitDepth = 24; + int bitDepth = toInt(value, 15, 24, 24); + if (bitDepth == 15 || bitDepth == 24){ + this->paletteEditorBitDepth = bitDepth; } } else if (key == "project_settings_tab") { this->projectSettingsTab = toInt(value, 0); @@ -180,9 +180,7 @@ bool PorymapConfig::parseLegacyKeyValue(const QString &key, const QString &value this->lastUpdateCheckTime = QDateTime::fromString(value).toLocalTime(); } else if (key == "last_update_check_version") { auto version = QVersionNumber::fromString(value); - if (version.segmentCount() != 3) { - this->lastUpdateCheckVersion = porymapVersion; - } else { + if (version.segmentCount() == 3) { this->lastUpdateCheckVersion = version; } } else if (key.startsWith("rate_limit_time/")) { diff --git a/src/core/basegame.cpp b/src/core/basegame.cpp index 0646a357..0eecb123 100644 --- a/src/core/basegame.cpp +++ b/src/core/basegame.cpp @@ -13,14 +13,14 @@ BaseGame::Version BaseGame::stringToVersion(const QString &input_) { }; const QString input(input_.toLower()); - BaseGame::Version version = BaseGame::Version::none; + Version version = Version::none; for (auto it = versionDetectNames.begin(); it != versionDetectNames.end(); it++) { // Compare the given string to all the possible names for this game version for (const auto &name : it.value()) { if (input.contains(name)) { - if (version != BaseGame::Version::none) { + if (version != Version::none) { // The given string matches multiple versions, so we can't be sure which it is. - return BaseGame::Version::none; + return Version::none; } version = it.key(); break; @@ -31,7 +31,6 @@ BaseGame::Version BaseGame::stringToVersion(const QString &input_) { return version; } -// TODO: Make sure empty string is ok everywhere this is used QString BaseGame::versionToString(BaseGame::Version version) { static const QMap map = { {Version::pokeruby, "pokeruby"}, @@ -42,15 +41,15 @@ QString BaseGame::versionToString(BaseGame::Version version) { } QString BaseGame::getPlayerIconPath(BaseGame::Version version, int character) { - if (version == BaseGame::Version::pokeemerald) { + if (version == Version::pokeemerald) { static const QStringList paths = { QStringLiteral(":/icons/player/brendan_em.ico"), QStringLiteral(":/icons/player/may_em.ico"), }; return paths.value(character); - } else if (version == BaseGame::Version::pokefirered) { + } else if (version == Version::pokefirered) { static const QStringList paths = { QStringLiteral(":/icons/player/red.ico"), QStringLiteral(":/icons/player/green.ico"), }; return paths.value(character); - } else if (version == BaseGame::Version::pokeruby) { + } else if (version == Version::pokeruby) { static const QStringList paths = { QStringLiteral(":/icons/player/brendan_rs.ico"), QStringLiteral(":/icons/player/may_rs.ico"), }; return paths.value(character); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6d6056e9..b1b721a5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -895,7 +895,8 @@ bool MainWindow::checkProjectVersion(Project *project) { logInfo(QString("Successfully checked project version. Supports at least Porymap v%1").arg(minimumVersion.toString())); } if (minimumVersion > porymapVersion || minimumVersion.majorVersion() != porymapVersion.majorVersion()) { - // We were unable to find the necessary changes for Porymap's current major version. + // Porymap is incompatible with the project if its version is below the specified minimum version, + // or if Porymap is so new that it exceeds the major version of the specified minimum version. // Unless they have explicitly suppressed this message, warn the user that this might mean their project is missing breaking changes. // Note: Do not report 'minimumVersion' to the user in this message. We've already logged it for troubleshooting. // It is very plausible that the user may have reproduced the required changes in an diff --git a/src/ui/eventfilters.cpp b/src/ui/eventfilters.cpp index df3e65c4..d32c5b01 100644 --- a/src/ui/eventfilters.cpp +++ b/src/ui/eventfilters.cpp @@ -40,14 +40,15 @@ bool GeometrySaver::eventFilter(QObject *object, QEvent *event) { if (m_loggingEnabled && !w->windowTitle().isEmpty()) { logInfo(QString("Opening window: %1").arg(w->windowTitle())); } - m_wasShown.insert(object); - } else if (event->type() == QEvent::Close && m_wasShown.contains(object)) { + m_shown.insert(object); + } else if (event->type() == QEvent::Close || event->type() == QEvent::DeferredDelete) { // There are situations where a window might be 'closed' without // ever actually having been opened (for example, the Shortcuts Editor // will quietly construct windows to get their shortcuts, and those windows // can later be closed without having been displayed). // We don't want to save the geometry of these windows, or log that they closed, - // so we've checked to make sure the widget was displayed before proceeding. + // so we checked to make sure the widget was displayed before proceeding. + if (!m_shown.remove(object)) return false; porymapConfig.saveGeometry(w); if (m_loggingEnabled && !w->windowTitle().isEmpty()) { logInfo(QString("Closing window: %1").arg(w->windowTitle())); diff --git a/src/ui/newlayoutdialog.cpp b/src/ui/newlayoutdialog.cpp index c12ba566..16a82645 100644 --- a/src/ui/newlayoutdialog.cpp +++ b/src/ui/newlayoutdialog.cpp @@ -3,6 +3,7 @@ #include "ui_newlayoutdialog.h" #include "config.h" #include "validator.h" +#include "eventfilters.h" #include #include @@ -40,17 +41,12 @@ NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layoutToCopy, Q connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewLayoutDialog::dialogButtonClicked); refresh(); - - if (!porymapConfig.restoreGeometry(this)) { - // On first display resize to fit contents a little better - adjustSize(); - } + installEventFilter(new GeometrySaver(this)); ui->lineEdit_Name->setFocus(); } NewLayoutDialog::~NewLayoutDialog() { - porymapConfig.saveGeometry(this); saveSettings(); delete ui; } diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index 5afcf81d..714ad326 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -111,12 +111,10 @@ void ProjectSettingsEditor::initUi() { ui->comboBox_IconSpecies->addItems(project->speciesNames); ui->comboBox_WarpBehaviors->addItems(project->metatileBehaviorMap.keys()); } - // TODO: We don't need to keep converting these to/from strings, just include the value as data. - ui->comboBox_BaseGameVersion->addItems({ - BaseGame::versionToString(BaseGame::Version::pokeruby), - BaseGame::versionToString(BaseGame::Version::pokefirered), - BaseGame::versionToString(BaseGame::Version::pokeemerald), - }); + ui->comboBox_BaseGameVersion->addItem("Custom", BaseGame::Version::none); + ui->comboBox_BaseGameVersion->addItem(BaseGame::versionToString(BaseGame::Version::pokeruby), BaseGame::Version::pokeruby); + ui->comboBox_BaseGameVersion->addItem(BaseGame::versionToString(BaseGame::Version::pokefirered), BaseGame::Version::pokefirered); + ui->comboBox_BaseGameVersion->addItem(BaseGame::versionToString(BaseGame::Version::pokeemerald), BaseGame::Version::pokeemerald); ui->comboBox_AttributesSize->addItems({"1", "2", "4"}); ui->comboBox_EventsTabIcon->addItem("Automatic", ""); @@ -207,6 +205,10 @@ bool ProjectSettingsEditor::disableParsedSetting(QWidget * widget, const QString return false; } +BaseGame::Version ProjectSettingsEditor::getBaseGameVersion() const { + return static_cast(ui->comboBox_BaseGameVersion->currentData().toInt()); +} + // Remember the current settings tab for future sessions void ProjectSettingsEditor::on_mainTabs_tabBarClicked(int index) { porymapConfig.projectSettingsTab = index; @@ -447,7 +449,7 @@ void ProjectSettingsEditor::refresh() { // Set combo box texts ui->comboBox_DefaultPrimaryTileset->setTextItem(projectConfig.defaultPrimaryTileset); ui->comboBox_DefaultSecondaryTileset->setTextItem(projectConfig.defaultSecondaryTileset); - ui->comboBox_BaseGameVersion->setTextItem(BaseGame::versionToString(projectConfig.baseGameVersion)); + ui->comboBox_BaseGameVersion->setNumberItem(projectConfig.baseGameVersion); ui->comboBox_AttributesSize->setTextItem(QString::number(projectConfig.metatileAttributesSize)); this->updateAttributeLimits(ui->comboBox_AttributesSize->currentText()); @@ -555,7 +557,7 @@ void ProjectSettingsEditor::save() { // Save combo box settings projectConfig.defaultPrimaryTileset = ui->comboBox_DefaultPrimaryTileset->currentText(); projectConfig.defaultSecondaryTileset = ui->comboBox_DefaultSecondaryTileset->currentText(); - projectConfig.baseGameVersion = BaseGame::stringToVersion(ui->comboBox_BaseGameVersion->currentText()); + projectConfig.baseGameVersion = getBaseGameVersion(); projectConfig.metatileAttributesSize = ui->comboBox_AttributesSize->currentText().toInt(); // Save check box settings @@ -772,7 +774,7 @@ QString ProjectSettingsEditor::stripProjectDir(QString s) { void ProjectSettingsEditor::importDefaultPrefabsClicked(bool) { // If the prompt is accepted the prefabs file will be created and its filepath will be saved in the config. - BaseGame::Version version = BaseGame::stringToVersion(ui->comboBox_BaseGameVersion->currentText()); + BaseGame::Version version = getBaseGameVersion(); if (prefab.tryImportDefaultPrefabs(this, version, ui->lineEdit_PrefabsPath->text())) { ui->lineEdit_PrefabsPath->setText(userConfig.prefabsFilepath); // Refresh with new filepath this->hasUnsavedChanges = true; @@ -804,19 +806,20 @@ bool ProjectSettingsEditor::promptSaveChanges() { return true; } +// TODO: Changing the base game version implicitly changes defaults. +// If the user declines this prompt, it should revert to the previous setting. bool ProjectSettingsEditor::promptRestoreDefaults() { if (this->refreshing) return false; - const QString versionText = ui->comboBox_BaseGameVersion->currentText(); - if (this->prompt(QString("Restore default config settings for %1?").arg(versionText)) == QMessageBox::No) + if (this->prompt(QString("Restore default config settings for %1?").arg(ui->comboBox_BaseGameVersion->currentText())) == QMessageBox::No) return false; // Restore defaults by resetting config in memory, refreshing the UI, then restoring the config. // Don't want to save changes until user accepts them. // TODO: Maybe give the project settings editor it's own copy of the config then. ProjectConfig tempProject = projectConfig; - projectConfig.setVersionSpecificDefaults(BaseGame::stringToVersion(versionText)); + projectConfig.setVersionSpecificDefaults(getBaseGameVersion()); this->refresh(); projectConfig = tempProject; diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index ce0fa0ce..5f4cded0 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -115,42 +115,15 @@ bool RegionMapEditor::saveRegionMapEntries() { return true; } -void buildEmeraldDefaults(poryjson::Json &json) { - ParseUtil parser; - QString emeraldDefault = parser.readTextFile(":/text/region_map_default_emerald.json"); - json = poryjson::Json::parse(emeraldDefault); -} - -void buildRubyDefaults(poryjson::Json &json) { - ParseUtil parser; - QString emeraldDefault = parser.readTextFile(":/text/region_map_default_ruby.json"); - json = poryjson::Json::parse(emeraldDefault); -} - -void buildFireredDefaults(poryjson::Json &json) { - - ParseUtil parser; - QString fireredDefault = parser.readTextFile(":/text/region_map_default_firered.json"); - json = poryjson::Json::parse(fireredDefault); -} - poryjson::Json RegionMapEditor::buildDefaultJson() { - poryjson::Json defaultJson; - switch (projectConfig.baseGameVersion) { - case BaseGame::Version::pokeemerald: - buildEmeraldDefaults(defaultJson); - break; - case BaseGame::Version::pokeruby: - buildRubyDefaults(defaultJson); - break; - case BaseGame::Version::pokefirered: - buildFireredDefaults(defaultJson); - break; - default: - break; - } - - return defaultJson; + static const QMap versionToJsonPath = { + {BaseGame::Version::pokeemerald, QStringLiteral(":/text/region_map_default_emerald.json")}, + {BaseGame::Version::pokeruby, QStringLiteral(":/text/region_map_default_ruby.json")}, + {BaseGame::Version::pokefirered, QStringLiteral(":/text/region_map_default_firered.json")}, + }; + const QString path = versionToJsonPath.value(projectConfig.baseGameVersion); + if (path.isEmpty()) { Q_ASSERT(false); return OrderedJson::object(); } + return OrderedJson::parse(ParseUtil::readTextFile(path)); } bool RegionMapEditor::buildConfigDialog() { @@ -276,6 +249,8 @@ bool RegionMapEditor::buildConfigDialog() { // for sake of convenience, option to just use defaults for each basegame version + // TODO: Each version's default settings should be available regardless of the base game version, + // it can just suggest which default settings to use depending on the base game version. QPushButton *config_useProjectDefault = nullptr; switch (projectConfig.baseGameVersion) { case BaseGame::Version::pokefirered: @@ -288,6 +263,8 @@ bool RegionMapEditor::buildConfigDialog() { config_useProjectDefault = new QPushButton("\nUse pokeruby defaults\n"); break; default: + config_useProjectDefault = new QPushButton("\nNo default settings available\n"); + config_useProjectDefault->setEnabled(false); break; } form.addRow(config_useProjectDefault);