From 37f1533dadf61c2321e4889242705f99e6495625 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 15 Mar 2026 22:41:56 +0300 Subject: [PATCH] overlays/home: Integrate the new dropdown widget --- .../HomeMenu/overlay_home_menu_components.h | 157 ++++++++++++++++-- .../HomeMenu/overlay_home_menu_page.cpp | 24 +++ .../HomeMenu/overlay_home_menu_page.h | 16 ++ .../HomeMenu/overlay_home_menu_settings.h | 90 +++++----- 4 files changed, 228 insertions(+), 59 deletions(-) diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h index 11624a25da..161730940d 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h @@ -2,6 +2,7 @@ #include "Emu/RSX/Overlays/overlays.h" #include "Emu/RSX/Overlays/overlay_checkbox.h" +#include "Emu/RSX/Overlays/overlay_select.h" #include "Emu/RSX/Overlays/overlay_slider.h" #include "Emu/System.h" @@ -121,19 +122,147 @@ namespace rsx public: home_menu_dropdown(cfg::_enum* setting, const std::string& text) : home_menu_setting>(setting, text) - {} + { + for (size_t index = 0; index < setting->size(); index++) + { + auto translated = Emu.GetCallbacks().get_localized_setting(home_menu_setting>::m_setting, static_cast(index)); + m_options.emplace_back(std::move(translated)); + } + } + + void sync_selection() + { + auto setting = home_menu_setting>::m_setting; + if (!setting || !m_dropdown) + { + return; + } + + const auto current = fmt::format("%s", setting->get()); + const auto list = setting->to_list(); + for (s32 index = 0; index <= list.size(); ++index) + { + if (list[index] != current) + { + continue; + } + + if (index != m_dropdown->get_selected_index()) + { + m_dropdown->select_item(index); + } + break; + } + } void set_size(u16 w, u16 h = element_height) override { - home_menu_setting>::set_reserved_width(w / 2 + menu_entry_margin); + auto dropdown = std::make_unique(w / 2, element_height, m_options); + dropdown->auto_resize(); + + home_menu_setting>::set_reserved_width(dropdown->w + menu_entry_margin); home_menu_setting>::set_size(w, h); - auto dropdown = std::make_unique(); + // Center horizontally + dropdown->set_pos(0, this->compute_vertically_centered(dropdown.get())); m_dropdown = horizontal_layout::add_element(dropdown); - m_dropdown->set_size(w / 2, element_height); - m_dropdown->set_font("Arial", 14); - m_dropdown->align_text(home_menu_dropdown::text_align::center); - m_dropdown->back_color = { 0.3f, 0.3f, 0.3f, 1.0f }; + + // Select the correct item + sync_selection(); + } + + bool is_popup_visible() const + { + return m_popup_visible; + } + + s32 get_selected_index() const + { + return m_dropdown->get_selected_index(); + } + + void open_popup(const overlay_element* parent = nullptr) + { + m_previous_selection = m_dropdown->get_selected_index(); + m_popup_visible = true; + + if (!parent) + { + m_dropdown->get_popup()->set_pos(m_dropdown->x, m_dropdown->y + m_dropdown->h); + return; + } + + u16 dropdown_y = m_dropdown->y; + + // Check if the parent is a layout that can scroll + if (auto container = dynamic_cast(parent)) + { + // Apply the scroll (convert coordinate to view space from container space) + dropdown_y = std::max(dropdown_y, container->scroll_offset_value) - container->scroll_offset_value; + } + + const int space_above = static_cast(dropdown_y) - std::min(parent->y, dropdown_y); + const int space_below = std::max(parent->y + parent->h, dropdown_y) - dropdown_y; + + const int popup_height = m_dropdown->get_popup()->h; + u16 popup_x = m_dropdown->x, popup_y = parent->y; + if (space_below >= popup_height) + { + popup_y = dropdown_y + m_dropdown->h + 4; + } + else if (space_above >= popup_height) + { + popup_y = dropdown_y - popup_height - 4; + } + + m_dropdown->get_popup()->set_pos(popup_x, popup_y); + } + + void close_popup() + { + m_popup_visible = false; + } + + page_navigation handle_input(pad_button button) + { + switch (button) + { + case pad_button::circle: + m_dropdown->select_item(m_previous_selection); + close_popup(); + return page_navigation::exit; + + case pad_button::cross: + m_dropdown->select_item(m_dropdown->get_selected_index()); + close_popup(); + return page_navigation::exit; + + case pad_button::dpad_up: + case pad_button::ls_up: + m_dropdown->select_previous(); + break; + + case pad_button::dpad_down: + case pad_button::ls_down: + m_dropdown->select_next(); + break; + + default: + break; + } + + return page_navigation::stay; + } + + compiled_resource& render_popup() + { + if (this->m_popup_visible) + { + return m_dropdown->get_popup()->get_compiled(); + } + + m_popup_compiled_resources.clear(); + return m_popup_compiled_resources; } compiled_resource& get_compiled() override @@ -142,11 +271,8 @@ namespace rsx if (!this->is_compiled()) { - const std::string value_text = Emu.GetCallbacks().get_localized_setting(home_menu_setting>::m_setting, static_cast(this->m_last_value)); - m_dropdown->set_text(value_text); - m_dropdown->set_pos(m_dropdown->x, this->y + (this->h - m_dropdown->h) / 2); - - this->compiled_resources = horizontal_layout::get_compiled(); + sync_selection(); + horizontal_layout::get_compiled(); this->compiled_resources.add(m_dropdown->get_compiled()); } @@ -154,7 +280,12 @@ namespace rsx } private: - label* m_dropdown; + std::vector m_options; + select* m_dropdown = nullptr; + + int m_previous_selection = -1; + bool m_popup_visible = false; + compiled_resource m_popup_compiled_resources; }; template diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp index 567aae7267..857112eaca 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp @@ -49,11 +49,13 @@ namespace rsx void home_menu_page::on_activate() { hide_scroll_indicators(false); + hide_row_highliter(false); } void home_menu_page::on_deactivate() { hide_scroll_indicators(true); + hide_row_highliter(true); } void home_menu_page::set_current_page(home_menu_page* page) @@ -186,6 +188,16 @@ namespace rsx return page->handle_button_press(button_press, is_auto_repeat, auto_repeat_interval_ms); } + if (m_popup && m_popup.input_hook) + { + auto popup_action = m_popup.input_hook(button_press); + if (popup_action == page_navigation::exit) + { + m_popup.dismiss(); + } + return page_navigation::stay; + } + switch (button_press) { case pad_button::dpad_left: @@ -232,6 +244,7 @@ namespace rsx g_cfg.from_string(g_backup_cfg.to_string()); Emu.GetCallbacks().update_emu_settings(); *m_config_changed = false; + refresh(); } }); } @@ -249,6 +262,7 @@ namespace rsx if (m_config_changed) { *m_config_changed = false; + refresh(); } }); } @@ -330,6 +344,11 @@ namespace rsx compiled_resource& home_menu_page::get_compiled() { + if (m_popup) + { + m_is_compiled = false; + } + if (m_message_box && !m_message_box->is_compiled()) { m_is_compiled = false; @@ -364,6 +383,11 @@ namespace rsx } } + if (m_popup) + { + compiled_resources.add(m_popup.get_compiled()); + } + m_is_compiled = true; return compiled_resources; } diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h index ad3f8b2907..67ee03f66d 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h @@ -9,6 +9,20 @@ namespace rsx { namespace overlays { + struct home_menu_popup + { + std::function get_compiled; + std::function input_hook; + + void dismiss() + { + get_compiled = {}; + input_hook = {}; + } + + operator bool() const { return !!get_compiled; } + }; + struct home_menu_page : public list_view { public: @@ -35,6 +49,8 @@ namespace rsx std::shared_ptr m_message_box; std::shared_ptr m_config_changed; + home_menu_popup m_popup; + protected: virtual void add_page(home_menu::fa_icon icon, std::shared_ptr page); virtual void add_item(home_menu::fa_icon icon, std::string_view, std::function callback); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.h index e2331c3562..c5cb70f694 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.h @@ -79,66 +79,64 @@ namespace rsx std::unique_ptr elem = std::make_unique>(setting, localized_text); elem->set_size(this->w, menu_entry_height); - add_item(elem, [this, setting](pad_button btn) -> page_navigation + add_item(elem, [this, setting, elem = elem.get()](pad_button btn) -> page_navigation { - if (setting) + if (btn != pad_button::cross) { - bool set_default = false; - switch (btn) + return page_navigation::stay; + } + + if (!setting) + { + return page_navigation::stay; + } + + // If we're receiving this, we need to open the popup and install the input hook + auto dropdown = ensure(dynamic_cast*>(elem)); + dropdown->open_popup(static_cast(this)); + + auto render_fn = [dropdown]() -> compiled_resource& + { + return dropdown->render_popup(); + }; + + auto input_fn = [this, dropdown, setting](pad_button button) -> page_navigation + { + const auto result = dropdown->handle_input(button); + + if (!setting || result != page_navigation::exit) { - case pad_button::cross: - break; - case pad_button::select: - set_default = true; - break; - default: - return page_navigation::stay; + return result; } - T value = setting->get(); - - if (set_default) - { - if (value == setting->def) - { - return page_navigation::stay; - } - - value = setting->def; - } - - usz new_index = 0; - const std::string val = fmt::format("%s", value); + const auto previous = fmt::format("%s", setting->get()); const std::vector list = setting->to_list(); + const int selected_idx = dropdown->get_selected_index(); - for (usz i = 0; i < list.size(); i++) - { - const std::string& entry = list[i]; - if (entry == val) - { - if (set_default) - { - new_index = i; - break; - } - - new_index = (i + 1) % list.size(); - break; - } - } - - if (const std::string& next_value = ::at32(list, new_index); setting->from_string(next_value)) + if (const std::string& next_value = ::at32(list, selected_idx); setting->from_string(next_value)) { rsx_log.notice("User toggled dropdown in '%s'. Setting '%s' to %s", title, setting->get_name(), next_value); + + if (next_value != previous) + { + Emu.GetCallbacks().update_emu_settings(); + if (m_config_changed) + { + *m_config_changed = true; + } + refresh(); + } } else { rsx_log.error("Can't toggle dropdown in '%s'. Setting '%s' to '%s' failed", title, setting->get_name(), next_value); } - Emu.GetCallbacks().update_emu_settings(); - if (m_config_changed) *m_config_changed = true; - refresh(); - } + + return result; + }; + + m_popup.get_compiled = render_fn; + m_popup.input_hook = input_fn; return page_navigation::stay; });