From 1d23a58a4e10bd2ea684ae3a285814f0a242d8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Nov 2025 19:06:22 +0100 Subject: [PATCH 1/5] Fix control mapping screen in portrait --- UI/ControlMappingScreen.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 856ef8bc71..db8af3610c 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -57,7 +57,7 @@ using KeyMap::MultiInputMapping; class SingleControlMapper : public UI::LinearLayout { public: - SingleControlMapper(int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams = nullptr); + SingleControlMapper(int pspKey, std::string keyName, bool portrait, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams = nullptr); ~SingleControlMapper() { g_IsMappingMouseInput = false; } @@ -79,10 +79,11 @@ private: std::vector rows_; std::string keyName_; ScreenManager *scrm_; + bool portrait_; }; -SingleControlMapper::SingleControlMapper(int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams) - : UI::LinearLayout(ORIENT_VERTICAL, layoutParams), pspKey_(pspKey), keyName_(keyName), scrm_(scrm) { +SingleControlMapper::SingleControlMapper(int pspKey, std::string keyName, bool portrait, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams) + : UI::LinearLayout(ORIENT_VERTICAL, layoutParams), pspKey_(pspKey), keyName_(keyName), scrm_(scrm), portrait_(portrait) { Refresh(); } @@ -129,7 +130,7 @@ void SingleControlMapper::Refresh() { p->OnClick.Handle(this, &SingleControlMapper::OnAddMouse); } - LinearLayout *rightColumn = root->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(rightColumnWidth, WRAP_CONTENT))); + LinearLayout *rightColumn = root->Add(new LinearLayout(ORIENT_VERTICAL, portrait_ ? new LinearLayoutParams(1.0f) : new LinearLayoutParams(rightColumnWidth, WRAP_CONTENT))); rightColumn->SetSpacing(2.0f); std::vector mappings; KeyMap::InputMappingsFromPspButton(pspKey_, &mappings, false); @@ -261,6 +262,8 @@ void ControlMappingScreen::CreateTabs() { size_t numMappableKeys = 0; const KeyMap::KeyMap_IntStrPair *mappableKeys = KeyMap::GetMappableKeys(&numMappableKeys); + bool portrait = this->GetDeviceOrientation() == DeviceOrientation::Portrait; + auto km = GetI18NCategory(I18NCat::KEYMAPPING); int curCat = -1; @@ -275,7 +278,7 @@ void ControlMappingScreen::CreateTabs() { curSection->SetSpacing(6.0f); } SingleControlMapper *mapper = curSection->Add( - new SingleControlMapper(mappableKeys[i].key, mappableKeys[i].name, screenManager(), + new SingleControlMapper(mappableKeys[i].key, mappableKeys[i].name, portrait, screenManager(), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); mapper->SetTag(StringFromFormat("KeyMap%s", mappableKeys[i].name)); mappers_.push_back(mapper); From 2c75946e6cfe2ca9e8dd2166940ce881be0b4e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Nov 2025 19:07:52 +0100 Subject: [PATCH 2/5] Switch ControlMappingScreen to a two-pane layout --- UI/ControlMappingScreen.cpp | 74 +++++++++++++++++---------------- UI/ControlMappingScreen.h | 11 ++--- UI/SimpleDialogScreen.cpp | 12 +++++- UI/TouchControlLayoutScreen.cpp | 2 +- 4 files changed, 55 insertions(+), 44 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index db8af3610c..b887abeca1 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -107,7 +107,7 @@ void SingleControlMapper::Refresh() { float itemH = 55.0f; float leftColumnWidth = 200; - float rightColumnWidth = 350; // TODO: Should be flexible somehow. Maybe we need to implement Measure. + float rightColumnWidth = 350; LinearLayout *root = Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); root->SetSpacing(3.0f); @@ -233,61 +233,63 @@ static const BindingCategory cats[] = { {}, // sentinel }; -void ControlMappingScreen::CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) { + +void ControlMappingScreen::CreateSettingsViews(UI::ViewGroup *parent) { using namespace UI; auto km = GetI18NCategory(I18NCat::KEYMAPPING); - verticalLayout->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) { + parent->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) { KeyMap::ClearAllMappings(); }); - verticalLayout->Add(new Choice(km->T("Default All")))->OnClick.Add([](UI::EventParams &) { + parent->Add(new Choice(km->T("Default All")))->OnClick.Add([](UI::EventParams &) { KeyMap::RestoreDefault(); }); 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. if (!KeyMap::HasBuiltinController(sysName) && KeyMap::GetSeenPads().size()) { - verticalLayout->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure); + parent->Add(new Choice(km->T("Autoconfigure")))->OnClick.Handle(this, &ControlMappingScreen::OnAutoConfigure); } - verticalLayout->Add(new CheckBox(&g_Config.bAllowMappingCombos, km->T("Allow combo mappings"))); - verticalLayout->Add(new CheckBox(&g_Config.bStrictComboOrder, km->T("Strict combo input order"))); - verticalLayout->Add(new Spacer(12.0f)); + parent->Add(new CheckBox(&g_Config.bAllowMappingCombos, km->T("Allow combo mappings"))); + parent->Add(new CheckBox(&g_Config.bStrictComboOrder, km->T("Strict combo input order"))); } -void ControlMappingScreen::CreateTabs() { +std::string_view ControlMappingScreen::GetTitle() const { + auto co = GetI18NCategory(I18NCat::CONTROLS); + return co->T("Control mapping"); +} + +void ControlMappingScreen::CreateContentViews(UI::ViewGroup *parent) { using namespace UI; + + LinearLayout *rootLayout = parent->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); mappers_.clear(); + size_t numMappableKeys = 0; + const KeyMap::KeyMap_IntStrPair *mappableKeys = KeyMap::GetMappableKeys(&numMappableKeys); + auto km = GetI18NCategory(I18NCat::KEYMAPPING); - AddTab("keymap", "Control mapping", [this](UI::LinearLayout *parent) { - size_t numMappableKeys = 0; - const KeyMap::KeyMap_IntStrPair *mappableKeys = KeyMap::GetMappableKeys(&numMappableKeys); + bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; - bool portrait = this->GetDeviceOrientation() == DeviceOrientation::Portrait; - - auto km = GetI18NCategory(I18NCat::KEYMAPPING); - - int curCat = -1; - CollapsibleSection *curSection = nullptr; - for (size_t i = 0; i < numMappableKeys; i++) { - if (curCat < (int)ARRAY_SIZE(cats) && mappableKeys[i].key == cats[curCat + 1].firstKey) { - if (curCat >= 0) { - curSection->SetOpenPtr(&categoryToggles_[curCat]); - } - curCat++; - curSection = parent->Add(new CollapsibleSection(km->T(cats[curCat].catName))); - curSection->SetSpacing(6.0f); + int curCat = -1; + CollapsibleSection *curSection = nullptr; + for (size_t i = 0; i < numMappableKeys; i++) { + if (curCat < (int)ARRAY_SIZE(cats) && mappableKeys[i].key == cats[curCat + 1].firstKey) { + if (curCat >= 0) { + curSection->SetOpenPtr(&categoryToggles_[curCat]); } - SingleControlMapper *mapper = curSection->Add( - new SingleControlMapper(mappableKeys[i].key, mappableKeys[i].name, portrait, screenManager(), - new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); - mapper->SetTag(StringFromFormat("KeyMap%s", mappableKeys[i].name)); - mappers_.push_back(mapper); + curCat++; + curSection = rootLayout->Add(new CollapsibleSection(km->T(cats[curCat].catName))); + curSection->SetSpacing(6.0f); } - if (curCat >= 0 && curSection) { - curSection->SetOpenPtr(&categoryToggles_[curCat]); - } - _dbg_assert_(curCat == ARRAY_SIZE(cats) - 2); // count the sentinel - }, TabFlags::Default); + SingleControlMapper *mapper = curSection->Add( + new SingleControlMapper(mappableKeys[i].key, mappableKeys[i].name, portrait, screenManager())); + mapper->SetTag(StringFromFormat("KeyMap%s", mappableKeys[i].name)); + mappers_.push_back(mapper); + } + if (curCat >= 0 && curSection) { + curSection->SetOpenPtr(&categoryToggles_[curCat]); + } + _dbg_assert_(curCat == ARRAY_SIZE(cats) - 2); // count the sentinel keyMapGeneration_ = KeyMap::g_controllerMapGeneration; } diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 4741fac0dc..e65649fd20 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -37,9 +37,9 @@ class SingleControlMapper; -class ControlMappingScreen : public UITabbedBaseDialogScreen { +class ControlMappingScreen : public UITwoPaneBaseDialogScreen { public: - ControlMappingScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath, TabDialogFlags::ContextMenuInPortrait) { + ControlMappingScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::SettingsInContextMenu | TwoPaneFlags::ContentsCanScroll) { categoryToggles_[0] = true; categoryToggles_[1] = true; categoryToggles_[2] = true; @@ -48,13 +48,14 @@ public: const char *tag() const override { return "ControlMapping"; } protected: - void CreateTabs() override; - void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override; + void CreateSettingsViews(UI::ViewGroup *parent) override; + void CreateContentViews(UI::ViewGroup *parent) override; void update() override; + std::string_view GetTitle() const override; + private: void OnAutoConfigure(UI::EventParams ¶ms); - bool ShowSearchControls() const override { return false; } void dialogFinished(const Screen *dialog, DialogResult result) override; diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index 452200f55f..4eec0d1a22 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -36,9 +36,17 @@ void UITwoPaneBaseDialogScreen::CreateViews() { BeforeCreateViews(); - auto createContentViews = [this](UI::ViewGroup *parent) { + auto createContentViews = [this, portrait](UI::ViewGroup *parent) { if (flags_ & TwoPaneFlags::ContentsCanScroll) { - ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f, Margins(8))); + Margins margins(8, 8, 8, 0); + if (flags_ & TwoPaneFlags::SettingsToTheRight) { + // If settings are in context menu, we want to avoid double margins on the sides. + margins.left = 0; + } else { + margins.right = 0; + } + + ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f, margins)); parent->Add(contentScroll); CreateContentViews(contentScroll); } else { diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 26cb9f78bb..02058eeebf 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -676,7 +676,7 @@ void TouchControlLayoutScreen::CreateViews() { leftColumn->Add(new Spacer(0.0f)); LinearLayout* rightColumn = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f, Margins(0.0f, 12.0f, 12.0f, 12.0f)))); - rightColumn->Add(new TextView(co->T(DeviceOrientationToString(orientation)))); + rightColumn->Add(new TextView(co->T(DeviceOrientationToString(orientation))))->SetTextSize(TextSize::Small); rightColumn->Add(new Spacer(new LinearLayoutParams(1.0))); float previewHeight = bounds.h * layoutAreaScale; layoutView_ = rightColumn->Add(new ControlLayoutView(GetDeviceOrientation(), new LinearLayoutParams(FILL_PARENT, previewHeight))); From 59ad5b4be4afeb197c3fb7b5b5a9baa5a32f19a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Nov 2025 19:27:57 +0100 Subject: [PATCH 3/5] More UI refactor, fixes to control customization screens --- UI/CustomButtonMappingScreen.cpp | 15 ++++++------ UI/CustomButtonMappingScreen.h | 10 ++++---- UI/MiscScreens.h | 3 +-- UI/ReportScreen.cpp | 2 +- UI/SimpleDialogScreen.cpp | 2 +- UI/SimpleDialogScreen.h | 11 ++++++--- UI/Store.cpp | 2 +- UI/Store.h | 1 - UI/TouchControlVisibilityScreen.cpp | 37 +++++++++++++---------------- UI/TouchControlVisibilityScreen.h | 14 +++++------ UI/UploadScreen.cpp | 2 +- 11 files changed, 49 insertions(+), 50 deletions(-) diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index ded01b9e60..010b78e9af 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -121,15 +121,13 @@ std::string_view CustomButtonMappingScreen::GetTitle() const { return co->T("Custom touch button setup"); } -void CustomButtonMappingScreen::CreateViews() { +void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { using namespace UI; using namespace CustomKeyData; auto co = GetI18NCategory(I18NCat::CONTROLS); auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS); - root_ = new LinearLayout(ORIENT_VERTICAL); - root_->Add(new ItemHeader(GetTitle())); - LinearLayout *root__ = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0)); - root_->Add(root__); + LinearLayout *root__ = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0)); + parent->Add(root__); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(120, FILL_PARENT)); auto di = GetI18NCategory(I18NCat::DIALOG); @@ -143,13 +141,14 @@ void CustomButtonMappingScreen::CreateViews() { for (int i = 0; i < ARRAY_SIZE(g_customKeyList); i++) array[i] = (0x01 == ((g_Config.CustomButton[id_].key >> i) & 0x01)); + // TODO: Less hacky layout work + const Bounds layoutBounds = screenManager()->getUIContext()->GetLayoutBounds(); leftColumn->Add(new ButtonPreview(g_Config.iTouchButtonStyle == 0 ? customKeyShapes[cfg->shape].i : customKeyShapes[cfg->shape].l, - customKeyImages[cfg->image].i, customKeyImages[cfg->image].r, customKeyShapes[cfg->shape].f, customKeyShapes[cfg->shape].r, 62, 82)); + customKeyImages[cfg->image].i, customKeyImages[cfg->image].r, customKeyShapes[cfg->shape].f, customKeyShapes[cfg->shape].r, layoutBounds.x + 62, layoutBounds.y + 102)); root__->Add(leftColumn); + ScrollView *rightScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f)); - leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f))); - leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); root__->Add(rightScroll); LinearLayout *vertLayout = new LinearLayout(ORIENT_VERTICAL); diff --git a/UI/CustomButtonMappingScreen.h b/UI/CustomButtonMappingScreen.h index 2fa658b00e..f5af27f51f 100644 --- a/UI/CustomButtonMappingScreen.h +++ b/UI/CustomButtonMappingScreen.h @@ -19,23 +19,25 @@ #include "UI/BaseScreens.h" #include "UI/GamepadEmu.h" +#include "UI/SimpleDialogScreen.h" namespace UI { class CheckBox; } -class CustomButtonMappingScreen : public UIBaseDialogScreen { +class CustomButtonMappingScreen : public UISimpleBaseDialogScreen { public: - CustomButtonMappingScreen(DeviceOrientation deviceOrientation, const Path &gamePath, int id) : UIBaseDialogScreen(gamePath), deviceOrientation_(deviceOrientation), id_(id) {} + CustomButtonMappingScreen(DeviceOrientation deviceOrientation, const Path &gamePath, int id) : UISimpleBaseDialogScreen(gamePath, SimpleDialogFlags::Default), deviceOrientation_(deviceOrientation), id_(id) {} const char *tag() const override { return "CustomButton"; } - void CreateViews() override; + void CreateDialogViews(UI::ViewGroup *parent) override; void onFinish(DialogResult result) override; protected: void dialogFinished(const Screen *dialog, DialogResult result) override; - std::string_view GetTitle() const; + std::string_view GetTitle() const override; + private: void saveArray(); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 0ce81762ee..b01175b9ba 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -119,13 +119,12 @@ private: class CreditsScreen : public UISimpleBaseDialogScreen { public: - CreditsScreen() : UISimpleBaseDialogScreen() {} + CreditsScreen() : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::Default) {} void update() override; protected: std::string_view GetTitle() const override; void CreateDialogViews(UI::ViewGroup *parent) override; - bool CanScroll() const override { return false; } const char *tag() const override { return "Credits"; } }; diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 52c7b9f0d3..15f8f780d7 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -418,7 +418,7 @@ void ReportScreen::HandleBrowser(UI::EventParams &e) { } ReportFinishScreen::ReportFinishScreen(const Path &gamePath, ReportingOverallScore score) - : UISimpleBaseDialogScreen(), gamePath_(gamePath), score_(score) { + : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::ContentsCanScroll), gamePath_(gamePath), score_(score) { } std::string_view ReportFinishScreen::GetTitle() const { diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index 4eec0d1a22..e43d36ba39 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -7,7 +7,7 @@ void UISimpleBaseDialogScreen::CreateViews() { using namespace UI; - const bool canScroll = CanScroll(); + const bool canScroll = flags_ & SimpleDialogFlags::ContentsCanScroll; ignoreBottomInset_ = canScroll; const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; diff --git a/UI/SimpleDialogScreen.h b/UI/SimpleDialogScreen.h index 62fa6491e7..4a5464f55c 100644 --- a/UI/SimpleDialogScreen.h +++ b/UI/SimpleDialogScreen.h @@ -5,11 +5,17 @@ #include "UI/BaseScreens.h" +enum class SimpleDialogFlags { + Default = 0, + ContentsCanScroll = 8, +}; +ENUM_CLASS_BITOPS(SimpleDialogFlags); + // The simpler cousin of TabbedDialogScreen, without tabs or the other bling, // but with a consistent portrait-compatible back button and title. class UISimpleBaseDialogScreen : public UIBaseDialogScreen { public: - UISimpleBaseDialogScreen(const Path &gamePath = Path()) : UIBaseDialogScreen(gamePath) { + UISimpleBaseDialogScreen(const Path &gamePath, SimpleDialogFlags flags) : UIBaseDialogScreen(gamePath), flags_(flags) { // We need to check CanScroll before we know whether to ignore // bottom inset. Can't do that here, we do it in CreateViews } @@ -17,11 +23,10 @@ public: // Override this, don't override CreateViews. And don't touch root_ directly. virtual void CreateDialogViews(UI::ViewGroup *parent) = 0; virtual std::string_view GetTitle() const { return ""; } -protected: - virtual bool CanScroll() const { return true; } private: void CreateViews() override; + SimpleDialogFlags flags_; }; enum class TwoPaneFlags { diff --git a/UI/Store.cpp b/UI/Store.cpp index f34547672e..53b755f7cb 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -422,7 +422,7 @@ void ProductView::OnLaunchClick(UI::EventParams &e) { OnClickLaunch.Trigger(e2); } -StoreScreen::StoreScreen() { +StoreScreen::StoreScreen() : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::Default) { lang_ = g_Config.sLanguageIni; loading_ = true; diff --git a/UI/Store.h b/UI/Store.h index eabdb55492..ffb785191f 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -76,7 +76,6 @@ protected: void OnGameLaunch(UI::EventParams &e); std::string_view GetTitle() const override; - bool CanScroll() const override { return false; } // does its own scrolling private: void ParseListing(const std::string &json); ProductItemView *GetSelectedItem(); diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index cd28557e75..5362bcd8bc 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -46,15 +46,27 @@ private: UI::CheckBox *checkbox_; }; -void TouchControlVisibilityScreen::CreateTabs() { +std::string_view TouchControlVisibilityScreen::GetTitle() const { auto co = GetI18NCategory(I18NCat::CONTROLS); + return co->T("Touch Control Visibility"); +} - AddTab("Visibility", co->T("Visibility"), [this](UI::LinearLayout *contents) { - CreateVisibilityTab(contents); +void TouchControlVisibilityScreen::CreateSettingsViews(UI::ViewGroup *parent) { + using namespace UI; + + auto di = GetI18NCategory(I18NCat::DIALOG); + + Choice *toggleAll = parent->Add(new Choice(di->T("Toggle All"))); + toggleAll->OnClick.Add([this](UI::EventParams &e) { + // TODO: Is this a meaningful operation to support? + for (auto toggle : toggles_) { + *toggle.show = nextToggleAll_; + } + nextToggleAll_ = !nextToggleAll_; }); } -void TouchControlVisibilityScreen::CreateVisibilityTab(UI::LinearLayout *vert) { +void TouchControlVisibilityScreen::CreateContentViews(UI::ViewGroup *parent) { using namespace UI; using namespace CustomKeyData; @@ -63,25 +75,10 @@ void TouchControlVisibilityScreen::CreateVisibilityTab(UI::LinearLayout *vert) { const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; - Choice *toggleAll = new Choice(di->T("Toggle All"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 84)); - - vert->SetSpacing(0); - - vert->Add(toggleAll)->OnClick.Add([this](UI::EventParams &e) { - // TODO: Is this a meaningful operation to support? - for (auto toggle : toggles_) { - *toggle.show = nextToggleAll_; - } - nextToggleAll_ = !nextToggleAll_; - }); - - vert->Add(new ItemHeader(co->T("Touch Control Visibility"))); - const int cellSize = portrait ? std::min((g_display.dp_xres / 2 - 10), 290) : 380; - UI::GridLayoutSettings gridsettings(cellSize, 64, 5); gridsettings.fillCells = true; - GridLayout *grid = vert->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); + GridLayout *grid = parent->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation()); diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index b9718c5268..f88417c591 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -21,7 +21,7 @@ #include #include "Common/Render/TextureAtlas.h" #include "UI/BaseScreens.h" -#include "UI/TabbedDialogScreen.h" +#include "UI/SimpleDialogScreen.h" namespace UI { class CheckBox; @@ -34,18 +34,16 @@ struct TouchButtonToggle { std::function handle; }; -class TouchControlVisibilityScreen : public UITabbedBaseDialogScreen { +class TouchControlVisibilityScreen : public UITwoPaneBaseDialogScreen { public: - TouchControlVisibilityScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath) {} - void CreateTabs() override; + TouchControlVisibilityScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::SettingsInContextMenu | TwoPaneFlags::ContentsCanScroll) {} + void CreateContentViews(UI::ViewGroup *parent) override; + void CreateSettingsViews(UI::ViewGroup *parent) override; void onFinish(DialogResult result) override; const char *tag() const override { return "TouchControlVisibility"; } -protected: - bool ShowSearchControls() const override { return false; } - void CreateVisibilityTab(UI::LinearLayout *contents); - + std::string_view GetTitle() const override; private: std::vector toggles_; bool nextToggleAll_ = true; diff --git a/UI/UploadScreen.cpp b/UI/UploadScreen.cpp index 9004d5b021..9f8e29242e 100644 --- a/UI/UploadScreen.cpp +++ b/UI/UploadScreen.cpp @@ -9,7 +9,7 @@ #include "UI/UploadScreen.h" #include "UI/MiscViews.h" -UploadScreen::UploadScreen(const Path &targetFolder) : targetFolder_(targetFolder) { +UploadScreen::UploadScreen(const Path &targetFolder) : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::Default), targetFolder_(targetFolder) { std::vector ips; net::GetLocalIP4List(ips); localIPs_.clear(); From c92fa17741ac79b6d0d3521e1755999415380095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Nov 2025 19:33:15 +0100 Subject: [PATCH 4/5] Update more screen layouts --- UI/TouchControlVisibilityScreen.cpp | 42 +++++++++++------------------ UI/TouchControlVisibilityScreen.h | 7 ++--- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 5362bcd8bc..9e584f964d 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -149,7 +149,12 @@ void TouchControlVisibilityScreen::onFinish(DialogResult result) { g_Config.Save("TouchControlVisibilityScreen::onFinish"); } -void RightAnalogMappingScreen::CreateViews() { +std::string_view RightAnalogMappingScreen::GetTitle() const { + auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS); + return mc->T("Right Analog Stick"); +} + +void RightAnalogMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { using namespace UI; auto di = GetI18NCategory(I18NCat::DIALOG); @@ -158,34 +163,19 @@ void RightAnalogMappingScreen::CreateViews() { TouchControlConfig &touch = g_Config.GetTouchControlsConfig(GetDeviceOrientation()); - root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); - Choice *back = new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10)); - root_->Add(back)->OnClick.Handle(this, &UIScreen::OnBack); - TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, TabHolderFlags::Default, nullptr, nullptr, new AnchorLayoutParams(10, 0, 10, 0, Centering::None)); - root_->Add(tabHolder); - ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL); - tabHolder->AddTab(co->T("Binds"), ImageID::invalid(), rightPanel); - LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); - vert->SetSpacing(0); - static const char *rightAnalogButton[] = {"None", "L", "R", "Square", "Triangle", "Circle", "Cross", "D-pad up", "D-pad down", "D-pad left", "D-pad right", "Start", "Select", "RightAn.Up", "RightAn.Down", "RightAn.Left", "RightAn.Right", "An.Up", "An.Down", "An.Left", "An.Right"}; - vert->Add(new ItemHeader(co->T("Analog Style"))); - vert->Add(new CheckBox(&touch.touchRightAnalogStick.show, co->T("Visible"))); - vert->Add(new CheckBox(&g_Config.bRightAnalogCustom, co->T("Use custom right analog"))); - vert->Add(new CheckBox(&g_Config.bRightAnalogDisableDiagonal, co->T("Disable diagonal input")))->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new ItemHeader(co->T("Analog Style"))); + parent->Add(new CheckBox(&touch.touchRightAnalogStick.show, co->T("Visible"))); + parent->Add(new CheckBox(&g_Config.bRightAnalogCustom, co->T("Use custom right analog"))); + parent->Add(new CheckBox(&g_Config.bRightAnalogDisableDiagonal, co->T("Disable diagonal input")))->SetEnabledPtr(&g_Config.bRightAnalogCustom); - vert->Add(new ItemHeader(co->T("Analog Binding"))); - PopupMultiChoice *rightAnalogUp = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogUp, mc->T("RightAn.Up"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager())); - PopupMultiChoice *rightAnalogDown = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogDown, mc->T("RightAn.Down"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager())); - PopupMultiChoice *rightAnalogLeft = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogLeft, mc->T("RightAn.Left"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager())); - PopupMultiChoice *rightAnalogRight = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogRight, mc->T("RightAn.Right"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager())); - PopupMultiChoice *rightAnalogPress = vert->Add(new PopupMultiChoice(&g_Config.iRightAnalogPress, co->T("Keep this button pressed when right analog is pressed"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton) - 8, I18NCat::MAPPABLECONTROLS, screenManager())); - rightAnalogUp->SetEnabledPtr(&g_Config.bRightAnalogCustom); - rightAnalogDown->SetEnabledPtr(&g_Config.bRightAnalogCustom); - rightAnalogLeft->SetEnabledPtr(&g_Config.bRightAnalogCustom); - rightAnalogRight->SetEnabledPtr(&g_Config.bRightAnalogCustom); - rightAnalogPress->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new ItemHeader(co->T("Analog Binding"))); + parent->Add(new PopupMultiChoice(&g_Config.iRightAnalogUp, mc->T("RightAn.Up"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new PopupMultiChoice(&g_Config.iRightAnalogDown, mc->T("RightAn.Down"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new PopupMultiChoice(&g_Config.iRightAnalogLeft, mc->T("RightAn.Left"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new PopupMultiChoice(&g_Config.iRightAnalogRight, mc->T("RightAn.Right"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bRightAnalogCustom); + parent->Add(new PopupMultiChoice(&g_Config.iRightAnalogPress, co->T("Keep this button pressed when right analog is pressed"), rightAnalogButton, 0, ARRAY_SIZE(rightAnalogButton) - 8, I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bRightAnalogCustom); } void CheckBoxChoice::HandleClick(UI::EventParams &e) { diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index f88417c591..000572ec4a 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -49,10 +49,11 @@ private: bool nextToggleAll_ = true; }; -class RightAnalogMappingScreen : public UIBaseDialogScreen { +class RightAnalogMappingScreen : public UISimpleBaseDialogScreen { public: - RightAnalogMappingScreen(const Path &gamePath) : UIBaseDialogScreen(gamePath) {} - void CreateViews() override; + RightAnalogMappingScreen(const Path &gamePath) : UISimpleBaseDialogScreen(gamePath, SimpleDialogFlags::ContentsCanScroll) {} + void CreateDialogViews(UI::ViewGroup *parent) override; + std::string_view GetTitle() const override; const char *tag() const override { return "RightAnalogMapping"; } }; From bbef3106da38f4ad4e5aa504d2a493d1cf2ee679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Nov 2025 19:54:37 +0100 Subject: [PATCH 5/5] Finish up the PauseScreen --- UI/PauseScreen.cpp | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 4840dccbfe..5839e24bf7 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -419,6 +419,10 @@ void GamePauseScreen::CreateViews() { root_ = new LinearLayout(portrait ? ORIENT_VERTICAL : ORIENT_HORIZONTAL); + if (portrait) { + ((LinearLayout *)root_)->SetSpacing(0); + } + if (portrait) { // We have room for a title bar. Use the game DB title if available. std::string title; @@ -533,9 +537,12 @@ void GamePauseScreen::CreateViews() { ViewGroup *buttonColumn = nullptr; if (portrait) { buttonColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + + middleColumn = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT, Margins(10, 10, 10, 10))); + root_->Add(middleColumn); root_->Add(buttonColumn); } else { - middleColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(64, FILL_PARENT, Margins(0, 10, 0, 15))); + middleColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(ITEM_HEIGHT, FILL_PARENT, Margins(0, 10, 0, 15))); root_->Add(middleColumn); middleColumn->SetSpacing(0.0f); @@ -559,15 +566,11 @@ void GamePauseScreen::CreateViews() { Choice *continueChoice = rightColumnItems->Add(new Choice(pa->T("Continue"), ImageID("I_PLAY"))); root_->SetDefaultFocusView(continueChoice); continueChoice->OnClick.Handle(this, &UIScreen::OnBack); + rightColumnItems->Add(new Spacer(20.0)); } - rightColumnItems->Add(new Spacer(20.0)); - if (g_paramSFO.IsValid() && g_Config.HasGameConfig(g_paramSFO.GetDiscID())) { rightColumnItems->Add(new Choice(pa->T("Game Settings"), ImageID("I_GEAR")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings); - Choice *delGameConfig = rightColumnItems->Add(new Choice(pa->T("Delete Game Config"))); - delGameConfig->OnClick.Handle(this, &GamePauseScreen::OnDeleteConfig); - delGameConfig->SetEnabled(!bootPending_); } else if (PSP_CoreParameter().fileType != IdentifiedFileType::PPSSPP_GE_DUMP) { rightColumnItems->Add(new Choice(pa->T("Settings"), ImageID("I_GEAR")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings); Choice *createGameConfig = rightColumnItems->Add(new Choice(pa->T("Create Game Config"), ImageID("I_GEAR_STAR"))); @@ -628,7 +631,7 @@ void GamePauseScreen::CreateViews() { exit->SetEnabled(!bootPending_); if (middleColumn) { - middleColumn->SetSpacing(20.0f); + middleColumn->SetSpacing(portrait ? 8.0f : 20.0f); playButton_ = middleColumn->Add(new Button("", g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY"), new LinearLayoutParams(64, 64))); playButton_->OnClick.Add([=](UI::EventParams &e) { g_Config.bRunBehindPauseMenu = !g_Config.bRunBehindPauseMenu; @@ -643,11 +646,12 @@ void GamePauseScreen::CreateViews() { screenManager()->push(new GameScreen(gamePath_, true)); }); - Button *menuButton = middleColumn->Add(new Button("", ImageID("I_THREE_DOTS"), new LinearLayoutParams(64, 64))); - - menuButton->OnClick.Add([this, menuButton, portrait](UI::EventParams &e) { - ShowContextMenu(menuButton, portrait); - }); + if (!portrait) { + Button *menuButton = middleColumn->Add(new Button("", ImageID("I_THREE_DOTS"), new LinearLayoutParams(64, 64))); + menuButton->OnClick.Add([this, menuButton, portrait](UI::EventParams &e) { + ShowContextMenu(menuButton, portrait); + }); + } } else { playButton_ = nullptr; } @@ -671,6 +675,12 @@ void GamePauseScreen::ShowContextMenu(UI::View *menuButton, bool portrait) { } }); + auto pa = GetI18NCategory(I18NCat::PAUSE); + + Choice *delGameConfig = parent->Add(new Choice(pa->T("Delete Game Config"))); + delGameConfig->OnClick.Handle(this, &GamePauseScreen::OnDeleteConfig); + delGameConfig->SetEnabled(!bootPending_); + if (portrait) { // Add some other options that are removed from the main screen in portrait mode. if (Reporting::IsSupported() && g_paramSFO.GetValueString("DISC_ID").size()) {