From c27430af6bc38340ba0b374bc28b56cea31c9ea0 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sun, 27 Apr 2025 23:56:33 -0400 Subject: [PATCH] Make config tabset navigate down to first mod entry when mod menu is open, make mod configure screen focus on configure button after closing --- include/recomp_ui.h | 3 +++ src/ui/ui_config.cpp | 39 +++++++++++++++++++++++++++++++++++ src/ui/ui_config_sub_menu.cpp | 1 + src/ui/ui_mod_menu.cpp | 33 +++++++++++++++++++++++++++-- src/ui/ui_mod_menu.h | 2 ++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/include/recomp_ui.h b/include/recomp_ui.h index 605d10a..66edca1 100644 --- a/include/recomp_ui.h +++ b/include/recomp_ui.h @@ -71,6 +71,8 @@ namespace recompui { void set_config_tab(ConfigTab tab); Rml::ElementTabSet* get_config_tabset(); Rml::Element* get_mod_tab(); + void set_config_tabset_mod_nav(); + void focus_mod_configure_button(); enum class ButtonVariant { Primary, @@ -131,6 +133,7 @@ namespace recompui { Rml::ElementPtr create_custom_element(Rml::Element* parent, std::string tag); Rml::ElementDocument* load_document(const std::filesystem::path& path); Rml::ElementDocument* create_empty_document(); + Rml::Element* get_child_by_tag(Rml::Element* parent, const std::string& tag); void queue_image_from_bytes_rgba32(const std::string &src, const std::vector &bytes, uint32_t width, uint32_t height); void queue_image_from_bytes_file(const std::string &src, const std::vector &bytes); diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index f235413..70bc930 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -454,7 +454,45 @@ recompui::ContextId recompui::get_config_context_id() { return config_context; } +// Helper copied from RmlUi to get a named child. +Rml::Element* recompui::get_child_by_tag(Rml::Element* parent, const std::string& tag) +{ + // Look for the existing child + for (int i = 0; i < parent->GetNumChildren(); i++) + { + Rml::Element* child = parent->GetChild(i); + if (child->GetTagName() == tag) + return child; + } + + return nullptr; +} + +class ConfigTabsetListener : public Rml::EventListener { + void ProcessEvent(Rml::Event& event) override { + if (event.GetId() == Rml::EventId::Tabchange) { + int tab_index = event.GetParameter("tab_index", 0); + bool in_mod_tab = (tab_index == config_tab_to_index(recompui::ConfigTab::Mods)); + if (in_mod_tab) { + recompui::set_config_tabset_mod_nav(); + } + else { + Rml::ElementTabSet* tabset = recompui::get_config_tabset(); + Rml::Element* tabs = recompui::get_child_by_tag(tabset, "tabs"); + if (tabs != nullptr) { + size_t num_children = tabs->GetNumChildren(); + for (size_t i = 0; i < num_children; i++) { + tabs->GetChild(i)->SetProperty(Rml::PropertyId::NavDown, Rml::Style::Nav::Auto); + } + } + } + } + } +}; + class ConfigMenu : public recompui::MenuController { +private: + ConfigTabsetListener config_tabset_listener; public: ConfigMenu() { @@ -465,6 +503,7 @@ public: void load_document() override { config_context = recompui::create_context(zelda64::get_asset_path("config_menu.rml")); recompui::update_mod_list(false); + recompui::get_config_tabset()->AddEventListener(Rml::EventId::Tabchange, &config_tabset_listener); } void register_events(recompui::UiEventListenerInstancer& listener) override { recompui::register_event(listener, "apply_options", diff --git a/src/ui/ui_config_sub_menu.cpp b/src/ui/ui_config_sub_menu.cpp index cc9ab63..3e5c8f3 100644 --- a/src/ui/ui_config_sub_menu.cpp +++ b/src/ui/ui_config_sub_menu.cpp @@ -119,6 +119,7 @@ void ConfigSubMenu::back_button_pressed() { recompui::hide_context(sub_menu_context); recompui::show_context(config_context, ""); + recompui::focus_mod_configure_button(); } void ConfigSubMenu::option_hovered(ConfigOptionElement *option, bool active) { diff --git a/src/ui/ui_mod_menu.cpp b/src/ui/ui_mod_menu.cpp index 818932e..b0ca63b 100644 --- a/src/ui/ui_mod_menu.cpp +++ b/src/ui/ui_mod_menu.cpp @@ -584,13 +584,13 @@ void ModMenu::create_mod_list() { mod_entry_buttons.front()->set_nav_manual(NavDirection::Up, mod_tab_id); mod_entry_buttons.back()->set_nav(NavDirection::Down, install_mods_button); install_mods_button->set_nav(NavDirection::Up, mod_entry_buttons.back()); - recompui::get_mod_tab()->SetProperty(Rml::PropertyId::NavDown, Rml::Property{ "#" + mod_entry_buttons.front()->get_id(), Rml::Unit::STRING }); } else { install_mods_button->set_nav_manual(NavDirection::Up, mod_tab_id); - recompui::get_mod_tab()->SetProperty(Rml::PropertyId::NavDown, Rml::Style::Nav::Auto); } + recompui::set_config_tabset_mod_nav(); + // Add one extra spacer at the bottom. ModEntrySpacer *spacer = context.create_element(list_scroll_container); mod_entry_spacers.emplace_back(spacer); @@ -751,6 +751,35 @@ void process_game_started() { } } +void set_config_tabset_mod_nav() { + if (mod_menu) { + Rml::ElementTabSet* tabset = recompui::get_config_tabset(); + Rml::Element* tabs = recompui::get_child_by_tag(tabset, "tabs"); + if (tabs != nullptr) { + size_t num_children = tabs->GetNumChildren(); + Element* first_mod_entry = mod_menu->get_first_mod_entry(); + if (first_mod_entry != nullptr) { + std::string id = "#" + first_mod_entry->get_id(); + for (size_t i = 0; i < num_children; i++) { + tabs->GetChild(i)->SetProperty(Rml::PropertyId::NavDown, Rml::Property{ id, Rml::Unit::STRING }); + } + } + else { + for (size_t i = 0; i < num_children; i++) { + tabs->GetChild(i)->SetProperty(Rml::PropertyId::NavDown, Rml::Style::Nav::Auto); + } + } + } + } +} + +void focus_mod_configure_button() { + Element* configure_button = mod_menu->get_mod_configure_button(); + if (configure_button) { + configure_button->focus(); + } +} + ElementModMenu::ElementModMenu(const Rml::String &tag) : Rml::Element(tag) { SetProperty("width", "100%"); SetProperty("height", "100%"); diff --git a/src/ui/ui_mod_menu.h b/src/ui/ui_mod_menu.h index d6cee19..90d104c 100644 --- a/src/ui/ui_mod_menu.h +++ b/src/ui/ui_mod_menu.h @@ -74,6 +74,8 @@ public: ModMenu(Element *parent); virtual ~ModMenu(); void set_mods_dirty(bool scan_mods) { mods_dirty = true; mod_scan_queued = scan_mods; } + Element* get_first_mod_entry() { return !mod_entry_buttons.empty() ? mod_entry_buttons[0] : nullptr; } + Element* get_mod_configure_button() { return mod_details_panel != nullptr ? mod_details_panel->get_configure_button() : nullptr; } protected: std::string_view get_type_name() override { return "ModMenu"; } private: