diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 4b3fc81dc7..390277aa69 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -862,13 +862,19 @@ std::string LinearLayoutList::DescribeText() const { } ChoiceGroup::ChoiceGroup(std::string_view title, LayoutParams *layoutParams) - : LinearLayout(ORIENT_VERTICAL, layoutParams ? layoutParams : new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 12, 8, 12))), title_(title) { + : LinearLayout(ORIENT_VERTICAL, layoutParams ? layoutParams : new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)), title_(title) { if (!title.empty()) { Add(new ItemHeader(title)); } SetSpacing(0.0f); } +ChoiceGroupList::ChoiceGroupList(LayoutParams *layoutParams) + : LinearLayoutList(ORIENT_VERTICAL, layoutParams ? layoutParams : new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)) { + SetPadding(Margins(12, 12, 8, 12)); + SetSpacing(24.0f); +} + void ChoiceGroup::Draw(UIContext &dc) { if (views_.empty()) { LinearLayout::Draw(dc); diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index 5cb9affc2f..5c6144c679 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -255,6 +255,7 @@ public: }; // These are pretty vertical LinearLayout, with a title and a roundrect background. +// They have no margins: put them in a ChoiceGroupList, or another layout that spaces them out. class ChoiceGroup : public LinearLayout { public: ChoiceGroup(std::string_view title, LayoutParams *layoutParams = nullptr); @@ -265,6 +266,12 @@ private: std::string title_; }; +// A vertical list that spaces out the ChoiceGroups in it and keeps them off its edges. +class ChoiceGroupList : public LinearLayoutList { +public: + ChoiceGroupList(LayoutParams *layoutParams = nullptr); +}; + // GridLayout is a little different from the Android layout. This one has fixed size // rows and columns, filled row by row. Items are not allowed to deviate from the set sizes. struct GridLayoutSettings { diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index a8e33aa725..80d9bcd55b 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -231,7 +231,12 @@ void ControlMappingScreen::CreateSettingsViews(UI::ViewGroup *parent) { using namespace UI; auto km = GetI18NCategory(I18NCat::KEYMAPPING); - ChoiceGroup *resetChoices = parent->Add(new ChoiceGroup("", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(0, 12)))); + // Use the whole width of the narrow settings column. + ChoiceGroupList *groups = parent->Add(new ChoiceGroupList()); + groups->SetPadding(Margins(0, 12, 0, 0)); + groups->SetSpacing(12.0f); + + ChoiceGroup *resetChoices = groups->Add(new ChoiceGroup("")); resetChoices->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) { KeyMap::ClearAllMappings(); @@ -240,7 +245,7 @@ void ControlMappingScreen::CreateSettingsViews(UI::ViewGroup *parent) { KeyMap::RestoreDefault(); }); - ChoiceGroup *moreChoices = parent->Add(new ChoiceGroup("", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + ChoiceGroup *moreChoices = groups->Add(new ChoiceGroup("")); std::string sysName = System_GetProperty(SYSPROP_NAME); // If there's a builtin controller, restore to default should suffice. No need to conf the controller on top. @@ -533,7 +538,9 @@ void AnalogCalibrationScreen::CreateSettingsViews(UI::ViewGroup *scrollContents) using namespace UI; auto co = GetI18NCategory(I18NCat::CONTROLS); - ChoiceGroup *analogSettings = scrollContents->Add(new ChoiceGroup(co->T("Analog Settings"))); + ChoiceGroupList *groups = scrollContents->Add(new ChoiceGroupList()); + + ChoiceGroup *analogSettings = groups->Add(new ChoiceGroup(co->T("Analog Settings"))); // TODO: Would be nicer if these didn't pop up... analogSettings->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogDeadzone, 0.0f, 0.5f, 0.15f, co->T("Deadzone radius"), 0.01f, screenManager(), "/ 1.0")); @@ -547,7 +554,7 @@ void AnalogCalibrationScreen::CreateSettingsViews(UI::ViewGroup *scrollContents) analogSettings->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogAutoRotSpeed, 0.1f, 20.0f, 8.0f, co->T("Auto-rotation speed"), 1.0f, screenManager())); // Advanced deadzone settings (Steam Input-style). - ChoiceGroup *deadzoneSettings = scrollContents->Add(new ChoiceGroup(co->T("Advanced Deadzone Settings"))); + ChoiceGroup *deadzoneSettings = groups->Add(new ChoiceGroup(co->T("Advanced Deadzone Settings"))); static const char *deadzoneShapes[] = { "Circle", "Square", "Cross" }; deadzoneSettings->Add(new PopupMultiChoice(&g_Config.iAnalogDeadzoneShape, co->T("Deadzone shape"), deadzoneShapes, 0, ARRAY_SIZE(deadzoneShapes), I18NCat::CONTROLS, screenManager())); @@ -560,7 +567,7 @@ void AnalogCalibrationScreen::CreateSettingsViews(UI::ViewGroup *scrollContents) static const char *responseCurves[] = { "Linear", "Aggressive", "Relaxed", "Wide" }; deadzoneSettings->Add(new PopupMultiChoice(&g_Config.iAnalogResponseCurve, co->T("Response curve"), responseCurves, 0, ARRAY_SIZE(responseCurves), I18NCat::CONTROLS, screenManager())); - scrollContents->Add(new ChoiceGroup(""))->Add(new Choice(co->T("Reset to defaults")))->OnClick.Handle(this, &AnalogCalibrationScreen::OnResetToDefaults); + groups->Add(new ChoiceGroup(""))->Add(new Choice(co->T("Reset to defaults")))->OnClick.Handle(this, &AnalogCalibrationScreen::OnResetToDefaults); } void AnalogCalibrationScreen::CreateContentViews(UI::ViewGroup *parent) { diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index 3abef446c1..8813f25416 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -159,7 +159,7 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { ScrollView *rightScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f)); root__->Add(rightScroll); - LinearLayout *vertLayout = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *vertLayout = new ChoiceGroupList(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); rightScroll->Add(vertLayout); ChoiceGroup *styleSettings = vertLayout->Add(new ChoiceGroup(co->T("Button style"))); diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 4ccb296ea2..b98f92a0bc 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -222,8 +222,9 @@ void CwCheatScreen::CreateSettingsViews(UI::ViewGroup *leftColumn) { auto di = GetI18NCategory(I18NCat::DIALOG); auto mm = GetI18NCategory(I18NCat::MAINMENU); + ChoiceGroupList *groups = leftColumn->Add(new ChoiceGroupList()); //leftColumn->Add(new Choice(cw->T("Add Cheat")))->OnClick.Handle(this, &CwCheatScreen::OnAddCheat); - ChoiceGroup *importGroup = leftColumn->Add(new ChoiceGroup(cw->T("Import Cheats"))); + ChoiceGroup *importGroup = groups->Add(new ChoiceGroup(cw->T("Import Cheats"))); importGroup->Add(new Choice(cw->T("Download cheat database")))->OnClick.Add([this](UI::EventParams &) { screenManager()->push(new CwCheatDownloadPopupScreen()); @@ -238,7 +239,7 @@ void CwCheatScreen::CreateSettingsViews(UI::ViewGroup *leftColumn) { importGroup->Add(new Choice(title))->OnClick.Handle(this, &CwCheatScreen::OnImportCheat); importGroup->Add(new Choice(mm->T("Browse"), ImageID("I_FOLDER_OPEN")))->OnClick.Handle(this, &CwCheatScreen::OnImportBrowse); - ChoiceGroup *optionsGroup = leftColumn->Add(new ChoiceGroup(di->T("Options"))); + ChoiceGroup *optionsGroup = groups->Add(new ChoiceGroup(di->T("Options"))); Choice *searchChoice = optionsGroup->Add(new Choice(di->T("Search"), ImageID("I_SEARCH"))); searchChoice->OnClick.Add([this, searchChoice, screenManager = screenManager(), token = GetRequesterToken()](UI::EventParams &) { auto di = GetI18NCategory(I18NCat::DIALOG); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index d8bcef71af..67f4b7b67e 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -456,7 +456,7 @@ void JitDebugScreen::CreateViews() { topbar->Add(new Choice(di->T("Enable All")))->OnClick.Handle(this, &JitDebugScreen::OnEnableAll); vert->Add(topbar); - ChoiceGroup *disabledFlags = vert->Add(new ChoiceGroup(dev->T("Disabled JIT functionality"))); + ChoiceGroup *disabledFlags = vert->Add(new ChoiceGroupList())->Add(new ChoiceGroup(dev->T("Disabled JIT functionality"))); for (auto flag : jitDisableFlags) { // Do not add translation of these. @@ -639,7 +639,7 @@ void FrameDumpTestScreen::CreateTabs() { auto dev = GetI18NCategory(I18NCat::DEVELOPER); AddTab("General", dev->T("Dumps"), [this](UI::LinearLayout *parent) { - ChoiceGroup *dumps = parent->Add(new ChoiceGroup("GE Frame Dumps")); + ChoiceGroup *dumps = parent->Add(new ChoiceGroupList())->Add(new ChoiceGroup("GE Frame Dumps")); for (auto &file : files_) { std::string url = framedumpsBaseUrl + file; diff --git a/UI/DeveloperToolsScreen.cpp b/UI/DeveloperToolsScreen.cpp index db1c5dbc49..8a36a73f3e 100644 --- a/UI/DeveloperToolsScreen.cpp +++ b/UI/DeveloperToolsScreen.cpp @@ -649,41 +649,41 @@ void DeveloperToolsScreen::CreateTabs() { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); AddTab("General", sy->T("General"), [this](UI::LinearLayout *parent) { - CreateGeneralTab(parent); + CreateGeneralTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("TextureReplacement", dev->T("Texture Replacement"), [this](UI::LinearLayout *parent) { - CreateTextureReplacementTab(parent); + CreateTextureReplacementTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("Graphics", ms->T("Graphics"), [this](UI::LinearLayout *parent) { - CreateGraphicsTab(parent); + CreateGraphicsTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("Networking", ms->T("Networking"), [this](UI::LinearLayout *parent) { - CreateNetworkTab(parent); + CreateNetworkTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("Audio", ms->T("Audio"), [this](UI::LinearLayout *parent) { - CreateAudioTab(parent); + CreateAudioTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("Tests", dev->T("Tests"), [this](UI::LinearLayout *parent) { - CreateTestsTab(parent); + CreateTestsTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("UI", dev->T("UI"), [this](UI::LinearLayout *parent) { - CreateUITab(parent); + CreateUITab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("DumpFiles", dev->T("Dump files"), [this](UI::LinearLayout *parent) { - CreateDumpFileTab(parent); + CreateDumpFileTab(parent->Add(new UI::ChoiceGroupList())); }); AddTab("HLE", dev->T("HLE"), [this](UI::LinearLayout *parent) { - CreateHLETab(parent); + CreateHLETab(parent->Add(new UI::ChoiceGroupList())); }); #if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH) AddTab("MIPSTracer", dev->T("MIPSTracer"), [this](UI::LinearLayout *parent) { - CreateMIPSTracerTab(parent); + CreateMIPSTracerTab(parent->Add(new UI::ChoiceGroupList())); }); #endif //#if PPSSPP_PLATFORM(ANDROID) if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) { AddTab("Crash history", dev->T("Crash history"), [this](UI::LinearLayout *parent) { - CreateCrashHistoryTab(parent); + CreateCrashHistoryTab(parent->Add(new UI::ChoiceGroupList())); }); } //#endif diff --git a/UI/DriverManagerScreen.cpp b/UI/DriverManagerScreen.cpp index b42868fcd3..51ae7a6950 100644 --- a/UI/DriverManagerScreen.cpp +++ b/UI/DriverManagerScreen.cpp @@ -165,7 +165,7 @@ void DriverManagerScreen::CreateDriverTab(UI::ViewGroup *drivers) { auto di = GetI18NCategory(I18NCat::DIALOG); auto gr = GetI18NCategory(I18NCat::GRAPHICS); - ChoiceGroup *driverManagerGroup = drivers->Add(new ChoiceGroup(gr->T("AdrenoTools driver manager"))); + ChoiceGroup *driverManagerGroup = drivers->Add(new ChoiceGroupList())->Add(new ChoiceGroup(gr->T("AdrenoTools driver manager"))); auto customDriverInstallChoice = driverManagerGroup->Add(new Choice(gr->T("Install custom driver..."))); driverManagerGroup->Add(new Choice(di->T("More info")))->OnClick.Add([=](UI::EventParams &e) { System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/docs/reference/custom-drivers/"); diff --git a/UI/FirmwareScreen.cpp b/UI/FirmwareScreen.cpp index 0eae28db27..5dc760f947 100644 --- a/UI/FirmwareScreen.cpp +++ b/UI/FirmwareScreen.cpp @@ -113,19 +113,20 @@ void FirmwareScreen::CreateSettingsViews(UI::ViewGroup *parent) { // that has flash0 mounted is worse - so while a game is running, this screen is read-only. const bool gameRunning = PSP_IsInited(); + ChoiceGroupList *groups = parent->Add(new ChoiceGroupList()); if (info_.hasVsh && !FirmwareVersionSupportsVSH(info_.version)) { - parent->Add(new NoticeView(NoticeLevel::WARN, sy->T("XMB unsupported"), + groups->Add(new NoticeView(NoticeLevel::WARN, sy->T("XMB unsupported"), sy->T("XMBUnsupportedVersion", "Only firmware 6.61 can boot the XMB so far"))); } - ChoiceGroup *launchGroup = parent->Add(new ChoiceGroup("")); + ChoiceGroup *launchGroup = groups->Add(new ChoiceGroup("")); Choice *launch = launchGroup->Add(new Choice(sy->T("Launch XMB"), ImageID("I_PLAY"))); - ChoiceGroup *settings = parent->Add(new ChoiceGroup("")); + ChoiceGroup *settings = groups->Add(new ChoiceGroup("")); settings->Add(new CheckBox(&g_Config.bAutoUpgradeFirmware, sy->T("Install or upgrade firmware from game discs"), sy->T("AutoUpgradeFirmwareDesc", "Most game discs carry a firmware updater. Install it when there's no firmware yet, or when the disc's is newer."))); - ChoiceGroup *actions = parent->Add(new ChoiceGroup("")); + ChoiceGroup *actions = groups->Add(new ChoiceGroup("")); launch->OnClick.Add([this](UI::EventParams &) { LaunchVSH(); }); @@ -154,7 +155,7 @@ void FirmwareScreen::CreateSettingsViews(UI::ViewGroup *parent) { } if (gameRunning) { - parent->Add(new NoticeView(NoticeLevel::INFO, + groups->Add(new NoticeView(NoticeLevel::INFO, sy->T("FirmwareNeedsNoGame", "Stop the game to change the installed firmware"), "")); } } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 42cfd23290..4a01b83634 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -246,43 +246,43 @@ void GameSettingsScreen::CreateTabs() { AddTab("GameSettingsGraphics", ms->T("Graphics"), ImageID("I_DISPLAY"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("Graphics"), "graphics", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); - CreateGraphicsSettings(parent); + CreateGraphicsSettings(parent->Add(new ChoiceGroupList())); }); AddTab("GameSettingsControls", ms->T("Controls"), ImageID("I_CONTROLLER"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("Controls"), "controls")); - CreateControlsSettings(parent); + CreateControlsSettings(parent->Add(new ChoiceGroupList())); }); AddTab("GameSettingsAudio", ms->T("Audio"), ImageID("I_SPEAKER_MAX"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("Audio"), "audio")); - CreateAudioSettings(parent); + CreateAudioSettings(parent->Add(new ChoiceGroupList())); }); AddTab("GameSettingsNetworking", ms->T("Networking"), ImageID("I_WIFI"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("Networking"), "network")); - CreateNetworkingSettings(parent); + CreateNetworkingSettings(parent->Add(new ChoiceGroupList())); }); AddTab("GameSettingsTools", ms->T("Tools"), ImageID("I_TOOLS"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("Tools"), "tools")); - CreateToolsSettings(parent); + CreateToolsSettings(parent->Add(new ChoiceGroupList())); }); AddTab("GameSettingsSystem", ms->T("System"), ImageID("I_PSP"), [this](UI::LinearLayout *parent) { auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); parent->Add(new PaneTitleBar(gamePath_, ms->T("System"), "system")); - CreateSystemSettings(parent); + CreateSystemSettings(parent->Add(new ChoiceGroupList())); }); int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE); if ((deviceType == DEVICE_TYPE_VR) || g_Config.bForceVR) { AddTab("GameSettingsVR", ms->T("VR"), ImageID::invalid(), [this](UI::LinearLayout *parent) { - CreateVRSettings(parent); + CreateVRSettings(parent->Add(new ChoiceGroupList())); }); } } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 5812310634..da8f804543 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -370,7 +370,11 @@ void MainScreen::CreateMainButtons(UI::ViewGroup *parent, bool portrait) { } return; } else { - ChoiceGroup *mainButtons = parent->Add(new ChoiceGroup("", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(0, 0, 0, 12)))); + ChoiceGroupList *buttons = parent->Add(new ChoiceGroupList()); + buttons->SetPadding(Margins(0)); + buttons->SetSpacing(12.0f); + + ChoiceGroup *mainButtons = buttons->Add(new ChoiceGroup("")); if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { mainButtons->Add(new Choice(mm->T("Load", "Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile); @@ -394,7 +398,7 @@ void MainScreen::CreateMainButtons(UI::ViewGroup *parent, bool portrait) { // Officially, iOS apps should not have exit buttons. Remove it to maximize app store review chances. // (The exit button previously created problems on Android, but now we allow it in landscape mode.) #if !PPSSPP_PLATFORM(IOS_APP_STORE) - Choice *exitButton = parent->Add(new Choice(di->T("Exit"))); + Choice *exitButton = buttons->Add(new Choice(di->T("Exit"))); exitButton->OnClick.Add([](UI::EventParams &e) { // Let's make sure the config was saved, since it may not have been. if (!g_Config.Save("MainScreen::OnExit")) { diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index e8542136ce..56da81bb44 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -711,10 +711,9 @@ void GamePauseScreen::CreateViews() { root_->Add(buttonColumnScroll); } - LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *rightColumnItems = new ChoiceGroupList(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); buttonColumn->Add(rightColumnItems); - rightColumnItems->SetSpacing(0.0f); ChoiceGroup *topGroup = rightColumnItems->Add(new ChoiceGroup("")); if (__KernelIsRunningVSH()) { // The XMB notices discs coming and going by itself, like a PSP's. @@ -795,7 +794,6 @@ void GamePauseScreen::CreateViews() { } if (portrait) { - rightColumnItems->Add(new Spacer(20.0)); UI::LinearLayout *exitRow = new UI::LinearLayout(ORIENT_HORIZONTAL, new UI::LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(0, 0, 0, 0))); rightColumnItems->Add(exitRow); exitRow->Add(exit); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index fe0f0958b8..c28d4f0291 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -285,7 +285,7 @@ void RemoteISOScreen::CreateTabs() { }); AddTab("Settings", ri->T("Settings"), [this](UI::LinearLayout *settings) { - CreateSettingsTab(settings); + CreateSettingsTab(settings->Add(new UI::ChoiceGroupList())); }); } @@ -403,7 +403,7 @@ void RemoteISOScreen::CreateSettingsTab(UI::ViewGroup *remoteisoSettings) { g_Config.iRemoteISOShareType = (int)RemoteISOShareType::RECENT; } - ChoiceGroup *remoteSettings = new ChoiceGroup(""); + ChoiceGroup *remoteSettings = remoteisoSettings->Add(new ChoiceGroup("")); UI::Choice *remoteServer = new PopupTextInputChoice(GetRequesterToken(), &g_Config.sLastRemoteISOServer, ri->T("Remote Server"), "", 255, screenManager()); remoteSettings->Add(remoteServer); @@ -421,7 +421,7 @@ void RemoteISOScreen::CreateSettingsTab(UI::ViewGroup *remoteisoSettings) { remoteSettings->Add(remoteSubdir); remoteSubdir->SetEnabledPtr(&g_Config.bRemoteISOManual); - ChoiceGroup *serverSettings = new ChoiceGroup(""); + ChoiceGroup *serverSettings = remoteisoSettings->Add(new ChoiceGroup("")); PopupSliderChoice *portChoice = new PopupSliderChoice(&g_Config.iRemoteISOPort, 0, 65535, 0, ri->T("Local Server Port", "Local Server Port"), 100, screenManager()); serverSettings->Add(portChoice); diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index c83d721c9b..8bf03d1cec 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -442,7 +442,8 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) } using namespace UI; - ChoiceGroup *settingsGroup = viewGroup->Add(new ChoiceGroup(di->T("Settings"))); + ChoiceGroupList *groups = viewGroup->Add(new ChoiceGroupList()); + ChoiceGroup *settingsGroup = groups->Add(new ChoiceGroup(di->T("Settings"))); settingsGroup->Add(new CheckBox(&g_Config.bAchievementsEnable, ac->T("Achievements enabled")))->OnClick.Add([this](UI::EventParams &e) -> void { Achievements::UpdateSettings(); RecreateViews(); @@ -450,7 +451,7 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) settingsGroup->Add(new CheckBox(&g_Config.bAchievementsHardcoreMode, ac->T("Hardcore Mode (no savestates)")))->SetEnabledPtr(&g_Config.bAchievementsEnable); settingsGroup->Add(new CheckBox(&g_Config.bAchievementsSoundEffects, ac->T("Sound Effects")))->SetEnabledPtr(&g_Config.bAchievementsEnable); - ChoiceGroup *linksGroup = viewGroup->Add(new ChoiceGroup(di->T("Links"))); + ChoiceGroup *linksGroup = groups->Add(new ChoiceGroup(di->T("Links"))); linksGroup->Add(new Choice(ac->T("RetroAchievements website"), ImageID("I_LINK_OUT")))->OnClick.Add([](UI::EventParams &) -> void { System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.retroachievements.org/"); }); @@ -466,7 +467,8 @@ void RetroAchievementsSettingsScreen::CreateCustomizeTab(UI::ViewGroup *viewGrou viewGroup->Add(new PaneTitleBar(Path(), ac->T("Customize"), "/docs/reference/retro-achievements/")); using namespace UI; - ChoiceGroup *soundSettings = viewGroup->Add(new ChoiceGroup(ac->T("Sound Effects"))); + ChoiceGroupList *groups = viewGroup->Add(new ChoiceGroupList()); + ChoiceGroup *soundSettings = groups->Add(new ChoiceGroup(ac->T("Sound Effects"))); if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { soundSettings->Add(new AudioFileChooser(GetRequesterToken(), &g_Config.sAchievementsUnlockAudioFile, ac->T("Achievement unlocked"), UISound::ACHIEVEMENT_UNLOCKED)); soundSettings->Add(new AudioFileChooser(GetRequesterToken(), &g_Config.sAchievementsLeaderboardSubmitAudioFile, ac->T("Leaderboard score submission"), UISound::LEADERBOARD_SUBMITTED)); @@ -478,7 +480,7 @@ void RetroAchievementsSettingsScreen::CreateCustomizeTab(UI::ViewGroup *viewGrou static const char *positions[] = { "None", "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right" }; - ChoiceGroup *notificationSettings = viewGroup->Add(new ChoiceGroup(ac->T("Notifications"))); + ChoiceGroup *notificationSettings = groups->Add(new ChoiceGroup(ac->T("Notifications"))); notificationSettings->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardStartedOrFailedPos, ac->T("Leaderboard attempt started or failed"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable); notificationSettings->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardSubmittedPos, ac->T("Leaderboard result submitted"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable); notificationSettings->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable); @@ -494,7 +496,7 @@ void RetroAchievementsSettingsScreen::CreateDeveloperToolsTab(UI::ViewGroup *vie using namespace UI; viewGroup->Add(new PaneTitleBar(Path(), sy->T("Developer Tools"), "/docs/reference/retro-achievements/")); - ChoiceGroup *devSettings = viewGroup->Add(new ChoiceGroup("")); + ChoiceGroup *devSettings = viewGroup->Add(new ChoiceGroupList())->Add(new ChoiceGroup("")); #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION devSettings->Add(new CheckBox(&g_Config.bAchievementsEnableRAIntegration, ac->T("Enable RAIntegration (for achievement development)")))->SetEnabledPtr(&g_Config.bAchievementsEnable); #endif diff --git a/UI/TabbedDialogScreen.cpp b/UI/TabbedDialogScreen.cpp index 649e76f946..4c785b4b86 100644 --- a/UI/TabbedDialogScreen.cpp +++ b/UI/TabbedDialogScreen.cpp @@ -130,7 +130,7 @@ void UITabbedBaseDialogScreen::CreateViews() { AddTab("GameSettingsSearch", ms->T("Search"), ImageID("I_SEARCH"), [this](UI::LinearLayout *searchSettings) { auto se = GetI18NCategory(I18NCat::SEARCH); - ChoiceGroup *searchGroup = searchSettings->Add(new ChoiceGroup(se->T("Find settings"))); + ChoiceGroup *searchGroup = searchSettings->Add(new ChoiceGroupList())->Add(new ChoiceGroup(se->T("Find settings"))); searchGroup->Add(new PopupTextInputChoice(GetRequesterToken(), &searchFilter_, se->T("Filter"), "", 64, screenManager()))->OnChange.Add([this](UI::EventParams &e) { System_PostUIMessage(UIMessage::GAMESETTINGS_SEARCH, StripSpaces(searchFilter_)); }); @@ -185,6 +185,36 @@ void UITabbedBaseDialogScreen::SetCurrentTab(int tab) { tabHolder_->SetCurrentTab(tab); } +// Hides the views in the group that don't match the filter, and returns whether any did. Groups that can't +// be focused (like ChoiceGroups) are searched recursively and stay visible if anything in them matches. +// Other views that can't be focused (headings, notices) are shown only when something after them matches. +static bool ApplySearchFilterToGroup(UI::ViewGroup *group, int firstIndex, const std::string &filter) { + using namespace UI; + bool anyMatch = false; + View *lastHeading = nullptr; + for (int i = firstIndex; i < group->GetNumSubviews(); ++i) { + View *v = group->GetViewByIndex(i); + bool match; + if (v->IsViewGroup() && !v->CanBeFocused()) { + match = ApplySearchFilterToGroup((ViewGroup *)v, 0, filter); + } else if (!v->CanBeFocused()) { + lastHeading = v; + v->SetVisibility(filter.empty() ? V_VISIBLE : V_GONE); + continue; + } else { + std::string label = v->DescribeText(); + std::transform(label.begin(), label.end(), label.begin(), tolower); + match = label.find(filter) != label.npos; + } + if (match && lastHeading) { + lastHeading->SetVisibility(V_VISIBLE); + } + v->SetVisibility((filter.empty() || match) ? V_VISIBLE : V_GONE); + anyMatch = anyMatch || match; + } + return anyMatch; +} + void UITabbedBaseDialogScreen::ApplySearchFilter() { using namespace UI; // GAMESETTINGS_SEARCH is broadcast to every screen, including ones whose views don't exist yet. @@ -222,24 +252,8 @@ void UITabbedBaseDialogScreen::ApplySearchFilter() { continue; } - bool tabMatches = searchFilter_.empty(); - - UI::View *lastHeading = nullptr; - for (int i = 1; i < tabContents->GetNumSubviews(); ++i) { - UI::View *v = tabContents->GetViewByIndex(i); - if (!v->CanBeFocused()) { - lastHeading = v; - } - - std::string label = v->DescribeText(); - std::transform(label.begin(), label.end(), label.begin(), tolower); - bool match = v->CanBeFocused() && label.find(searchFilter_) != label.npos; - tabMatches = tabMatches || match; - - if (match && lastHeading) - lastHeading->SetVisibility(UI::V_VISIBLE); - v->SetVisibility((searchFilter_.empty() || match) ? UI::V_VISIBLE : UI::V_GONE); - } + // The first view is the tab's title. + const bool tabMatches = ApplySearchFilterToGroup((ViewGroup *)tabContents, 1, searchFilter_) || searchFilter_.empty(); tabHolder_->EnableTab(t, tabMatches); matches = matches || tabMatches; } diff --git a/UI/TiltAnalogSettingsScreen.cpp b/UI/TiltAnalogSettingsScreen.cpp index 846ebed8ff..e84234529c 100644 --- a/UI/TiltAnalogSettingsScreen.cpp +++ b/UI/TiltAnalogSettingsScreen.cpp @@ -96,11 +96,12 @@ void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) { GamepadUpdateOpacity(1.0f); + ChoiceGroupList *groups = settings->Add(new ChoiceGroupList()); auto enabledFunc = [=]() -> bool { return g_Config.iTiltInputType != 0; }; - ChoiceGroup *setupSettings = settings->Add(new ChoiceGroup(co->T("Tilt control setup"))); + ChoiceGroup *setupSettings = groups->Add(new ChoiceGroup(co->T("Tilt control setup"))); CheckBox *enabled = setupSettings->Add(new CheckBox(&g_Config.bTiltInputEnabled, co->T("Enabled"))); PopupMultiChoice *typeChoice = setupSettings->Add(new PopupMultiChoice(&g_Config.iTiltInputType, co->T("Tilt Input Type"), g_tiltTypes, 0, g_numTiltTypes, I18NCat::CONTROLS, screenManager())); if (g_Config.iTiltInputType != 0) { @@ -117,7 +118,7 @@ void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) { if (System_GetPropertyBool(SYSPROP_HAS_GYRO)) { setupSettings->Add(new CheckBox(&g_Config.bTiltUseGyro, co->T("Use gyroscope")))->SetEnabledPtr(&g_Config.bTiltInputEnabled); } - ChoiceGroup *calibrationSettings = settings->Add(new ChoiceGroup(co->T("Calibration"))); + ChoiceGroup *calibrationSettings = groups->Add(new ChoiceGroup(co->T("Calibration"))); Choice *calibrate = new Choice(co->T("Calibrate")); calibrate->OnClick.Handle(this, &TiltAnalogSettingsScreen::OnCalibrate); calibrate->SetEnabledPtr(&g_Config.bTiltInputEnabled); @@ -125,7 +126,7 @@ void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) { SettingHint *calibrationInfo = new SettingHint(co->T("To Calibrate", "Hold device at your preferred angle and press Calibrate."), calibrate); calibrationSettings->Add(calibrationInfo); - ChoiceGroup *sensitivitySettings = settings->Add(new ChoiceGroup(co->T("Sensitivity"))); + ChoiceGroup *sensitivitySettings = groups->Add(new ChoiceGroup(co->T("Sensitivity"))); if (TiltTypeIsAnalog(g_Config.iTiltInputType)) { sensitivitySettings->Add(new PopupSliderChoiceFloat(&g_Config.fTiltAnalogDeadzoneRadius, 0.0f, 0.8f, 0.0f, co->T("Deadzone radius"), 0.02f, screenManager(), "/ 1.0"))->SetEnabledPtr(&g_Config.bTiltInputEnabled); sensitivitySettings->Add(new PopupSliderChoiceFloat(&g_Config.fTiltInverseDeadzone, 0.0f, 0.8f, 0.0f, co->T("Low end radius"), 0.02f, screenManager(), "/ 1.0"))->SetEnabledPtr(&g_Config.bTiltInputEnabled); @@ -134,7 +135,7 @@ void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) { sensitivitySettings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivityX, 0, 100, 60, co->T("Tilt Sensitivity along X axis"), screenManager(), "%"))->SetEnabledPtr(&g_Config.bTiltInputEnabled); sensitivitySettings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivityY, 0, 100, 60, co->T("Tilt Sensitivity along Y axis"), screenManager(), "%"))->SetEnabledPtr(&g_Config.bTiltInputEnabled); - ChoiceGroup *invertSettings = settings->Add(new ChoiceGroup(co->T("Invert Axes"))); + ChoiceGroup *invertSettings = groups->Add(new ChoiceGroup(co->T("Invert Axes"))); invertSettings->Add(new CheckBox(&g_Config.bInvertTiltX, co->T("Invert Tilt along X axis")))->SetEnabledPtr(&g_Config.bTiltInputEnabled); invertSettings->Add(new CheckBox(&g_Config.bInvertTiltY, co->T("Invert Tilt along Y axis")))->SetEnabledPtr(&g_Config.bTiltInputEnabled); }