From f63bea9ef281be2513b8653a4d2eabd77842a5c9 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Fri, 4 Feb 2022 17:20:26 -0300 Subject: [PATCH] Make popup menus focus items automatically when not using the mouse --- doc/classes/PopupMenu.xml | 7 +++++++ scene/gui/menu_button.cpp | 9 +++++++++ scene/gui/option_button.cpp | 9 +++++++++ scene/gui/popup_menu.cpp | 19 ++++++++++++++++--- scene/gui/popup_menu.h | 2 ++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index d2c5a2f2989d..5deb6f02f95d 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -311,6 +311,13 @@ [b]Note:[/b] The indices of items after the removed item will be shifted by one. + + + + + Sets the currently focused item as the given [code]index[/code]. + + diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index fb8b114187bf..2c9766cae010 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "menu_button.h" +#include "core/os/input.h" #include "core/os/keyboard.h" #include "scene/main/viewport.h" @@ -61,6 +62,14 @@ void MenuButton::pressed() { popup->set_size(Size2(size.width, 0)); popup->set_scale(get_global_transform().get_scale()); popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size())); + + // If not triggered by the mouse, start the popup with its first item selected. + if (popup->get_item_count() > 0 && + ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || + (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) { + popup->set_current_index(0); + } + popup->popup(); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 17881edbb489..b847f772a4ea 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "option_button.h" +#include "core/os/input.h" #include "core/print_string.h" Size2 OptionButton::get_minimum_size() const { @@ -109,6 +110,14 @@ void OptionButton::pressed() { popup->set_global_position(get_global_position() + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); popup->set_scale(get_global_transform().get_scale()); + + // If not triggered by the mouse, start the popup with its first item selected. + if (popup->get_item_count() > 0 && + ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || + (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) { + popup->set_current_index(0); + } + popup->popup(); } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 81d1ddfb144f..ea96c5627f26 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -146,7 +146,7 @@ void PopupMenu::_activate_submenu(int over) { Popup *pm = Object::cast_to(n); ERR_FAIL_COND_MSG(!pm, "Item subnode is not a Popup: " + items[over].submenu + "."); if (pm->is_visible_in_tree()) { - return; //already visible! + return; // Already visible. } Point2 p = get_global_position(); @@ -155,17 +155,21 @@ void PopupMenu::_activate_submenu(int over) { Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y) * get_global_transform().get_scale(); Size2 size = pm->get_size(); - // fix pos + // Fix pos. if (pos.x + size.width > get_viewport_rect().size.width) { pos.x = p.x - size.width; } pm->set_position(pos); pm->set_scale(get_global_transform().get_scale()); - pm->popup(); PopupMenu *pum = Object::cast_to(pm); if (pum) { + // If not triggered by the mouse, start the popup with its first item selected. + if (pum->get_item_count() > 0 && Input::get_singleton()->is_action_just_pressed("ui_accept")) { + pum->set_current_index(0); + } + pr.position -= pum->get_global_position(); pum->clear_autohide_areas(); pum->add_autohide_area(Rect2(pr.position.x, pr.position.y, pr.size.x, items[over]._ofs_cache)); @@ -174,6 +178,8 @@ void PopupMenu::_activate_submenu(int over) { pum->add_autohide_area(Rect2(pr.position.x, pr.position.y + from, pr.size.x, pr.size.y - from)); } } + + pm->popup(); } void PopupMenu::_submenu_timeout() { @@ -1033,6 +1039,12 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const { return items[p_idx].shortcut_is_disabled; } +void PopupMenu::set_current_index(int p_idx) { + ERR_FAIL_INDEX(p_idx, items.size()); + mouse_over = p_idx; + update(); +} + int PopupMenu::get_current_index() const { return mouse_over; } @@ -1403,6 +1415,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip); ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut); + ClassDB::bind_method(D_METHOD("set_current_index", "index"), &PopupMenu::set_current_index); ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index); ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index b07c0419d9f8..e2e367b93886 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -175,7 +175,9 @@ class PopupMenu : public Popup { Ref get_item_shortcut(int p_idx) const; int get_item_state(int p_idx) const; + void set_current_index(int p_idx); int get_current_index() const; + int get_item_count() const; bool activate_item_by_event(const Ref &p_event, bool p_for_global_only = false);