From 8b52cf67afb72a99cbe081fa99cf27c1a359f8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 11 Nov 2025 12:30:22 +0100 Subject: [PATCH] Add a way to put the "ExtraButtons" in a popup menu, use on control mapping screen --- Common/UI/PopupScreens.cpp | 7 +++++++ Common/UI/PopupScreens.h | 17 ++++++++++++++++- Common/UI/TabHolder.cpp | 9 ++++++++- Common/UI/TabHolder.h | 2 +- Common/UI/View.cpp | 3 +++ Common/UI/View.h | 10 +++++++++- UI/ControlMappingScreen.cpp | 2 +- UI/ControlMappingScreen.h | 4 ++-- UI/GPUDriverTestScreen.cpp | 2 +- UI/MainScreen.cpp | 4 ++-- UI/RemoteISOScreen.cpp | 2 +- UI/SavedataScreen.cpp | 2 +- UI/SavedataScreen.h | 4 ++-- UI/TabbedDialogScreen.cpp | 16 +++++++++++++--- UI/TabbedDialogScreen.h | 4 +++- UI/TouchControlVisibilityScreen.cpp | 2 +- 16 files changed, 71 insertions(+), 19 deletions(-) diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 218066fca8..c7fbc8e2c0 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -267,6 +267,13 @@ void PopupContextMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { ap->top = sourceView_->GetBounds().y2(); } +void PopupCallbackScreen::CreatePopupContents(ViewGroup *parent) { + createViews_(parent); + for (int i = 0; i < parent->GetNumSubviews(); i++) { + parent->GetViewByIndex(i)->SetAutoResult(DialogResult::DR_OK); + } +} + std::string ChopTitle(const std::string &title) { size_t pos = title.find('\n'); if (pos != title.npos) { diff --git a/Common/UI/PopupScreens.h b/Common/UI/PopupScreens.h index 6042dea487..bfd3879f7a 100644 --- a/Common/UI/PopupScreens.h +++ b/Common/UI/PopupScreens.h @@ -241,7 +241,6 @@ struct ContextMenuItem { class PopupContextMenuScreen : public PopupScreen { public: PopupContextMenuScreen(const ContextMenuItem *items, size_t itemCount, I18NCat category, UI::View *sourceView); - void CreatePopupContents(ViewGroup *parent) override; const char *tag() const override { return "ContextMenuPopup"; } @@ -252,6 +251,7 @@ public: UI::Event OnChoice; private: + void CreatePopupContents(ViewGroup *parent) override; const ContextMenuItem *items_; size_t itemCount_; I18NCat category_; @@ -259,6 +259,21 @@ private: std::vector enabled_; }; +class PopupCallbackScreen : public PopupScreen { +public: + PopupCallbackScreen(std::function createViews, UI::View *sourceView) : PopupScreen(""), createViews_(createViews) { + if (sourceView) { + SetPopupOrigin(sourceView); + } + } + + const char *tag() const override { return "ContextMenuPopup"; } + +private: + void CreatePopupContents(ViewGroup *parent) override; + std::function createViews_; +}; + // Reads and writes value to determine the current selection. class PopupMultiChoice : public AbstractChoiceWithValueDisplay { public: diff --git a/Common/UI/TabHolder.cpp b/Common/UI/TabHolder.cpp index 05fabaf4e1..86a30437ab 100644 --- a/Common/UI/TabHolder.cpp +++ b/Common/UI/TabHolder.cpp @@ -8,7 +8,7 @@ namespace UI { -TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams) +TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, std::function contextMenuCallback, LayoutParams *layoutParams) : LinearLayout(Opposite(orientation), layoutParams), tabOrientation_(orientation), flags_(flags) { SetSpacing(0.0f); if (orientation == ORIENT_HORIZONTAL) { @@ -26,6 +26,13 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags fl tabScroll_ = new ScrollView(orientation, new LinearLayoutParams(1.0f)); tabScroll_->Add(tabStrip_); container->Add(tabScroll_); + if (contextMenuCallback) { + Choice *menuChoice = new Choice(ImageID("I_THREE_DOTS"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)); + menuChoice->OnClick.Add([contextMenuCallback](EventParams &e) { + contextMenuCallback(); + }); + container->Add(menuChoice); + } Add(container); } else { tabScroll_ = new ScrollView(orientation, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); diff --git a/Common/UI/TabHolder.h b/Common/UI/TabHolder.h index 4c49fa17e6..9c8d791860 100644 --- a/Common/UI/TabHolder.h +++ b/Common/UI/TabHolder.h @@ -21,7 +21,7 @@ ENUM_CLASS_BITOPS(TabHolderFlags); class TabHolder : public LinearLayout { public: - TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams); + TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, std::function contextMenuCallback, LayoutParams *layoutParams); template T *AddTab(std::string_view title, ImageID imageId, T *tabContents) { diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 8e5cb7870e..31a3771bef 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -221,6 +221,9 @@ void Clickable::DrawBG(UIContext &dc, const Style &style) { void Clickable::ClickInternal() { UI::EventParams e{}; e.v = this; + if (hasAutoResult_) { + e.bubbleResult = autoResult_; + } OnClick.Trigger(e); }; diff --git a/Common/UI/View.h b/Common/UI/View.h index 80ba75612d..05b7555ca1 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -463,6 +463,10 @@ public: } virtual void Recurse(void (*func)(View *view)) {} + virtual void SetAutoResult(DialogResult result) { + hasAutoResult_ = true; + autoResult_ = result; + } protected: // Inputs to layout @@ -482,6 +486,8 @@ protected: // Whether to use popup colors for styling. bool popupStyle_ = false; + bool hasAutoResult_ = false; + DialogResult autoResult_ = DR_OK; private: std::function enabledFunc_; @@ -893,6 +899,9 @@ public: virtual void Toggle(); virtual bool Toggled() const; + // we don't allow these for checkboxes. + virtual void SetAutoResult(DialogResult result) {} + protected: void ClickInternal() override; @@ -998,7 +1007,6 @@ private: bool big_ = false; }; - class TextView : public InertView { public: TextView(std::string_view text, LayoutParams *layoutParams = 0) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 572432fd49..92c8512cbe 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -232,7 +232,7 @@ static const BindingCategory cats[] = { {}, // sentinel }; -void ControlMappingScreen::CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) { +void ControlMappingScreen::CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) { using namespace UI; auto km = GetI18NCategory(I18NCat::KEYMAPPING); verticalLayout->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) { diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 43bb825085..3dc7711ae6 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -39,7 +39,7 @@ class SingleControlMapper; class ControlMappingScreen : public UITabbedBaseDialogScreen { public: - ControlMappingScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath) { + ControlMappingScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath, TabDialogFlags::ContextMenuInPortrait) { categoryToggles_[0] = true; categoryToggles_[1] = true; categoryToggles_[2] = true; @@ -49,7 +49,7 @@ public: protected: void CreateTabs() override; - void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) override; + void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override; void update() override; private: diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 29ebd09cf2..b000505964 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -301,7 +301,7 @@ void GPUDriverTestScreen::CreateViews() { AnchorLayout *anchor = new AnchorLayout(); root_ = anchor; - tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, TabHolderFlags::Default, nullptr, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, Centering::None)); + tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, TabHolderFlags::Default, nullptr, nullptr, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, Centering::None)); anchor->Add(tabHolder_); tabHolder_->AddTab("Discard", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL)); tabHolder_->AddTab("Shader", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL)); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 963515565c..4d0ad36001 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1224,7 +1224,7 @@ void MainScreen::CreateViews() { auto mm = GetI18NCategory(I18NCat::MAINMENU); - tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); + tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); ViewGroup *leftColumn = tabHolder_; tabHolder_->SetTag("MainScreenGames"); gameBrowsers_.clear(); @@ -1610,7 +1610,7 @@ void UmdReplaceScreen::CreateViews() { const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; - TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); + TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); leftColumn->SetTag("UmdReplace"); leftColumn->SetClip(true); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 777bd506f0..c12c1a7fd6 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -596,7 +596,7 @@ void RemoteISOBrowseScreen::CreateViews() { using namespace UI; - TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); tabHolder_ = leftColumn; tabHolder_->SetTag("RemoteGames"); gameBrowsers_.clear(); diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 8c3d1ba4ed..e31bcd43bb 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -671,7 +671,7 @@ void SavedataScreen::CreateTabs() { }); } -void SavedataScreen::CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) { +void SavedataScreen::CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) { using namespace UI; if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) { auto di = GetI18NCategory(I18NCat::DIALOG); diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index 68fcfd8023..83449ab421 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -80,10 +80,10 @@ public: protected: void CreateTabs() override; - void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) override; + void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override; bool ShowSearchControls() const override { return false; } - + private: void OnSavedataButtonClick(UI::EventParams &e); void OnSearch(UI::EventParams &e); diff --git a/UI/TabbedDialogScreen.cpp b/UI/TabbedDialogScreen.cpp index 82421b268d..18b378fc03 100644 --- a/UI/TabbedDialogScreen.cpp +++ b/UI/TabbedDialogScreen.cpp @@ -58,16 +58,26 @@ void UITabbedBaseDialogScreen::CreateViews() { if (flags_ & TabDialogFlags::HorizontalOnlyIcons) { tabHolderFlags |= TabHolderFlags::HorizontalOnlyIcons; } - tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 200, tabHolderFlags, filterNotice_, new LinearLayoutParams(1.0f)); + std::function contextMenu; + if (flags_ & TabDialogFlags::ContextMenuInPortrait) { + contextMenu = [this]() { + this->screenManager()->push(new PopupCallbackScreen([this](UI::ViewGroup *parent) { + CreateExtraButtons(parent, 0); + }, nullptr)); + }; + } + tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 200, tabHolderFlags, filterNotice_, contextMenu, new LinearLayoutParams(1.0f)); verticalLayout->Add(tabHolder_); - CreateExtraButtons(verticalLayout, 0); + if (!(flags_ & TabDialogFlags::ContextMenuInPortrait)) { + CreateExtraButtons(verticalLayout, 0); + } root_->Add(verticalLayout); } else { TabHolderFlags tabHolderFlags = TabHolderFlags::Default; if (flags_ & TabDialogFlags::VerticalShowIcons) { tabHolderFlags |= TabHolderFlags::VerticalShowIcons; } - tabHolder_ = new TabHolder(ORIENT_VERTICAL, 300, tabHolderFlags, filterNotice_, new AnchorLayoutParams(10, 0, 10, 0)); + tabHolder_ = new TabHolder(ORIENT_VERTICAL, 300, tabHolderFlags, filterNotice_, nullptr, new AnchorLayoutParams(10, 0, 10, 0)); CreateExtraButtons(tabHolder_->Container(), 10); tabHolder_->AddBack(this); root_->Add(tabHolder_); diff --git a/UI/TabbedDialogScreen.h b/UI/TabbedDialogScreen.h index b3089e937d..a61deac397 100644 --- a/UI/TabbedDialogScreen.h +++ b/UI/TabbedDialogScreen.h @@ -20,6 +20,7 @@ enum class TabDialogFlags { HorizontalOnlyIcons = 1, VerticalShowIcons = 2, AddTitles = 4, + ContextMenuInPortrait = 8, }; ENUM_CLASS_BITOPS(TabDialogFlags); @@ -45,9 +46,10 @@ protected: // Load data and define your tabs here. virtual void PreCreateViews() {} virtual void CreateTabs() = 0; - virtual void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) {} + virtual void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) {} virtual bool ShowSearchControls() const { return true; } virtual void EnsureTabs(); + virtual bool UsePopupMenuInPortrait() const { return false; } virtual bool ForceHorizontalTabs() const { return false; } int GetCurrentTab() const; diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index a0659c0dd8..29736b756d 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -155,7 +155,7 @@ void RightAnalogMappingScreen::CreateViews() { 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, new AnchorLayoutParams(10, 0, 10, 0, Centering::None)); + 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);