diff --git a/Core/Config.cpp b/Core/Config.cpp index 5a887b91fe..a5b8ace797 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1643,6 +1643,48 @@ float Config::GetDefaultValueFloat(const float *configSetting) { return ref->setting->GetDefaultFloat(); } +bool Config::RestoreSettingToDefault(void *ptr) { + const ConfigSettingRef *ref = FindSetting(ptr); + if (!ref) { + return false; + } + if (!ref->block->CanResetToDefault()) { + return ref->setting->RestoreToDefault(ref->block, false); + } + // Copy the value from a reset copy of the block. + std::unique_ptr defaults = ref->block->NewDefault(ref->section); + const void *def = ref->setting->GetVoidPtr(defaults.get()); + switch (ref->setting->type_) { + case ConfigSetting::Type::TYPE_BOOL: + { + bool changed = *(bool *)ptr != *(const bool *)def; + *(bool *)ptr = *(const bool *)def; + return changed; + } + case ConfigSetting::Type::TYPE_INT: + { + bool changed = *(int *)ptr != *(const int *)def; + *(int *)ptr = *(const int *)def; + return changed; + } + case ConfigSetting::Type::TYPE_FLOAT: + { + bool changed = *(float *)ptr != *(const float *)def; + *(float *)ptr = *(const float *)def; + return changed; + } + case ConfigSetting::Type::TYPE_STRING: + { + bool changed = *(std::string *)ptr != *(const std::string *)def; + *(std::string *)ptr = *(const std::string *)def; + return changed; + } + default: + _dbg_assert_msg_(false, "RestoreSettingToDefault: Unsupported type %d in a block", (int)ref->setting->type_); + return false; + } +} + void Config::Init() { // Initialize the pointer->setting lookup map. auto &ref = getPtrLUT(); diff --git a/Core/Config.h b/Core/Config.h index 3a981eafd4..58f9d51977 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -820,6 +820,8 @@ public: static const ConfigSettingRef *FindSetting(const void *ptr); static int GetDefaultValueInt(const int *configSetting); static float GetDefaultValueFloat(const float *configSetting); + // Sets the setting that ptr points to back to its default. Returns true if that changed it. + static bool RestoreSettingToDefault(void *ptr); void DoNotSaveSetting(void *configSetting) { settingsNotToSave_.push_back(configSetting); diff --git a/UI/Settings/GraphicsPage.cpp b/UI/Settings/GraphicsPage.cpp index 21e968180a..da1a4ec0f8 100644 --- a/UI/Settings/GraphicsPage.cpp +++ b/UI/Settings/GraphicsPage.cpp @@ -123,6 +123,9 @@ static void BuildSpeedHacksPage(SettingsPage *page) { if (g_Config.iDepthRasterMode != 3) depthRasterMode->HideChoice(3); }); + + page->Header(); + page->RestoreDefaults(); } static void BuildCardboardVRPage(SettingsPage *page) { diff --git a/UI/SettingsPage.cpp b/UI/SettingsPage.cpp index f3466fcf8f..6ed42530cc 100644 --- a/UI/SettingsPage.cpp +++ b/UI/SettingsPage.cpp @@ -21,8 +21,10 @@ #include "Common/UI/ViewGroup.h" #include "Common/UI/PopupScreens.h" #include "Common/UI/Root.h" +#include "Common/UI/ScreenManager.h" #include "Core/Config.h" #include "Core/ConfigSettings.h" +#include "UI/MiscScreens.h" #include "UI/MiscViews.h" #include "UI/SettingsPage.h" @@ -191,6 +193,37 @@ SettingItem &SettingsPage::Link(SettingText text, I18NCat cat, std::function settings; + for (const SettingItem &item : items_) { + if (item.ptr && Config::FindSetting(item.ptr)) { + settings.push_back(Restorable{ item.ptr, item.configOnChanged }); + } + } + + SettingsPageHost *host = host_; + SettingItem &item = Choice({ I18NCat::DIALOG, "Restore defaults" }); + item.OnClick([host, settings](UI::EventParams &) { + std::string_view message = T(I18NCat::DIALOG, "RestorePageDefaults", "Restore the settings on this page to their defaults?"); + host->PageScreenManager()->push(new PromptScreen(host->PageGamePath(), message, T(I18NCat::DIALOG, "Reset"), T(I18NCat::DIALOG, "Cancel"), [host, settings](bool yes) { + if (!yes) { + return; + } + for (const Restorable &setting : settings) { + if (Config::RestoreSettingToDefault(setting.ptr) && setting.onChanged) { + setting.onChanged(); + } + } + host->RecreatePageViews(); + })); + }); + return item; +} + SettingItem &SettingsPage::Custom(SettingText text, std::function create) { SettingItem &item = Add(SettingType::Custom, text); item.createCustom = std::move(create); diff --git a/UI/SettingsPage.h b/UI/SettingsPage.h index 5d4c631582..a828eedf1d 100644 --- a/UI/SettingsPage.h +++ b/UI/SettingsPage.h @@ -200,6 +200,9 @@ public: SettingItem &Custom(SettingText text, std::function create); // Opens another page, built by build, on top of this one. The text is the new page's title. SettingItem &Link(SettingText text, I18NCat cat, std::function build); + // After asking, restores the config settings added before it to their defaults. Only the config's + // reactions to the changes are run, not the page's OnChange handlers. Custom items aren't reset. + SettingItem &RestoreDefaults(); I18NCat DefaultCat() const { return defaultCat_; } SettingsPageHost *Host() const { return host_; } diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 06f47a1aa4..f561b2a75a 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -3296,6 +3296,19 @@ bool TestConfigSettings() { EXPECT_EQ_FLOAT(Config::GetDefaultValueFloat(&g_Config.displayLayoutLandscape.fDisplayOffsetY), 0.5f); EXPECT_EQ_FLOAT(Config::GetDefaultValueFloat(&g_Config.displayLayoutPortrait.fDisplayOffsetY), 0.25f); EXPECT_EQ_INT(Config::GetDefaultValueInt(&g_Config.gestureControls[1].iSwipeUp), 0); + + int oldVolume = g_Config.iUIVolume; + g_Config.iUIVolume = 10; + EXPECT_TRUE(Config::RestoreSettingToDefault(&g_Config.iUIVolume)); + EXPECT_EQ_INT(g_Config.iUIVolume, 75); + EXPECT_FALSE(Config::RestoreSettingToDefault(&g_Config.iUIVolume)); + g_Config.iUIVolume = oldVolume; + + float oldOffsetY = g_Config.displayLayoutPortrait.fDisplayOffsetY; + g_Config.displayLayoutPortrait.fDisplayOffsetY = 0.9f; + EXPECT_TRUE(Config::RestoreSettingToDefault(&g_Config.displayLayoutPortrait.fDisplayOffsetY)); + EXPECT_EQ_FLOAT(g_Config.displayLayoutPortrait.fDisplayOffsetY, 0.25f); + g_Config.displayLayoutPortrait.fDisplayOffsetY = oldOffsetY; return true; }