Browse Source

Improve Input Map and Shortcuts editor

- Fixes not being able to search by pressing ESC.
- Removes double translations.
- Cleans up `InputEventConfigurationDialog` for auto-translation.
- Adapts to editor theme change.
- Unifies search bar boilerplate code.
- Shortcuts: disable the "erase" button if there is no event to remove.
- Input Map: always include built-in actions when searching.
Haoyu Qiu 2 months ago
parent
commit
146599d3b2

+ 37 - 77
editor/action_map_editor.cpp

@@ -33,6 +33,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_string_names.h"
 #include "editor/event_listener_line_edit.h"
 #include "editor/event_listener_line_edit.h"
+#include "editor/gui/editor_event_search_bar.h"
 #include "editor/input_event_configuration_dialog.h"
 #include "editor/input_event_configuration_dialog.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/check_button.h"
 #include "scene/gui/check_button.h"
@@ -223,9 +224,8 @@ void ActionMapEditor::_tree_item_activated() {
 	_tree_button_pressed(item, 2, BUTTON_EDIT_EVENT, MouseButton::LEFT);
 	_tree_button_pressed(item, 2, BUTTON_EDIT_EVENT, MouseButton::LEFT);
 }
 }
 
 
-void ActionMapEditor::set_show_builtin_actions(bool p_show) {
+void ActionMapEditor::_set_show_builtin_actions(bool p_show) {
 	show_builtin_actions = p_show;
 	show_builtin_actions = p_show;
-	show_builtin_actions_checkbutton->set_pressed(p_show);
 	EditorSettings::get_singleton()->set_project_metadata("project_settings", "show_builtin_actions", show_builtin_actions);
 	EditorSettings::get_singleton()->set_project_metadata("project_settings", "show_builtin_actions", show_builtin_actions);
 
 
 	// Prevent unnecessary updates of action list when cache is empty.
 	// Prevent unnecessary updates of action list when cache is empty.
@@ -234,14 +234,17 @@ void ActionMapEditor::set_show_builtin_actions(bool p_show) {
 	}
 	}
 }
 }
 
 
-void ActionMapEditor::_search_term_updated(const String &) {
-	update_action_list();
-}
-
-void ActionMapEditor::_search_by_event(const Ref<InputEvent> &p_event) {
-	if (p_event.is_null() || (p_event->is_pressed() && !p_event->is_echo())) {
-		update_action_list();
+void ActionMapEditor::_on_search_bar_value_changed() {
+	if (action_list_search_bar->is_searching()) {
+		show_builtin_actions_checkbutton->set_pressed_no_signal(true);
+		show_builtin_actions_checkbutton->set_disabled(true);
+		show_builtin_actions_checkbutton->set_tooltip_text(TTRC("Built-in actions are always shown when searching."));
+	} else {
+		show_builtin_actions_checkbutton->set_pressed_no_signal(show_builtin_actions);
+		show_builtin_actions_checkbutton->set_disabled(false);
+		show_builtin_actions_checkbutton->set_tooltip_text(String());
 	}
 	}
+	update_action_list();
 }
 }
 
 
 Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
 Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
@@ -371,7 +374,6 @@ void ActionMapEditor::_notification(int p_what) {
 		} break;
 		} break;
 
 
 		case NOTIFICATION_THEME_CHANGED: {
 		case NOTIFICATION_THEME_CHANGED: {
-			action_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
 			add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
 			add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
 			if (!actions_cache.is_empty()) {
 			if (!actions_cache.is_empty()) {
 				update_action_list();
 				update_action_list();
@@ -386,12 +388,10 @@ void ActionMapEditor::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::STRING, "name")));
 	ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::STRING, "name")));
 	ADD_SIGNAL(MethodInfo("action_renamed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
 	ADD_SIGNAL(MethodInfo("action_renamed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
 	ADD_SIGNAL(MethodInfo("action_reordered", PropertyInfo(Variant::STRING, "action_name"), PropertyInfo(Variant::STRING, "relative_to"), PropertyInfo(Variant::BOOL, "before")));
 	ADD_SIGNAL(MethodInfo("action_reordered", PropertyInfo(Variant::STRING, "action_name"), PropertyInfo(Variant::STRING, "relative_to"), PropertyInfo(Variant::BOOL, "before")));
-	ADD_SIGNAL(MethodInfo(SNAME("filter_focused")));
-	ADD_SIGNAL(MethodInfo(SNAME("filter_unfocused")));
 }
 }
 
 
 LineEdit *ActionMapEditor::get_search_box() const {
 LineEdit *ActionMapEditor::get_search_box() const {
-	return action_list_search;
+	return action_list_search_bar->get_name_search_box();
 }
 }
 
 
 LineEdit *ActionMapEditor::get_path_box() const {
 LineEdit *ActionMapEditor::get_path_box() const {
@@ -403,7 +403,7 @@ InputEventConfigurationDialog *ActionMapEditor::get_configuration_dialog() {
 }
 }
 
 
 bool ActionMapEditor::_should_display_action(const String &p_name, const Array &p_events) const {
 bool ActionMapEditor::_should_display_action(const String &p_name, const Array &p_events) const {
-	const Ref<InputEvent> search_ev = action_list_search_by_event->get_event();
+	const Ref<InputEvent> search_ev = action_list_search_bar->get_event();
 	bool event_match = true;
 	bool event_match = true;
 	if (search_ev.is_valid()) {
 	if (search_ev.is_valid()) {
 		event_match = false;
 		event_match = false;
@@ -415,7 +415,7 @@ bool ActionMapEditor::_should_display_action(const String &p_name, const Array &
 		}
 		}
 	}
 	}
 
 
-	return event_match && action_list_search->get_text().is_subsequence_ofn(p_name);
+	return event_match && action_list_search_bar->get_name().is_subsequence_ofn(p_name);
 }
 }
 
 
 void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_infos) {
 void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_infos) {
@@ -426,15 +426,13 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 	action_tree->clear();
 	action_tree->clear();
 	TreeItem *root = action_tree->create_item();
 	TreeItem *root = action_tree->create_item();
 
 
-	for (int i = 0; i < actions_cache.size(); i++) {
-		ActionInfo action_info = actions_cache[i];
-
+	for (const ActionInfo &action_info : actions_cache) {
 		const Array events = action_info.action["events"];
 		const Array events = action_info.action["events"];
 		if (!_should_display_action(action_info.name, events)) {
 		if (!_should_display_action(action_info.name, events)) {
 			continue;
 			continue;
 		}
 		}
 
 
-		if (!action_info.editable && !show_builtin_actions) {
+		if (!action_info.editable && !action_list_search_bar->is_searching() && !show_builtin_actions) {
 			continue;
 			continue;
 		}
 		}
 
 
@@ -448,6 +446,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 		action_item->set_meta("__name", action_info.name);
 		action_item->set_meta("__name", action_info.name);
 
 
 		// First Column - Action Name
 		// First Column - Action Name
+		action_item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
 		action_item->set_text(0, action_info.name);
 		action_item->set_text(0, action_info.name);
 		action_item->set_editable(0, action_info.editable);
 		action_item->set_editable(0, action_info.editable);
 		action_item->set_icon(0, action_info.icon);
 		action_item->set_icon(0, action_info.icon);
@@ -464,13 +463,13 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 			bool events_eq = Shortcut::is_event_array_equal(action_info.action_initial["events"], action_info.action["events"]);
 			bool events_eq = Shortcut::is_event_array_equal(action_info.action_initial["events"], action_info.action["events"]);
 			bool action_eq = deadzone_eq && events_eq;
 			bool action_eq = deadzone_eq && events_eq;
 			action_item->set_meta("__action_initial", action_info.action_initial);
 			action_item->set_meta("__action_initial", action_info.action_initial);
-			action_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("ReloadSmall")), BUTTON_REVERT_ACTION, action_eq, action_eq ? TTRC("Cannot Revert - Action is same as initial") : TTRC("Revert Action"));
+			action_item->add_button(2, get_editor_theme_icon(SNAME("ReloadSmall")), BUTTON_REVERT_ACTION, action_eq, action_eq ? TTRC("Cannot Revert - Action is same as initial") : TTRC("Revert Action"));
 		}
 		}
-		action_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Add")), BUTTON_ADD_EVENT, false, TTRC("Add Event"));
-		action_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_ACTION, !action_info.editable, action_info.editable ? TTRC("Remove Action") : TTRC("Cannot Remove Action"));
+		action_item->add_button(2, get_editor_theme_icon(SNAME("Add")), BUTTON_ADD_EVENT, false, TTRC("Add Event"));
+		action_item->add_button(2, get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_ACTION, !action_info.editable, action_info.editable ? TTRC("Remove Action") : TTRC("Cannot Remove Action"));
 
 
-		action_item->set_custom_bg_color(0, action_tree->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
-		action_item->set_custom_bg_color(1, action_tree->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+		action_item->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+		action_item->set_custom_bg_color(1, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
 
 
 		for (int evnt_idx = 0; evnt_idx < events.size(); evnt_idx++) {
 		for (int evnt_idx = 0; evnt_idx < events.size(); evnt_idx++) {
 			Ref<InputEvent> event = events[evnt_idx];
 			Ref<InputEvent> event = events[evnt_idx];
@@ -481,6 +480,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 			TreeItem *event_item = action_tree->create_item(action_item);
 			TreeItem *event_item = action_tree->create_item(action_item);
 
 
 			// First Column - Text
 			// First Column - Text
+			event_item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
 			event_item->set_text(0, EventListenerLineEdit::get_event_text(event, true));
 			event_item->set_text(0, EventListenerLineEdit::get_event_text(event, true));
 			event_item->set_meta("__event", event);
 			event_item->set_meta("__event", event);
 			event_item->set_meta("__index", evnt_idx);
 			event_item->set_meta("__index", evnt_idx);
@@ -489,41 +489,38 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 			Ref<InputEventKey> k = event;
 			Ref<InputEventKey> k = event;
 			if (k.is_valid()) {
 			if (k.is_valid()) {
 				if (k->get_physical_keycode() == Key::NONE && k->get_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
 				if (k->get_physical_keycode() == Key::NONE && k->get_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
-					event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("KeyboardLabel")));
+					event_item->set_icon(0, get_editor_theme_icon(SNAME("KeyboardLabel")));
 				} else if (k->get_keycode() != Key::NONE) {
 				} else if (k->get_keycode() != Key::NONE) {
-					event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("Keyboard")));
+					event_item->set_icon(0, get_editor_theme_icon(SNAME("Keyboard")));
 				} else if (k->get_physical_keycode() != Key::NONE) {
 				} else if (k->get_physical_keycode() != Key::NONE) {
-					event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("KeyboardPhysical")));
+					event_item->set_icon(0, get_editor_theme_icon(SNAME("KeyboardPhysical")));
 				} else {
 				} else {
-					event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("KeyboardError")));
+					event_item->set_icon(0, get_editor_theme_icon(SNAME("KeyboardError")));
 				}
 				}
 			}
 			}
 
 
 			Ref<InputEventMouseButton> mb = event;
 			Ref<InputEventMouseButton> mb = event;
 			if (mb.is_valid()) {
 			if (mb.is_valid()) {
-				event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("Mouse")));
+				event_item->set_icon(0, get_editor_theme_icon(SNAME("Mouse")));
 			}
 			}
 
 
 			Ref<InputEventJoypadButton> jb = event;
 			Ref<InputEventJoypadButton> jb = event;
 			if (jb.is_valid()) {
 			if (jb.is_valid()) {
-				event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("JoyButton")));
+				event_item->set_icon(0, get_editor_theme_icon(SNAME("JoyButton")));
 			}
 			}
 
 
 			Ref<InputEventJoypadMotion> jm = event;
 			Ref<InputEventJoypadMotion> jm = event;
 			if (jm.is_valid()) {
 			if (jm.is_valid()) {
-				event_item->set_icon(0, action_tree->get_editor_theme_icon(SNAME("JoyAxis")));
+				event_item->set_icon(0, get_editor_theme_icon(SNAME("JoyAxis")));
 			}
 			}
 
 
 			// Third Column - Buttons
 			// Third Column - Buttons
-			event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Edit")), BUTTON_EDIT_EVENT, false, TTRC("Edit Event"), TTRC("Edit Event"));
-			event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_EVENT, false, TTRC("Remove Event"), TTRC("Remove Event"));
+			event_item->add_button(2, get_editor_theme_icon(SNAME("Edit")), BUTTON_EDIT_EVENT, false, TTRC("Edit Event"), TTRC("Edit Event"));
+			event_item->add_button(2, get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_EVENT, false, TTRC("Remove Event"), TTRC("Remove Event"));
 			event_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
 			event_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
 			event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75));
 			event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75));
 		}
 		}
 	}
 	}
-
-	// Update UI.
-	clear_all_search->set_disabled(action_list_search->get_text().is_empty() && action_list_search_by_event->get_event().is_null());
 }
 }
 
 
 void ActionMapEditor::show_message(const String &p_message) {
 void ActionMapEditor::show_message(const String &p_message) {
@@ -531,52 +528,15 @@ void ActionMapEditor::show_message(const String &p_message) {
 	message->popup_centered();
 	message->popup_centered();
 }
 }
 
 
-void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {
-	memdelete(action_list_search);
-	action_list_search = p_searchbox;
-	action_list_search->connect(SceneStringName(text_changed), callable_mp(this, &ActionMapEditor::_search_term_updated));
-}
-
-void ActionMapEditor::_on_filter_focused() {
-	emit_signal(SNAME("filter_focused"));
-}
-
-void ActionMapEditor::_on_filter_unfocused() {
-	emit_signal(SNAME("filter_unfocused"));
-}
-
 ActionMapEditor::ActionMapEditor() {
 ActionMapEditor::ActionMapEditor() {
 	// Main Vbox Container
 	// Main Vbox Container
 	VBoxContainer *main_vbox = memnew(VBoxContainer);
 	VBoxContainer *main_vbox = memnew(VBoxContainer);
 	main_vbox->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
 	main_vbox->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
 	add_child(main_vbox);
 	add_child(main_vbox);
 
 
-	HBoxContainer *top_hbox = memnew(HBoxContainer);
-	main_vbox->add_child(top_hbox);
-
-	action_list_search = memnew(LineEdit);
-	action_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	action_list_search->set_placeholder(TTRC("Filter by Name"));
-	action_list_search->set_accessibility_name(TTRC("Filter by Name"));
-	action_list_search->set_clear_button_enabled(true);
-	action_list_search->connect(SceneStringName(text_changed), callable_mp(this, &ActionMapEditor::_search_term_updated));
-	top_hbox->add_child(action_list_search);
-
-	action_list_search_by_event = memnew(EventListenerLineEdit);
-	action_list_search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	action_list_search_by_event->set_stretch_ratio(0.75);
-	action_list_search_by_event->set_accessibility_name(TTRC("Action Event"));
-	action_list_search_by_event->connect("event_changed", callable_mp(this, &ActionMapEditor::_search_by_event));
-	action_list_search_by_event->connect(SceneStringName(focus_entered), callable_mp(this, &ActionMapEditor::_on_filter_focused));
-	action_list_search_by_event->connect(SceneStringName(focus_exited), callable_mp(this, &ActionMapEditor::_on_filter_unfocused));
-	top_hbox->add_child(action_list_search_by_event);
-
-	clear_all_search = memnew(Button);
-	clear_all_search->set_text(TTRC("Clear All"));
-	clear_all_search->set_tooltip_text(TTRC("Clear all search filters."));
-	clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search_by_event, &EventListenerLineEdit::clear_event));
-	clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search, &LineEdit::clear));
-	top_hbox->add_child(clear_all_search);
+	action_list_search_bar = memnew(EditorEventSearchBar);
+	action_list_search_bar->connect(SceneStringName(value_changed), callable_mp(this, &ActionMapEditor::_on_search_bar_value_changed));
+	main_vbox->add_child(action_list_search_bar);
 
 
 	// Adding Action line edit + button
 	// Adding Action line edit + button
 	add_hbox = memnew(HBoxContainer);
 	add_hbox = memnew(HBoxContainer);
@@ -603,7 +563,7 @@ ActionMapEditor::ActionMapEditor() {
 
 
 	show_builtin_actions_checkbutton = memnew(CheckButton);
 	show_builtin_actions_checkbutton = memnew(CheckButton);
 	show_builtin_actions_checkbutton->set_text(TTRC("Show Built-in Actions"));
 	show_builtin_actions_checkbutton->set_text(TTRC("Show Built-in Actions"));
-	show_builtin_actions_checkbutton->connect(SceneStringName(toggled), callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
+	show_builtin_actions_checkbutton->connect(SceneStringName(toggled), callable_mp(this, &ActionMapEditor::_set_show_builtin_actions));
 	add_hbox->add_child(show_builtin_actions_checkbutton);
 	add_hbox->add_child(show_builtin_actions_checkbutton);
 
 
 	show_builtin_actions = EditorSettings::get_singleton()->get_project_metadata("project_settings", "show_builtin_actions", false);
 	show_builtin_actions = EditorSettings::get_singleton()->get_project_metadata("project_settings", "show_builtin_actions", false);

+ 8 - 15
editor/action_map_editor.h

@@ -32,13 +32,14 @@
 
 
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"
 
 
+class AcceptDialog;
 class Button;
 class Button;
-class HBoxContainer;
-class EventListenerLineEdit;
-class LineEdit;
 class CheckButton;
 class CheckButton;
-class AcceptDialog;
+class EditorEventSearchBar;
+class EventListenerLineEdit;
+class HBoxContainer;
 class InputEventConfigurationDialog;
 class InputEventConfigurationDialog;
+class LineEdit;
 class Tree;
 class Tree;
 
 
 class ActionMapEditor : public Control {
 class ActionMapEditor : public Control {
@@ -82,9 +83,7 @@ private:
 
 
 	bool show_builtin_actions = false;
 	bool show_builtin_actions = false;
 	CheckButton *show_builtin_actions_checkbutton = nullptr;
 	CheckButton *show_builtin_actions_checkbutton = nullptr;
-	LineEdit *action_list_search = nullptr;
-	EventListenerLineEdit *action_list_search_by_event = nullptr;
-	Button *clear_all_search = nullptr;
+	EditorEventSearchBar *action_list_search_bar = nullptr;
 
 
 	HBoxContainer *add_hbox = nullptr;
 	HBoxContainer *add_hbox = nullptr;
 	LineEdit *add_edit = nullptr;
 	LineEdit *add_edit = nullptr;
@@ -101,16 +100,14 @@ private:
 
 
 	void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
 	void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
 	void _tree_item_activated();
 	void _tree_item_activated();
-	void _search_term_updated(const String &p_search_term);
-	void _search_by_event(const Ref<InputEvent> &p_event);
+	void _on_search_bar_value_changed();
 	bool _should_display_action(const String &p_name, const Array &p_events) const;
 	bool _should_display_action(const String &p_name, const Array &p_events) const;
 
 
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 
 
-	void _on_filter_focused();
-	void _on_filter_unfocused();
+	void _set_show_builtin_actions(bool p_show);
 
 
 protected:
 protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
@@ -125,9 +122,5 @@ public:
 	void update_action_list(const Vector<ActionInfo> &p_action_infos = Vector<ActionInfo>());
 	void update_action_list(const Vector<ActionInfo> &p_action_infos = Vector<ActionInfo>());
 	void show_message(const String &p_message);
 	void show_message(const String &p_message);
 
 
-	void set_show_builtin_actions(bool p_show);
-
-	void use_external_search_box(LineEdit *p_searchbox);
-
 	ActionMapEditor();
 	ActionMapEditor();
 };
 };

+ 28 - 62
editor/editor_settings_dialog.cpp

@@ -43,9 +43,9 @@
 #include "editor/editor_string_names.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/event_listener_line_edit.h"
 #include "editor/event_listener_line_edit.h"
+#include "editor/gui/editor_event_search_bar.h"
 #include "editor/input_event_configuration_dialog.h"
 #include "editor/input_event_configuration_dialog.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
-#include "editor/project_settings_editor.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_theme_manager.h"
 #include "editor/themes/editor_theme_manager.h"
 #include "scene/gui/check_button.h"
 #include "scene/gui/check_button.h"
@@ -217,16 +217,6 @@ void EditorSettingsDialog::popup_edit_settings() {
 	_focus_current_search_box();
 	_focus_current_search_box();
 }
 }
 
 
-void EditorSettingsDialog::_filter_shortcuts(const String &) {
-	_update_shortcuts();
-}
-
-void EditorSettingsDialog::_filter_shortcuts_by_event(const Ref<InputEvent> &p_event) {
-	if (p_event.is_null() || (p_event->is_pressed() && !p_event->is_echo())) {
-		_update_shortcuts();
-	}
-}
-
 void EditorSettingsDialog::_undo_redo_callback(void *p_self, const String &p_name) {
 void EditorSettingsDialog::_undo_redo_callback(void *p_self, const String &p_name) {
 	EditorNode::get_log()->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
 	EditorNode::get_log()->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
 }
 }
@@ -253,6 +243,10 @@ void EditorSettingsDialog::_notification(int p_what) {
 			_update_icons();
 			_update_icons();
 		} break;
 		} break;
 
 
+		case NOTIFICATION_THEME_CHANGED: {
+			_update_shortcuts();
+		} break;
+
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
 			if (EditorThemeManager::is_generated_theme_outdated()) {
 			if (EditorThemeManager::is_generated_theme_outdated()) {
 				_update_icons();
 				_update_icons();
@@ -305,15 +299,13 @@ void EditorSettingsDialog::shortcut_input(const Ref<InputEvent> &p_event) {
 }
 }
 
 
 void EditorSettingsDialog::_update_icons() {
 void EditorSettingsDialog::_update_icons() {
-	search_box->set_right_icon(shortcuts->get_editor_theme_icon(SNAME("Search")));
+	search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
 	search_box->set_clear_button_enabled(true);
 	search_box->set_clear_button_enabled(true);
-	shortcut_search_box->set_right_icon(shortcuts->get_editor_theme_icon(SNAME("Search")));
-	shortcut_search_box->set_clear_button_enabled(true);
 
 
-	restart_close_button->set_button_icon(shortcuts->get_editor_theme_icon(SNAME("Close")));
-	restart_container->add_theme_style_override(SceneStringName(panel), shortcuts->get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
-	restart_icon->set_texture(shortcuts->get_editor_theme_icon(SNAME("StatusWarning")));
-	restart_label->add_theme_color_override(SceneStringName(font_color), shortcuts->get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
+	restart_close_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));
+	restart_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
+	restart_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning")));
+	restart_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
 }
 }
 
 
 void EditorSettingsDialog::_event_config_confirmed() {
 void EditorSettingsDialog::_event_config_confirmed() {
@@ -422,15 +414,15 @@ TreeItem *EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, co
 	shortcut_item->set_text(1, sc_text);
 	shortcut_item->set_text(1, sc_text);
 	if (sc_text == "None") {
 	if (sc_text == "None") {
 		// Fade out unassigned shortcut labels for easier visual grepping.
 		// Fade out unassigned shortcut labels for easier visual grepping.
-		shortcut_item->set_custom_color(1, shortcuts->get_theme_color(SceneStringName(font_color), SNAME("Label")) * Color(1, 1, 1, 0.5));
+		shortcut_item->set_custom_color(1, get_theme_color(SceneStringName(font_color), SNAME("Label")) * Color(1, 1, 1, 0.5));
 	}
 	}
 
 
 	if (p_allow_revert) {
 	if (p_allow_revert) {
-		shortcut_item->add_button(1, shortcuts->get_editor_theme_icon(SNAME("Reload")), SHORTCUT_REVERT);
+		shortcut_item->add_button(1, get_editor_theme_icon(SNAME("Reload")), SHORTCUT_REVERT);
 	}
 	}
 
 
-	shortcut_item->add_button(1, shortcuts->get_editor_theme_icon(SNAME("Add")), SHORTCUT_ADD);
-	shortcut_item->add_button(1, shortcuts->get_editor_theme_icon(SNAME("Close")), SHORTCUT_ERASE);
+	shortcut_item->add_button(1, get_editor_theme_icon(SNAME("Add")), SHORTCUT_ADD);
+	shortcut_item->add_button(1, get_editor_theme_icon(SNAME("Close")), SHORTCUT_ERASE, p_events.is_empty());
 
 
 	shortcut_item->set_meta("is_action", p_is_action);
 	shortcut_item->set_meta("is_action", p_is_action);
 	shortcut_item->set_meta("type", "shortcut");
 	shortcut_item->set_meta("type", "shortcut");
@@ -451,11 +443,11 @@ TreeItem *EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, co
 		event_item->set_text(1, ie->as_text());
 		event_item->set_text(1, ie->as_text());
 		event_item->set_auto_translate_mode(1, AUTO_TRANSLATE_MODE_DISABLED);
 		event_item->set_auto_translate_mode(1, AUTO_TRANSLATE_MODE_DISABLED);
 
 
-		event_item->add_button(1, shortcuts->get_editor_theme_icon(SNAME("Edit")), SHORTCUT_EDIT);
-		event_item->add_button(1, shortcuts->get_editor_theme_icon(SNAME("Close")), SHORTCUT_ERASE);
+		event_item->add_button(1, get_editor_theme_icon(SNAME("Edit")), SHORTCUT_EDIT);
+		event_item->add_button(1, get_editor_theme_icon(SNAME("Close")), SHORTCUT_ERASE);
 
 
-		event_item->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
-		event_item->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
+		event_item->set_custom_bg_color(0, get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
+		event_item->set_custom_bg_color(1, get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
 
 
 		event_item->set_meta("is_action", p_is_action);
 		event_item->set_meta("is_action", p_is_action);
 		event_item->set_meta("type", "event");
 		event_item->set_meta("type", "event");
@@ -466,7 +458,7 @@ TreeItem *EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, co
 }
 }
 
 
 bool EditorSettingsDialog::_should_display_shortcut(const String &p_name, const Array &p_events, bool p_match_localized_name) const {
 bool EditorSettingsDialog::_should_display_shortcut(const String &p_name, const Array &p_events, bool p_match_localized_name) const {
-	const Ref<InputEvent> search_ev = shortcut_search_by_event->get_event();
+	const Ref<InputEvent> search_ev = shortcut_search_bar->get_event();
 	if (search_ev.is_valid()) {
 	if (search_ev.is_valid()) {
 		bool event_match = false;
 		bool event_match = false;
 		for (int i = 0; i < p_events.size(); ++i) {
 		for (int i = 0; i < p_events.size(); ++i) {
@@ -481,7 +473,7 @@ bool EditorSettingsDialog::_should_display_shortcut(const String &p_name, const
 		}
 		}
 	}
 	}
 
 
-	const String &search_text = shortcut_search_box->get_text();
+	const String &search_text = shortcut_search_bar->get_name();
 	if (search_text.is_empty()) {
 	if (search_text.is_empty()) {
 		return true;
 		return true;
 	}
 	}
@@ -540,8 +532,8 @@ void EditorSettingsDialog::_update_shortcuts() {
 	if (collapsed.has("Common")) {
 	if (collapsed.has("Common")) {
 		common_section->set_collapsed(collapsed["Common"]);
 		common_section->set_collapsed(collapsed["Common"]);
 	}
 	}
-	common_section->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
-	common_section->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+	common_section->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+	common_section->set_custom_bg_color(1, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
 
 
 	// Get the action map for the editor, and add each item to the "Common" section.
 	// Get the action map for the editor, and add each item to the "Common" section.
 	for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
 	for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
@@ -595,8 +587,8 @@ void EditorSettingsDialog::_update_shortcuts() {
 		section->set_tooltip_text(0, tooltip);
 		section->set_tooltip_text(0, tooltip);
 		section->set_selectable(0, false);
 		section->set_selectable(0, false);
 		section->set_selectable(1, false);
 		section->set_selectable(1, false);
-		section->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
-		section->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+		section->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
+		section->set_custom_bg_color(1, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
 
 
 		if (collapsed.has(item_name)) {
 		if (collapsed.has(item_name)) {
 			section->set_collapsed(collapsed[item_name]);
 			section->set_collapsed(collapsed[item_name]);
@@ -635,9 +627,6 @@ void EditorSettingsDialog::_update_shortcuts() {
 			memdelete(section);
 			memdelete(section);
 		}
 		}
 	}
 	}
-
-	// Update UI.
-	clear_all_search->set_disabled(shortcut_search_box->get_text().is_empty() && shortcut_search_by_event->get_event().is_null());
 }
 }
 
 
 void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button) {
 void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button) {
@@ -853,7 +842,7 @@ void EditorSettingsDialog::_focus_current_search_box() {
 	if (tab == tab_general) {
 	if (tab == tab_general) {
 		current_search_box = search_box;
 		current_search_box = search_box;
 	} else if (tab == tab_shortcuts) {
 	} else if (tab == tab_shortcuts) {
-		current_search_box = shortcut_search_box;
+		current_search_box = shortcut_search_bar->get_name_search_box();
 	}
 	}
 
 
 	if (current_search_box) {
 	if (current_search_box) {
@@ -961,32 +950,9 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	tabs->add_child(tab_shortcuts);
 	tabs->add_child(tab_shortcuts);
 	tab_shortcuts->set_name(TTRC("Shortcuts"));
 	tab_shortcuts->set_name(TTRC("Shortcuts"));
 
 
-	HBoxContainer *top_hbox = memnew(HBoxContainer);
-	top_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	tab_shortcuts->add_child(top_hbox);
-
-	shortcut_search_box = memnew(LineEdit);
-	shortcut_search_box->set_placeholder(TTRC("Filter by Name"));
-	shortcut_search_box->set_accessibility_name(TTRC("Filter by Name"));
-	shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	top_hbox->add_child(shortcut_search_box);
-	shortcut_search_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorSettingsDialog::_filter_shortcuts));
-
-	shortcut_search_by_event = memnew(EventListenerLineEdit);
-	shortcut_search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	shortcut_search_by_event->set_stretch_ratio(0.75);
-	shortcut_search_by_event->set_allowed_input_types(INPUT_KEY);
-	shortcut_search_by_event->connect("event_changed", callable_mp(this, &EditorSettingsDialog::_filter_shortcuts_by_event));
-	shortcut_search_by_event->connect(SceneStringName(focus_entered), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(false));
-	shortcut_search_by_event->connect(SceneStringName(focus_exited), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true));
-	top_hbox->add_child(shortcut_search_by_event);
-
-	clear_all_search = memnew(Button);
-	clear_all_search->set_text(TTRC("Clear All"));
-	clear_all_search->set_tooltip_text(TTRC("Clear all search filters."));
-	clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_box, &LineEdit::clear));
-	clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_by_event, &EventListenerLineEdit::clear_event));
-	top_hbox->add_child(clear_all_search);
+	shortcut_search_bar = memnew(EditorEventSearchBar);
+	shortcut_search_bar->connect(SceneStringName(value_changed), callable_mp(this, &EditorSettingsDialog::_update_shortcuts));
+	tab_shortcuts->add_child(shortcut_search_bar);
 
 
 	shortcuts = memnew(Tree);
 	shortcuts = memnew(Tree);
 	shortcuts->set_accessibility_name(TTRC("Shortcuts"));
 	shortcuts->set_accessibility_name(TTRC("Shortcuts"));

+ 2 - 5
editor/editor_settings_dialog.h

@@ -34,6 +34,7 @@
 #include "scene/gui/dialogs.h"
 #include "scene/gui/dialogs.h"
 
 
 class CheckButton;
 class CheckButton;
+class EditorEventSearchBar;
 class EventListenerLineEdit;
 class EventListenerLineEdit;
 class InputEventConfigurationDialog;
 class InputEventConfigurationDialog;
 class PanelContainer;
 class PanelContainer;
@@ -54,10 +55,8 @@ class EditorSettingsDialog : public AcceptDialog {
 
 
 	LineEdit *search_box = nullptr;
 	LineEdit *search_box = nullptr;
 	CheckButton *advanced_switch = nullptr;
 	CheckButton *advanced_switch = nullptr;
-	LineEdit *shortcut_search_box = nullptr;
-	EventListenerLineEdit *shortcut_search_by_event = nullptr;
 	SectionedInspector *inspector = nullptr;
 	SectionedInspector *inspector = nullptr;
-	Button *clear_all_search = nullptr;
+	EditorEventSearchBar *shortcut_search_bar = nullptr;
 
 
 	// Shortcuts
 	// Shortcuts
 	enum ShortcutButton {
 	enum ShortcutButton {
@@ -109,8 +108,6 @@ class EditorSettingsDialog : public AcceptDialog {
 	PropertyInfo _create_mouse_shortcut_property_info(const String &p_property_name, const String &p_shortcut_1_name, const String &p_shortcut_2_name);
 	PropertyInfo _create_mouse_shortcut_property_info(const String &p_property_name, const String &p_shortcut_1_name, const String &p_shortcut_2_name);
 	String _get_shortcut_button_string(const String &p_shortcut_name);
 	String _get_shortcut_button_string(const String &p_shortcut_name);
 
 
-	void _filter_shortcuts(const String &p_filter);
-	void _filter_shortcuts_by_event(const Ref<InputEvent> &p_event);
 	bool _should_display_shortcut(const String &p_name, const Array &p_events, bool p_match_localized_name) const;
 	bool _should_display_shortcut(const String &p_name, const Array &p_events, bool p_match_localized_name) const;
 
 
 	void _update_shortcuts();
 	void _update_shortcuts();

+ 37 - 19
editor/event_listener_line_edit.cpp

@@ -31,6 +31,7 @@
 #include "editor/event_listener_line_edit.h"
 #include "editor/event_listener_line_edit.h"
 
 
 #include "core/input/input_map.h"
 #include "core/input/input_map.h"
+#include "scene/gui/dialogs.h"
 
 
 // Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
 // Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
 static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = {
 static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = {
@@ -162,9 +163,10 @@ void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) {
 		return;
 		return;
 	}
 	}
 
 
+	Ref<InputEvent> event_to_check = p_event;
+
 	// Allow releasing focus by holding "ui_cancel" action.
 	// Allow releasing focus by holding "ui_cancel" action.
-	bool accept_release = false;
-	uint64_t hold_to_unfocus_timeout = 3000;
+	const uint64_t hold_to_unfocus_timeout = 3000;
 	if (p_event->is_action_pressed(SNAME("ui_cancel"), true, true)) {
 	if (p_event->is_action_pressed(SNAME("ui_cancel"), true, true)) {
 		if ((OS::get_singleton()->get_ticks_msec() - hold_next) < hold_to_unfocus_timeout) {
 		if ((OS::get_singleton()->get_ticks_msec() - hold_next) < hold_to_unfocus_timeout) {
 			hold_next = 0;
 			hold_next = 0;
@@ -172,20 +174,23 @@ void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) {
 			next->grab_focus();
 			next->grab_focus();
 		} else {
 		} else {
 			hold_next = OS::get_singleton()->get_ticks_msec();
 			hold_next = OS::get_singleton()->get_ticks_msec();
+			hold_event = p_event;
 		}
 		}
 		accept_event();
 		accept_event();
 		return;
 		return;
-	} else if (p_event->is_action_released(SNAME("ui_cancel"), true)) {
-		accept_release = true;
 	}
 	}
-	hold_next = 0;
+	if (p_event->is_action_released(SNAME("ui_cancel"), true)) {
+		event_to_check = hold_event;
+		hold_next = 0;
+		hold_event = Ref<InputEvent>();
+	}
 
 
 	accept_event();
 	accept_event();
-	if (!(p_event->is_pressed() || accept_release) || p_event->is_echo() || p_event->is_match(event) || !_is_event_allowed(p_event)) {
+	if (!event_to_check->is_pressed() || event_to_check->is_echo() || event_to_check->is_match(event) || !_is_event_allowed(event_to_check)) {
 		return;
 		return;
 	}
 	}
 
 
-	event = p_event;
+	event = event_to_check;
 	set_text(get_event_text(event, false));
 	set_text(get_event_text(event, false));
 	emit_signal("event_changed", event);
 	emit_signal("event_changed", event);
 }
 }
@@ -196,15 +201,6 @@ void EventListenerLineEdit::_on_text_changed(const String &p_text) {
 	}
 	}
 }
 }
 
 
-void EventListenerLineEdit::_on_focus() {
-	set_placeholder(TTRC("Listening for Input"));
-}
-
-void EventListenerLineEdit::_on_unfocus() {
-	ignore_next_event = true;
-	set_placeholder(TTRC("Filter by Event"));
-}
-
 Ref<InputEvent> EventListenerLineEdit::get_event() const {
 Ref<InputEvent> EventListenerLineEdit::get_event() const {
 	return event;
 	return event;
 }
 }
@@ -239,17 +235,39 @@ void EventListenerLineEdit::_notification(int p_what) {
 
 
 			DisplayServer::get_singleton()->accessibility_update_set_extra_info(ae, vformat(TTR("Listening for Input. Hold %s to release focus."), InputMap::get_singleton()->get_action_description("ui_cancel")));
 			DisplayServer::get_singleton()->accessibility_update_set_extra_info(ae, vformat(TTR("Listening for Input. Hold %s to release focus."), InputMap::get_singleton()->get_action_description("ui_cancel")));
 		} break;
 		} break;
+
+		case NOTIFICATION_THEME_CHANGED: {
+			set_right_icon(get_editor_theme_icon(SNAME("Keyboard")));
+		} break;
+
 		case NOTIFICATION_ENTER_TREE: {
 		case NOTIFICATION_ENTER_TREE: {
 			connect(SceneStringName(text_changed), callable_mp(this, &EventListenerLineEdit::_on_text_changed));
 			connect(SceneStringName(text_changed), callable_mp(this, &EventListenerLineEdit::_on_text_changed));
-			connect(SceneStringName(focus_entered), callable_mp(this, &EventListenerLineEdit::_on_focus));
-			connect(SceneStringName(focus_exited), callable_mp(this, &EventListenerLineEdit::_on_unfocus));
-			set_right_icon(get_editor_theme_icon(SNAME("Keyboard")));
 			set_clear_button_enabled(true);
 			set_clear_button_enabled(true);
 		} break;
 		} break;
+
+		case NOTIFICATION_FOCUS_ENTER: {
+			AcceptDialog *dialog = Object::cast_to<AcceptDialog>(get_window());
+			if (dialog) {
+				dialog->set_close_on_escape(false);
+			}
+
+			set_placeholder(TTRC("Listening for Input"));
+		} break;
+
+		case NOTIFICATION_FOCUS_EXIT: {
+			AcceptDialog *dialog = Object::cast_to<AcceptDialog>(get_window());
+			if (dialog) {
+				dialog->set_close_on_escape(true);
+			}
+
+			ignore_next_event = true;
+			set_placeholder(TTRC("Filter by Event"));
+		} break;
 	}
 	}
 }
 }
 
 
 void EventListenerLineEdit::_bind_methods() {
 void EventListenerLineEdit::_bind_methods() {
+	// `event` is either null or a valid InputEvent that is pressed and non-echo.
 	ADD_SIGNAL(MethodInfo("event_changed", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
 	ADD_SIGNAL(MethodInfo("event_changed", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
 }
 }
 
 

+ 1 - 3
editor/event_listener_line_edit.h

@@ -43,6 +43,7 @@ class EventListenerLineEdit : public LineEdit {
 	GDCLASS(EventListenerLineEdit, LineEdit)
 	GDCLASS(EventListenerLineEdit, LineEdit)
 
 
 	uint64_t hold_next = 0;
 	uint64_t hold_next = 0;
+	Ref<InputEvent> hold_event;
 
 
 	int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;
 	int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;
 	bool ignore_next_event = true;
 	bool ignore_next_event = true;
@@ -54,9 +55,6 @@ class EventListenerLineEdit : public LineEdit {
 	void gui_input(const Ref<InputEvent> &p_event) override;
 	void gui_input(const Ref<InputEvent> &p_event) override;
 	void _on_text_changed(const String &p_text);
 	void _on_text_changed(const String &p_text);
 
 
-	void _on_focus();
-	void _on_unfocus();
-
 protected:
 protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
 	static void _bind_methods();
 	static void _bind_methods();

+ 109 - 0
editor/gui/editor_event_search_bar.cpp

@@ -0,0 +1,109 @@
+/**************************************************************************/
+/*  editor_event_search_bar.cpp                                           */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "editor_event_search_bar.h"
+
+#include "editor/event_listener_line_edit.h"
+#include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/line_edit.h"
+
+void EditorEventSearchBar::_on_event_changed(const Ref<InputEvent> &p_event) {
+	if (p_event.is_valid() && (!p_event->is_pressed() || p_event->is_echo())) {
+		return;
+	}
+	_value_changed();
+}
+
+void EditorEventSearchBar::_on_clear_all() {
+	search_by_name->set_block_signals(true);
+	search_by_name->clear();
+	search_by_name->set_block_signals(false);
+
+	search_by_event->set_block_signals(true);
+	search_by_event->clear_event();
+	search_by_event->set_block_signals(false);
+
+	_value_changed();
+}
+
+void EditorEventSearchBar::_value_changed() {
+	clear_all->set_disabled(!is_searching());
+	emit_signal(SceneStringName(value_changed));
+}
+
+bool EditorEventSearchBar::is_searching() const {
+	return !get_name().is_empty() || get_event().is_valid();
+}
+
+String EditorEventSearchBar::get_name() const {
+	return search_by_name->get_text().strip_edges();
+}
+
+Ref<InputEvent> EditorEventSearchBar::get_event() const {
+	return search_by_event->get_event();
+}
+
+void EditorEventSearchBar::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_THEME_CHANGED: {
+			search_by_name->set_right_icon(get_editor_theme_icon(SNAME("Search")));
+		} break;
+	}
+}
+
+void EditorEventSearchBar::_bind_methods() {
+	ADD_SIGNAL(MethodInfo("value_changed"));
+}
+
+EditorEventSearchBar::EditorEventSearchBar() {
+	set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+	search_by_name = memnew(LineEdit);
+	search_by_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	search_by_name->set_placeholder(TTRC("Filter by Name"));
+	search_by_name->set_accessibility_name(TTRC("Filter by Name"));
+	search_by_name->set_clear_button_enabled(true);
+	search_by_name->connect(SceneStringName(text_changed), callable_mp(this, &EditorEventSearchBar::_value_changed).unbind(1));
+	add_child(search_by_name);
+
+	search_by_event = memnew(EventListenerLineEdit);
+	search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	search_by_event->set_stretch_ratio(0.75);
+	search_by_event->set_accessibility_name(TTRC("Action Event"));
+	search_by_event->connect("event_changed", callable_mp(this, &EditorEventSearchBar::_on_event_changed));
+	add_child(search_by_event);
+
+	clear_all = memnew(Button(TTRC("Clear All")));
+	clear_all->set_tooltip_text(TTRC("Clear all search filters."));
+	clear_all->connect(SceneStringName(pressed), callable_mp(this, &EditorEventSearchBar::_on_clear_all));
+	clear_all->set_disabled(true);
+	add_child(clear_all);
+}

+ 64 - 0
editor/gui/editor_event_search_bar.h

@@ -0,0 +1,64 @@
+/**************************************************************************/
+/*  editor_event_search_bar.h                                             */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "scene/gui/box_container.h"
+
+class Button;
+class EventListenerLineEdit;
+class LineEdit;
+
+class EditorEventSearchBar : public HBoxContainer {
+	GDCLASS(EditorEventSearchBar, HBoxContainer);
+
+	LineEdit *search_by_name = nullptr;
+	EventListenerLineEdit *search_by_event = nullptr;
+	Button *clear_all = nullptr;
+
+	void _on_event_changed(const Ref<InputEvent> &p_event);
+	void _on_clear_all();
+
+	void _value_changed();
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	LineEdit *get_name_search_box() const { return search_by_name; }
+
+	bool is_searching() const;
+
+	String get_name() const;
+	Ref<InputEvent> get_event() const;
+
+	EditorEventSearchBar();
+};

+ 27 - 25
editor/input_event_configuration_dialog.cpp

@@ -94,7 +94,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, c
 				event = Ref<InputEvent>();
 				event = Ref<InputEvent>();
 				original_event = Ref<InputEvent>();
 				original_event = Ref<InputEvent>();
 				event_listener->clear_event();
 				event_listener->clear_event();
-				event_as_text->set_text(TTR("No Event Configured"));
+				event_as_text->set_text(TTRC("No Event Configured"));
 
 
 				additional_options_container->hide();
 				additional_options_container->hide();
 				input_list_tree->deselect_all();
 				input_list_tree->deselect_all();
@@ -192,7 +192,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, c
 		event = Ref<InputEvent>();
 		event = Ref<InputEvent>();
 		original_event = Ref<InputEvent>();
 		original_event = Ref<InputEvent>();
 		event_listener->clear_event();
 		event_listener->clear_event();
-		event_as_text->set_text(TTR("No Event Configured"));
+		event_as_text->set_text(TTRC("No Event Configured"));
 
 
 		additional_options_container->hide();
 		additional_options_container->hide();
 		input_list_tree->deselect_all();
 		input_list_tree->deselect_all();
@@ -587,6 +587,12 @@ void InputEventConfigurationDialog::_notification(int p_what) {
 
 
 			_update_input_list();
 			_update_input_list();
 		} break;
 		} break;
+
+		case NOTIFICATION_TRANSLATION_CHANGED: {
+			key_location->set_item_text(key_location->get_item_index((int)KeyLocation::UNSPECIFIED), TTR("Unspecified", "Key Location"));
+			key_location->set_item_text(key_location->get_item_index((int)KeyLocation::LEFT), TTR("Left", "Key Location"));
+			key_location->set_item_text(key_location->get_item_index((int)KeyLocation::RIGHT), TTR("Right", "Key Location"));
+		} break;
 	}
 	}
 }
 }
 
 
@@ -654,21 +660,19 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	event_listener->set_stretch_ratio(0.75);
 	event_listener->set_stretch_ratio(0.75);
 	event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));
 	event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));
-	event_listener->connect(SceneStringName(focus_entered), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(false));
-	event_listener->connect(SceneStringName(focus_exited), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true));
 	main_vbox->add_child(event_listener);
 	main_vbox->add_child(event_listener);
 
 
 	main_vbox->add_child(memnew(HSeparator));
 	main_vbox->add_child(memnew(HSeparator));
 
 
 	// List of all input options to manually select from.
 	// List of all input options to manually select from.
 	VBoxContainer *manual_vbox = memnew(VBoxContainer);
 	VBoxContainer *manual_vbox = memnew(VBoxContainer);
-	manual_vbox->set_name(TTR("Manual Selection"));
+	manual_vbox->set_name("Manual Selection");
 	manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	main_vbox->add_child(manual_vbox);
 	main_vbox->add_child(manual_vbox);
 
 
 	input_list_search = memnew(LineEdit);
 	input_list_search = memnew(LineEdit);
 	input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	input_list_search->set_placeholder(TTR("Filter Inputs"));
+	input_list_search->set_placeholder(TTRC("Filter Inputs"));
 	input_list_search->set_accessibility_name(TTRC("Filter Inputs"));
 	input_list_search->set_accessibility_name(TTRC("Filter Inputs"));
 	input_list_search->set_clear_button_enabled(true);
 	input_list_search->set_clear_button_enabled(true);
 	input_list_search->connect(SceneStringName(text_changed), callable_mp(this, &InputEventConfigurationDialog::_search_term_updated));
 	input_list_search->connect(SceneStringName(text_changed), callable_mp(this, &InputEventConfigurationDialog::_search_term_updated));
@@ -690,18 +694,16 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	additional_options_container = memnew(VBoxContainer);
 	additional_options_container = memnew(VBoxContainer);
 	additional_options_container->hide();
 	additional_options_container->hide();
 
 
-	Label *opts_label = memnew(Label);
+	Label *opts_label = memnew(Label(TTRC("Additional Options")));
 	opts_label->set_theme_type_variation("HeaderSmall");
 	opts_label->set_theme_type_variation("HeaderSmall");
-	opts_label->set_text(TTR("Additional Options"));
 	additional_options_container->add_child(opts_label);
 	additional_options_container->add_child(opts_label);
 
 
 	// Device Selection
 	// Device Selection
 	device_container = memnew(HBoxContainer);
 	device_container = memnew(HBoxContainer);
 	device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 
 
-	Label *device_label = memnew(Label);
+	Label *device_label = memnew(Label(TTRC("Device:")));
 	device_label->set_theme_type_variation("HeaderSmall");
 	device_label->set_theme_type_variation("HeaderSmall");
-	device_label->set_text(TTR("Device:"));
 	device_container->add_child(device_label);
 	device_container->add_child(device_label);
 
 
 	device_id_option = memnew(OptionButton);
 	device_id_option = memnew(OptionButton);
@@ -721,20 +723,20 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	mod_container = memnew(HBoxContainer);
 	mod_container = memnew(HBoxContainer);
 	for (int i = 0; i < MOD_MAX; i++) {
 	for (int i = 0; i < MOD_MAX; i++) {
 		String name = mods[i];
 		String name = mods[i];
-		mod_checkboxes[i] = memnew(CheckBox);
+		mod_checkboxes[i] = memnew(CheckBox(name));
 		mod_checkboxes[i]->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_mod_toggled).bind(i));
 		mod_checkboxes[i]->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_mod_toggled).bind(i));
-		mod_checkboxes[i]->set_text(name);
-		mod_checkboxes[i]->set_tooltip_text(TTR(mods_tip[i]));
+		mod_checkboxes[i]->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+		mod_checkboxes[i]->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS);
+		mod_checkboxes[i]->set_tooltip_text(mods_tip[i]);
 		mod_container->add_child(mod_checkboxes[i]);
 		mod_container->add_child(mod_checkboxes[i]);
 	}
 	}
 
 
 	mod_container->add_child(memnew(VSeparator));
 	mod_container->add_child(memnew(VSeparator));
 
 
-	autoremap_command_or_control_checkbox = memnew(CheckBox);
+	autoremap_command_or_control_checkbox = memnew(CheckBox(TTRC("Command / Control (auto)")));
 	autoremap_command_or_control_checkbox->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_autoremap_command_or_control_toggled));
 	autoremap_command_or_control_checkbox->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_autoremap_command_or_control_toggled));
 	autoremap_command_or_control_checkbox->set_pressed(false);
 	autoremap_command_or_control_checkbox->set_pressed(false);
-	autoremap_command_or_control_checkbox->set_text(TTR("Command / Control (auto)"));
-	autoremap_command_or_control_checkbox->set_tooltip_text(TTR("Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform."));
+	autoremap_command_or_control_checkbox->set_tooltip_text(TTRC("Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform."));
 	mod_container->add_child(autoremap_command_or_control_checkbox);
 	mod_container->add_child(autoremap_command_or_control_checkbox);
 
 
 	mod_container->hide();
 	mod_container->hide();
@@ -743,9 +745,9 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	// Key Mode Selection
 	// Key Mode Selection
 
 
 	key_mode = memnew(OptionButton);
 	key_mode = memnew(OptionButton);
-	key_mode->add_item(TTR("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE);
-	key_mode->add_item(TTR("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE);
-	key_mode->add_item(TTR("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE);
+	key_mode->add_item(TTRC("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE);
+	key_mode->add_item(TTRC("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE);
+	key_mode->add_item(TTRC("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE);
 	key_mode->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));
 	key_mode->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));
 	key_mode->hide();
 	key_mode->hide();
 	additional_options_container->add_child(key_mode);
 	additional_options_container->add_child(key_mode);
@@ -755,15 +757,15 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	location_container = memnew(HBoxContainer);
 	location_container = memnew(HBoxContainer);
 	location_container->hide();
 	location_container->hide();
 
 
-	Label *location_label = memnew(Label);
-	location_label->set_text(TTR("Physical location"));
-	location_container->add_child(location_label);
+	location_container->add_child(memnew(Label(TTRC("Physical location"))));
 
 
 	key_location = memnew(OptionButton);
 	key_location = memnew(OptionButton);
 	key_location->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	key_location->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	key_location->add_item(TTR("Any"), (int)KeyLocation::UNSPECIFIED);
-	key_location->add_item(TTR("Left"), (int)KeyLocation::LEFT);
-	key_location->add_item(TTR("Right"), (int)KeyLocation::RIGHT);
+	key_location->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+	// Item texts will be set in `NOTIFICATION_TRANSLATION_CHANGED`.
+	key_location->add_item(String(), (int)KeyLocation::UNSPECIFIED);
+	key_location->add_item(String(), (int)KeyLocation::LEFT);
+	key_location->add_item(String(), (int)KeyLocation::RIGHT);
 	key_location->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_location_selected));
 	key_location->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_location_selected));
 	key_location->set_accessibility_name(TTRC("Location"));
 	key_location->set_accessibility_name(TTRC("Location"));
 
 

+ 6 - 1
editor/input_event_configuration_dialog.h

@@ -85,7 +85,12 @@ private:
 #else
 #else
 	String mods[MOD_MAX] = { "Alt", "Shift", "Ctrl", "Meta" };
 	String mods[MOD_MAX] = { "Alt", "Shift", "Ctrl", "Meta" };
 #endif
 #endif
-	String mods_tip[MOD_MAX] = { "Alt or Option key", "Shift key", "Control key", "Meta/Windows or Command key" };
+	String mods_tip[MOD_MAX] = {
+		TTRC("Alt or Option key"),
+		TTRC("Shift key"),
+		TTRC("Control key"),
+		TTRC("Meta/Windows or Command key"),
+	};
 
 
 	CheckBox *mod_checkboxes[MOD_MAX];
 	CheckBox *mod_checkboxes[MOD_MAX];
 	CheckBox *autoremap_command_or_control_checkbox = nullptr;
 	CheckBox *autoremap_command_or_control_checkbox = nullptr;

+ 0 - 2
editor/project_settings_editor.cpp

@@ -804,8 +804,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
 	action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
 	action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
 	action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
 	action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
 	action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
-	action_map_editor->connect(SNAME("filter_focused"), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(false));
-	action_map_editor->connect(SNAME("filter_unfocused"), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true));
 	tab_container->add_child(action_map_editor);
 	tab_container->add_child(action_map_editor);
 
 
 	localization_editor = memnew(LocalizationEditor);
 	localization_editor = memnew(LocalizationEditor);