Ver código fonte

Merge pull request #28841 from KoBeWi/option_button_options

Improvements to incremental search
Rémi Verschelde 6 anos atrás
pai
commit
64a88e8ef3

+ 3 - 0
doc/classes/PopupMenu.xml

@@ -524,6 +524,9 @@
 		<member name="submenu_popup_delay" type="float" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay">
 			Sets the delay time for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. Default value: [code]0.3[/code] seconds.
 		</member>
+		<member name="allow_search" type="bool" setter="set_allow_search" getter="get_allow_search">
+			If [code]true[/code], allows to navigate [PopupMenu] with letter keys. Default value: [code]false[/code].
+		</member>
 	</members>
 	<signals>
 		<signal name="id_focused">

+ 15 - 3
scene/gui/item_list.cpp

@@ -749,9 +749,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
 					search_string = "";
 				}
 
-				search_string += String::chr(k->get_unicode());
-				for (int i = 0; i < items.size(); i++) {
-					if (items[i].text.begins_with(search_string)) {
+				if (String::chr(k->get_unicode()) != search_string)
+					search_string += String::chr(k->get_unicode());
+
+				for (int i = current + 1; i <= items.size(); i++) {
+					if (i == items.size()) {
+						if (current == 0)
+							break;
+						else
+							i = 0;
+					}
+
+					if (i == current)
+						break;
+
+					if (items[i].text.findn(search_string) == 0) {
 						set_current(i);
 						ensure_current_is_visible();
 						if (select_mode == SELECT_SINGLE) {

+ 1 - 0
scene/gui/option_button.cpp

@@ -354,6 +354,7 @@ OptionButton::OptionButton() {
 	add_child(popup);
 	popup->set_pass_on_modal_close_click(false);
 	popup->set_notify_transform(true);
+	popup->set_allow_search(true);
 	popup->connect("id_pressed", this, "_selected");
 	popup->connect("id_focused", this, "_focused");
 	popup->connect("popup_hide", this, "set_pressed", varray(false));

+ 56 - 0
scene/gui/popup_menu.cpp

@@ -31,6 +31,7 @@
 #include "popup_menu.h"
 #include "core/os/input.h"
 #include "core/os/keyboard.h"
+#include "core/os/os.h"
 #include "core/print_string.h"
 #include "core/translation.h"
 
@@ -380,6 +381,43 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
 			_scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
 		}
 	}
+
+	Ref<InputEventKey> k = p_event;
+
+	if (allow_search && k.is_valid() && k->get_unicode()) {
+
+		uint64_t now = OS::get_singleton()->get_ticks_msec();
+		uint64_t diff = now - search_time_msec;
+		uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+		search_time_msec = now;
+
+		if (diff > max_interval) {
+			search_string = "";
+		}
+
+		if (String::chr(k->get_unicode()) != search_string)
+			search_string += String::chr(k->get_unicode());
+
+		for (int i = mouse_over + 1; i <= items.size(); i++) {
+			if (i == items.size()) {
+				if (mouse_over <= 0)
+					break;
+				else
+					i = 0;
+			}
+
+			if (i == mouse_over)
+				break;
+
+			if (items[i].text.findn(search_string) == 0) {
+				mouse_over = i;
+				emit_signal("id_focused", i);
+				update();
+				accept_event();
+				break;
+			}
+		}
+	}
 }
 
 bool PopupMenu::has_point(const Point2 &p_point) const {
@@ -1289,6 +1327,16 @@ float PopupMenu::get_submenu_popup_delay() const {
 	return submenu_timer->get_wait_time();
 }
 
+void PopupMenu::set_allow_search(bool p_allow) {
+
+	allow_search = p_allow;
+}
+
+bool PopupMenu::get_allow_search() const {
+
+	return allow_search;
+}
+
 void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) {
 
 	hide_on_window_lose_focus = p_enabled;
@@ -1407,6 +1455,9 @@ void PopupMenu::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus);
 	ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus);
 
+	ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
+	ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
+
 	ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
 
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
@@ -1414,6 +1465,7 @@ void PopupMenu::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
 
 	ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
 	ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
@@ -1435,6 +1487,10 @@ PopupMenu::PopupMenu() {
 	initial_button_mask = 0;
 	during_grabbed_click = false;
 
+	allow_search = false;
+	search_time_msec = 0;
+	search_string = "";
+
 	set_focus_mode(FOCUS_ALL);
 	set_as_toplevel(true);
 	set_hide_on_item_selection(true);

+ 7 - 0
scene/gui/popup_menu.h

@@ -112,6 +112,10 @@ class PopupMenu : public Popup {
 	void _ref_shortcut(Ref<ShortCut> p_sc);
 	void _unref_shortcut(Ref<ShortCut> p_sc);
 
+	bool allow_search;
+	uint64_t search_time_msec;
+	String search_string;
+
 protected:
 	virtual bool has_point(const Point2 &p_point) const;
 
@@ -206,6 +210,9 @@ public:
 	void set_submenu_popup_delay(float p_time);
 	float get_submenu_popup_delay() const;
 
+	void set_allow_search(bool p_allow);
+	bool get_allow_search() const;
+
 	virtual void popup(const Rect2 &p_bounds = Rect2());
 
 	void set_hide_on_window_lose_focus(bool p_enabled);

+ 36 - 12
scene/gui/tree.cpp

@@ -389,7 +389,7 @@ TreeItem *TreeItem::get_children() {
 	return children;
 }
 
-TreeItem *TreeItem::get_prev_visible() {
+TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
 
 	TreeItem *current = this;
 
@@ -398,8 +398,20 @@ TreeItem *TreeItem::get_prev_visible() {
 	if (!prev) {
 
 		current = current->parent;
-		if (!current || (current == tree->root && tree->hide_root))
+		if (current == tree->root && tree->hide_root) {
 			return NULL;
+		} else if (!current) {
+			if (p_wrap) {
+				current = this;
+				TreeItem *temp = this->get_next_visible();
+				while (temp) {
+					current = temp;
+					temp = temp->get_next_visible();
+				}
+			} else {
+				return NULL;
+			}
+		}
 	} else {
 
 		current = prev;
@@ -415,7 +427,7 @@ TreeItem *TreeItem::get_prev_visible() {
 	return current;
 }
 
-TreeItem *TreeItem::get_next_visible() {
+TreeItem *TreeItem::get_next_visible(bool p_wrap) {
 
 	TreeItem *current = this;
 
@@ -433,10 +445,14 @@ TreeItem *TreeItem::get_next_visible() {
 			current = current->parent;
 		}
 
-		if (current == NULL)
-			return NULL;
-		else
+		if (!current) {
+			if (p_wrap)
+				return tree->root;
+			else
+				return NULL;
+		} else {
 			current = current->next;
+		}
 	}
 
 	return current;
@@ -740,8 +756,8 @@ void TreeItem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
 	ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
 
-	ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible);
-	ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible);
+	ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
 
 	ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
 
@@ -3491,6 +3507,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
 
 TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
 
+	TreeItem *from = p_at;
 	while (p_at) {
 
 		for (int i = 0; i < columns.size(); i++) {
@@ -3502,9 +3519,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
 		}
 
 		if (p_backwards)
-			p_at = p_at->get_prev_visible();
+			p_at = p_at->get_prev_visible(true);
 		else
-			p_at = p_at->get_next_visible();
+			p_at = p_at->get_next_visible(true);
+
+		if ((p_at) == from)
+			break;
 	}
 
 	return NULL;
@@ -3512,10 +3532,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
 
 TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
 
+	TreeItem *from = get_selected()->get_next_visible();
+
 	if (!root)
+		from = root;
+	if (!from)
 		return NULL;
 
-	return _search_item_text(root, p_find, r_col, p_selectable);
+	return _search_item_text(from, p_find, r_col, p_selectable);
 }
 
 void Tree::_do_incr_search(const String &p_add) {
@@ -3524,7 +3548,7 @@ void Tree::_do_incr_search(const String &p_add) {
 	uint64_t diff = time - last_keypress;
 	if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)))
 		incr_search = p_add;
-	else
+	else if (incr_search != p_add)
 		incr_search += p_add;
 
 	last_keypress = time;

+ 2 - 2
scene/gui/tree.h

@@ -238,8 +238,8 @@ public:
 	TreeItem *get_parent();
 	TreeItem *get_children();
 
-	TreeItem *get_prev_visible();
-	TreeItem *get_next_visible();
+	TreeItem *get_prev_visible(bool p_wrap = false);
+	TreeItem *get_next_visible(bool p_wrap = false);
 
 	void remove_child(TreeItem *p_item);