Browse Source

Merge pull request #66770 from EricEzaM/search-by-event

Add searching by event in Editor Settings shortcuts and Project Settings input map.
Rémi Verschelde 2 năm trước cách đây
mục cha
commit
132a0e3242

+ 184 - 105
editor/action_map_editor.cpp

@@ -35,6 +35,106 @@
 #include "editor/editor_scale.h"
 #include "scene/gui/separator.h"
 
+bool EventListenerLineEdit::_is_event_allowed(const Ref<InputEvent> &p_event) const {
+	const Ref<InputEventMouseButton> mb = p_event;
+	const Ref<InputEventKey> k = p_event;
+	const Ref<InputEventJoypadButton> jb = p_event;
+	const Ref<InputEventJoypadMotion> jm = p_event;
+
+	return (mb.is_valid() && (allowed_input_types & INPUT_MOUSE_BUTTON)) ||
+			(k.is_valid() && (allowed_input_types & INPUT_KEY)) ||
+			(jb.is_valid() && (allowed_input_types & INPUT_JOY_BUTTON)) ||
+			(jm.is_valid() && (allowed_input_types & INPUT_JOY_MOTION));
+}
+
+void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) {
+	const Ref<InputEventMouseMotion> mm = p_event;
+	if (mm.is_valid()) {
+		LineEdit::gui_input(p_event);
+		return;
+	}
+
+	// Allow mouse button click on the clear button without being treated as an event.
+	const Ref<InputEventMouseButton> b = p_event;
+	if (b.is_valid() && _is_over_clear_button(b->get_position())) {
+		LineEdit::gui_input(p_event);
+		return;
+	}
+
+	// First event will be an event which is used to focus this control - i.e. a mouse click, or a tab press.
+	// Ignore the first one so that clicking into the LineEdit does not override the current event.
+	// Ignore is reset to true when the control is unfocused.
+	if (ignore) {
+		ignore = false;
+		return;
+	}
+
+	accept_event();
+	if (!p_event->is_pressed() || p_event->is_echo() || p_event->is_match(event) || !_is_event_allowed(p_event)) {
+		return;
+	}
+
+	event = p_event;
+	set_text(event->as_text());
+	emit_signal("event_changed", event);
+}
+
+void EventListenerLineEdit::_on_text_changed(const String &p_text) {
+	if (p_text.is_empty()) {
+		clear_event();
+	}
+}
+
+void EventListenerLineEdit::_on_focus() {
+	set_placeholder(TTR("Listening for input..."));
+}
+
+void EventListenerLineEdit::_on_unfocus() {
+	ignore = true;
+	set_placeholder(TTR("Filter by event..."));
+}
+
+Ref<InputEvent> EventListenerLineEdit::get_event() const {
+	return event;
+}
+
+void EventListenerLineEdit::clear_event() {
+	if (event.is_valid()) {
+		event = Ref<InputEvent>();
+		set_text("");
+		emit_signal("event_changed", event);
+	}
+}
+
+void EventListenerLineEdit::set_allowed_input_types(int input_types) {
+	allowed_input_types = input_types;
+}
+
+int EventListenerLineEdit::get_allowed_input_types() const {
+	return allowed_input_types;
+}
+
+void EventListenerLineEdit::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_ENTER_TREE: {
+			connect("text_changed", callable_mp(this, &EventListenerLineEdit::_on_text_changed));
+			connect("focus_entered", callable_mp(this, &EventListenerLineEdit::_on_focus));
+			connect("focus_exited", callable_mp(this, &EventListenerLineEdit::_on_unfocus));
+			set_right_icon(get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons")));
+			set_clear_button_enabled(true);
+		} break;
+	}
+}
+
+void EventListenerLineEdit::_bind_methods() {
+	ADD_SIGNAL(MethodInfo("event_changed", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
+}
+
+EventListenerLineEdit::EventListenerLineEdit() {
+	set_caret_blink_enabled(false);
+	set_placeholder(TTR("Filter by event..."));
+}
+
 /////////////////////////////////////////
 
 // Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
@@ -98,6 +198,13 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
 	if (p_event.is_valid()) {
 		event = p_event;
 
+		// If the event is changed to something which is not the same as the listener,
+		// clear out the event from the listener text box to avoid confusion.
+		const Ref<InputEvent> listener_event = event_listener->get_event();
+		if (listener_event.is_valid() && !listener_event->is_match(p_event)) {
+			event_listener->clear_event();
+		}
+
 		// Update Label
 		event_as_text->set_text(get_event_text(event, true));
 
@@ -175,31 +282,8 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
 	} else {
 		// Event is not valid, reset dialog
 		event = p_event;
-		Vector<String> strings;
-
-		// Reset message, promp for input according to which input types are allowed.
-		String text = TTR("Perform an Input (%s).");
-
-		if (allowed_input_types & INPUT_KEY) {
-			strings.append(TTR("Key"));
-		}
-
-		if (allowed_input_types & INPUT_JOY_BUTTON) {
-			strings.append(TTR("Joypad Button"));
-		}
-		if (allowed_input_types & INPUT_JOY_MOTION) {
-			strings.append(TTR("Joypad Axis"));
-		}
-		if (allowed_input_types & INPUT_MOUSE_BUTTON) {
-			strings.append(TTR("Mouse Button in area below"));
-		}
-		if (strings.size() == 0) {
-			text = TTR("Input Event dialog has been misconfigured: No input types are allowed.");
-			event_as_text->set_text(text);
-		} else {
-			String insert_text = String(", ").join(strings);
-			event_as_text->set_text(vformat(text, insert_text));
-		}
+		event_listener->clear_event();
+		event_as_text->set_text(TTR("No Event Configured"));
 
 		additional_options_container->hide();
 		input_list_tree->deselect_all();
@@ -207,54 +291,19 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
 	}
 }
 
-void InputEventConfigurationDialog::_tab_selected(int p_tab) {
-	Callable signal_method = callable_mp(this, &InputEventConfigurationDialog::_listen_window_input);
-	if (p_tab == 0) {
-		// Start Listening.
-		if (!is_connected("window_input", signal_method)) {
-			connect("window_input", signal_method);
-		}
-	} else {
-		// Stop Listening.
-		if (is_connected("window_input", signal_method)) {
-			disconnect("window_input", signal_method);
-		}
-		input_list_tree->call_deferred(SNAME("ensure_cursor_is_visible"));
-		if (input_list_tree->get_selected() == nullptr) {
-			// If nothing selected, scroll to top.
-			input_list_tree->scroll_to_item(input_list_tree->get_root());
-		}
-	}
-}
-
-void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> &p_event) {
-	// Ignore if echo or not pressed
-	if (p_event->is_echo() || !p_event->is_pressed()) {
-		return;
-	}
-
-	// Ignore mouse motion
-	Ref<InputEventMouseMotion> mm = p_event;
-	if (mm.is_valid()) {
+void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEvent> &p_event) {
+	// Ignore if invalid, echo or not pressed
+	if (p_event.is_null() || p_event->is_echo() || !p_event->is_pressed()) {
 		return;
 	}
 
-	// Ignore mouse button if not in the detection rect
-	Ref<InputEventMouseButton> mb = p_event;
-	if (mb.is_valid()) {
-		Rect2 r = mouse_detection_rect->get_rect();
-		if (!r.has_point(mouse_detection_rect->get_local_mouse_position() + r.get_position())) {
-			return;
-		}
-	}
-
 	// Create an editable reference
 	Ref<InputEvent> received_event = p_event;
-
 	// Check what the type is and if it is allowed.
 	Ref<InputEventKey> k = received_event;
 	Ref<InputEventJoypadButton> joyb = received_event;
 	Ref<InputEventJoypadMotion> joym = received_event;
+	Ref<InputEventMouseButton> mb = received_event;
 
 	int type = 0;
 	if (k.is_valid()) {
@@ -301,7 +350,14 @@ void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> &
 	received_event->set_device(_get_current_device());
 
 	_set_event(received_event);
-	set_input_as_handled();
+}
+
+void InputEventConfigurationDialog::_on_listen_focus_changed() {
+	if (event_listener->has_focus()) {
+		set_close_on_escape(false);
+	} else {
+		set_close_on_escape(true);
+	}
 }
 
 void InputEventConfigurationDialog::_search_term_updated(const String &) {
@@ -478,10 +534,10 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
 		return;
 	}
 
-	InputEventConfigurationDialog::InputType input_type = (InputEventConfigurationDialog::InputType)(int)selected->get_parent()->get_meta("__type");
+	InputType input_type = (InputType)(int)selected->get_parent()->get_meta("__type");
 
 	switch (input_type) {
-		case InputEventConfigurationDialog::INPUT_KEY: {
+		case INPUT_KEY: {
 			Key keycode = (Key)(int)selected->get_meta("__keycode");
 			Ref<InputEventKey> k;
 			k.instantiate();
@@ -506,7 +562,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
 
 			_set_event(k, false);
 		} break;
-		case InputEventConfigurationDialog::INPUT_MOUSE_BUTTON: {
+		case INPUT_MOUSE_BUTTON: {
 			MouseButton idx = (MouseButton)(int)selected->get_meta("__index");
 			Ref<InputEventMouseButton> mb;
 			mb.instantiate();
@@ -526,7 +582,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
 
 			_set_event(mb, false);
 		} break;
-		case InputEventConfigurationDialog::INPUT_JOY_BUTTON: {
+		case INPUT_JOY_BUTTON: {
 			JoyButton idx = (JoyButton)(int)selected->get_meta("__index");
 			Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx);
 
@@ -535,7 +591,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
 
 			_set_event(jb, false);
 		} break;
-		case InputEventConfigurationDialog::INPUT_JOY_MOTION: {
+		case INPUT_JOY_MOTION: {
 			JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis");
 			int value = selected->get_meta("__value");
 
@@ -611,10 +667,6 @@ void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p
 		physical_key_checkbox->set_pressed(true);
 
 		autoremap_command_or_control_checkbox->set_pressed(false);
-		_set_current_device(0);
-
-		// Switch to "Listen" tab
-		tab_container->set_current_tab(0);
 
 		// Select "All Devices" by default.
 		device_id_option->select(0);
@@ -632,7 +684,7 @@ void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) {
 }
 
 InputEventConfigurationDialog::InputEventConfigurationDialog() {
-	allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION | INPUT_MOUSE_BUTTON;
+	allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;
 
 	set_title(TTR("Event Configuration"));
 	set_min_size(Size2i(550 * EDSCALE, 0)); // Min width
@@ -640,31 +692,28 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	VBoxContainer *main_vbox = memnew(VBoxContainer);
 	add_child(main_vbox);
 
-	tab_container = memnew(TabContainer);
-	tab_container->set_use_hidden_tabs_for_min_size(true);
-	tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-	tab_container->set_theme_type_variation("TabContainerOdd");
-	tab_container->connect("tab_selected", callable_mp(this, &InputEventConfigurationDialog::_tab_selected));
-	main_vbox->add_child(tab_container);
-
-	// Listen to input tab
-	VBoxContainer *vb = memnew(VBoxContainer);
-	vb->set_name(TTR("Listen for Input"));
 	event_as_text = memnew(Label);
+	event_as_text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
 	event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
-	vb->add_child(event_as_text);
-	// Mouse button detection rect (Mouse button event outside this rect will be ignored)
-	mouse_detection_rect = memnew(Panel);
-	mouse_detection_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-	vb->add_child(mouse_detection_rect);
-	tab_container->add_child(vb);
+	event_as_text->add_theme_font_override("font", get_theme_font(SNAME("bold"), SNAME("EditorFonts")));
+	event_as_text->add_theme_font_size_override("font_size", 18 * EDSCALE);
+	main_vbox->add_child(event_as_text);
 
-	// List of all input options to manually select from.
+	event_listener = memnew(EventListenerLineEdit);
+	event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	event_listener->set_stretch_ratio(0.75);
+	event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));
+	event_listener->connect("focus_entered", callable_mp(this, &InputEventConfigurationDialog::_on_listen_focus_changed));
+	event_listener->connect("focus_exited", callable_mp(this, &InputEventConfigurationDialog::_on_listen_focus_changed));
+	main_vbox->add_child(event_listener);
 
+	main_vbox->add_child(memnew(HSeparator));
+
+	// List of all input options to manually select from.
 	VBoxContainer *manual_vbox = memnew(VBoxContainer);
 	manual_vbox->set_name(TTR("Manual Selection"));
 	manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-	tab_container->add_child(manual_vbox);
+	main_vbox->add_child(manual_vbox);
 
 	input_list_search = memnew(LineEdit);
 	input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -747,9 +796,6 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
 	additional_options_container->add_child(physical_key_checkbox);
 
 	main_vbox->add_child(additional_options_container);
-
-	// Default to first tab
-	tab_container->set_current_tab(0);
 }
 
 /////////////////////////////////////////
@@ -944,6 +990,12 @@ 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();
+	}
+}
+
 Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
 	TreeItem *selected = action_tree->get_selected();
 	if (!selected) {
@@ -1084,6 +1136,22 @@ InputEventConfigurationDialog *ActionMapEditor::get_configuration_dialog() {
 	return event_config_dialog;
 }
 
+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();
+	bool event_match = true;
+	if (search_ev.is_valid()) {
+		event_match = false;
+		for (int i = 0; i < p_events.size(); ++i) {
+			const Ref<InputEvent> ev = p_events[i];
+			if (ev.is_valid() && ev->is_match(search_ev, true)) {
+				event_match = true;
+			}
+		}
+	}
+
+	return event_match && action_list_search->get_text().is_subsequence_ofn(p_name);
+}
+
 void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_infos) {
 	if (!p_action_infos.is_empty()) {
 		actions_cache = p_action_infos;
@@ -1101,8 +1169,8 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 			uneditable_count++;
 		}
 
-		String search_term = action_list_search->get_text();
-		if (!search_term.is_empty() && action_info.name.findn(search_term) == -1) {
+		const Array events = action_info.action["events"];
+		if (!_should_display_action(action_info.name, events)) {
 			continue;
 		}
 
@@ -1110,7 +1178,6 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
 			continue;
 		}
 
-		const Array events = action_info.action["events"];
 		const Variant deadzone = action_info.action["deadzone"];
 
 		// Update Tree...
@@ -1206,16 +1273,22 @@ ActionMapEditor::ActionMapEditor() {
 
 	action_list_search = memnew(LineEdit);
 	action_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	action_list_search->set_placeholder(TTR("Filter Actions"));
+	action_list_search->set_placeholder(TTR("Filter by name..."));
 	action_list_search->set_clear_button_enabled(true);
 	action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated));
 	top_hbox->add_child(action_list_search);
 
-	show_builtin_actions_checkbutton = memnew(CheckButton);
-	show_builtin_actions_checkbutton->set_pressed(false);
-	show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions"));
-	show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
-	top_hbox->add_child(show_builtin_actions_checkbutton);
+	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->connect("event_changed", callable_mp(this, &ActionMapEditor::_search_by_event));
+	top_hbox->add_child(action_list_search_by_event);
+
+	Button *clear_all_search = memnew(Button);
+	clear_all_search->set_text(TTR("Clear All"));
+	clear_all_search->connect("pressed", callable_mp(action_list_search_by_event, &EventListenerLineEdit::clear_event));
+	clear_all_search->connect("pressed", callable_mp(action_list_search, &LineEdit::clear));
+	top_hbox->add_child(clear_all_search);
 
 	// Adding Action line edit + button
 	add_hbox = memnew(HBoxContainer);
@@ -1236,6 +1309,12 @@ ActionMapEditor::ActionMapEditor() {
 	// Disable the button and set its tooltip.
 	_add_edit_text_changed(add_edit->get_text());
 
+	show_builtin_actions_checkbutton = memnew(CheckButton);
+	show_builtin_actions_checkbutton->set_pressed(false);
+	show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions"));
+	show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
+	add_hbox->add_child(show_builtin_actions_checkbutton);
+
 	main_vbox->add_child(add_hbox);
 
 	// Action Editor Tree

+ 45 - 16
editor/action_map_editor.h

@@ -40,19 +40,48 @@
 #include "scene/gui/tab_container.h"
 #include "scene/gui/tree.h"
 
-// Confirmation Dialog used when configuring an input event.
-// Separate from ActionMapEditor for code cleanliness and separation of responsibilities.
-class InputEventConfigurationDialog : public ConfirmationDialog {
-	GDCLASS(InputEventConfigurationDialog, ConfirmationDialog);
+enum InputType {
+	INPUT_KEY = 1,
+	INPUT_MOUSE_BUTTON = 2,
+	INPUT_JOY_BUTTON = 4,
+	INPUT_JOY_MOTION = 8
+};
+
+class EventListenerLineEdit : public LineEdit {
+	GDCLASS(EventListenerLineEdit, LineEdit)
+
+	int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;
+	bool ignore = true;
+	bool share_keycodes = false;
+	Ref<InputEvent> event;
+
+	bool _is_event_allowed(const Ref<InputEvent> &p_event) const;
+
+	void gui_input(const Ref<InputEvent> &p_event) override;
+	void _on_text_changed(const String &p_text);
+
+	void _on_focus();
+	void _on_unfocus();
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
 
 public:
-	enum InputType {
-		INPUT_KEY = 1,
-		INPUT_MOUSE_BUTTON = 2,
-		INPUT_JOY_BUTTON = 4,
-		INPUT_JOY_MOTION = 8
-	};
+	Ref<InputEvent> get_event() const;
+	void clear_event();
+
+	void set_allowed_input_types(int input_types);
+	int get_allowed_input_types() const;
+
+public:
+	EventListenerLineEdit();
+};
 
+// Confirmation Dialog used when configuring an input event.
+// Separate from ActionMapEditor for code cleanliness and separation of responsibilities.
+class InputEventConfigurationDialog : public ConfirmationDialog {
+	GDCLASS(InputEventConfigurationDialog, ConfirmationDialog)
 private:
 	struct IconCache {
 		Ref<Texture2D> keyboard;
@@ -63,11 +92,9 @@ private:
 
 	Ref<InputEvent> event = Ref<InputEvent>();
 
-	TabContainer *tab_container = nullptr;
-
 	// Listening for input
+	EventListenerLineEdit *event_listener = nullptr;
 	Label *event_as_text = nullptr;
-	Panel *mouse_detection_rect = nullptr;
 
 	// List of All Key/Mouse/Joypad input options.
 	int allowed_input_types;
@@ -104,9 +131,8 @@ private:
 	CheckBox *physical_key_checkbox = nullptr;
 
 	void _set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection = true);
-
-	void _tab_selected(int p_tab);
-	void _listen_window_input(const Ref<InputEvent> &p_event);
+	void _on_listen_input_changed(const Ref<InputEvent> &p_event);
+	void _on_listen_focus_changed();
 
 	void _search_term_updated(const String &p_term);
 	void _update_input_list();
@@ -174,6 +200,7 @@ private:
 	bool show_builtin_actions = false;
 	CheckButton *show_builtin_actions_checkbutton = nullptr;
 	LineEdit *action_list_search = nullptr;
+	EventListenerLineEdit *action_list_search_by_event = nullptr;
 
 	HBoxContainer *add_hbox = nullptr;
 	LineEdit *add_edit = nullptr;
@@ -191,6 +218,8 @@ private:
 	void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
 	void _tree_item_activated();
 	void _search_term_updated(const String &p_search_term);
+	void _search_by_event(const Ref<InputEvent> &p_event);
+	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);
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;

+ 46 - 25
editor/editor_settings_dialog.cpp

@@ -106,11 +106,16 @@ void EditorSettingsDialog::popup_edit_settings() {
 	_focus_current_search_box();
 }
 
-void EditorSettingsDialog::_filter_shortcuts(const String &p_filter) {
-	shortcut_filter = p_filter;
+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) {
 	EditorNode::get_log()->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
 }
@@ -326,6 +331,22 @@ void EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, const S
 	}
 }
 
+bool EditorSettingsDialog::_should_display_shortcut(const String &p_name, const Array &p_events) const {
+	const Ref<InputEvent> search_ev = shortcut_search_by_event->get_event();
+	bool event_match = true;
+	if (search_ev.is_valid()) {
+		event_match = false;
+		for (int i = 0; i < p_events.size(); ++i) {
+			const Ref<InputEvent> ev = p_events[i];
+			if (ev.is_valid() && ev->is_match(search_ev, true)) {
+				event_match = true;
+			}
+		}
+	}
+
+	return event_match && shortcut_search_box->get_text().is_subsequence_ofn(p_name);
+}
+
 void EditorSettingsDialog::_update_shortcuts() {
 	// Before clearing the tree, take note of which categories are collapsed so that this state can be maintained when the tree is repopulated.
 	HashMap<String, bool> collapsed;
@@ -379,32 +400,17 @@ void EditorSettingsDialog::_update_shortcuts() {
 		const String &action_name = E.key;
 		const InputMap::Action &action = E.value;
 
-		Array events; // Need to get the list of events into an array so it can be set as metadata on the item.
-		Vector<String> event_strings;
-
 		// Skip non-builtin actions.
 		if (!InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().has(action_name)) {
 			continue;
 		}
 
 		const List<Ref<InputEvent>> &all_default_events = InputMap::get_singleton()->get_builtins_with_feature_overrides_applied().find(action_name)->value;
-		List<Ref<InputEventKey>> key_default_events;
-		// Remove all non-key events from the defaults. Only check keys, since we are in the editor.
-		for (const List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) {
-			Ref<InputEventKey> k = I->get();
-			if (k.is_valid()) {
-				key_default_events.push_back(k);
-			}
-		}
-
-		// Join the text of the events with a delimiter so they can all be displayed in one cell.
-		String events_display_string = event_strings.is_empty() ? "None" : String("; ").join(event_strings);
-
-		if (!shortcut_filter.is_subsequence_ofn(action_name) && (events_display_string == "None" || !shortcut_filter.is_subsequence_ofn(events_display_string))) {
+		Array action_events = _event_list_to_array_helper(action.inputs);
+		if (!_should_display_shortcut(action_name, action_events)) {
 			continue;
 		}
 
-		Array action_events = _event_list_to_array_helper(action.inputs);
 		Array default_events = _event_list_to_array_helper(all_default_events);
 		bool same_as_defaults = Shortcut::is_event_array_equal(default_events, action_events);
 		bool collapse = !collapsed.has(action_name) || (collapsed.has(action_name) && collapsed[action_name]);
@@ -459,8 +465,7 @@ void EditorSettingsDialog::_update_shortcuts() {
 		String section_name = E.get_slice("/", 0);
 		TreeItem *section = sections[section_name];
 
-		// Shortcut Item
-		if (!shortcut_filter.is_subsequence_ofn(sc->get_name())) {
+		if (!_should_display_shortcut(sc->get_name(), sc->get_events())) {
 			continue;
 		}
 
@@ -749,12 +754,29 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	tabs->add_child(tab_shortcuts);
 	tab_shortcuts->set_name(TTR("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(TTR("Filter Shortcuts"));
+	shortcut_search_box->set_placeholder(TTR("Filter by name..."));
 	shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	tab_shortcuts->add_child(shortcut_search_box);
+	top_hbox->add_child(shortcut_search_box);
 	shortcut_search_box->connect("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));
+	top_hbox->add_child(shortcut_search_by_event);
+
+	Button *clear_all_search = memnew(Button);
+	clear_all_search->set_text(TTR("Clear All"));
+	clear_all_search->connect("pressed", callable_mp(shortcut_search_box, &LineEdit::clear));
+	clear_all_search->connect("pressed", callable_mp(shortcut_search_by_event, &EventListenerLineEdit::clear_event));
+	top_hbox->add_child(clear_all_search);
+
 	shortcuts = memnew(Tree);
 	shortcuts->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	shortcuts->set_columns(2);
@@ -771,8 +793,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	// Adding event dialog
 	shortcut_editor = memnew(InputEventConfigurationDialog);
 	shortcut_editor->connect("confirmed", callable_mp(this, &EditorSettingsDialog::_event_config_confirmed));
-	shortcut_editor->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_KEY);
-	shortcut_editor->set_close_on_escape(false);
+	shortcut_editor->set_allowed_input_types(INPUT_KEY);
 	add_child(shortcut_editor);
 
 	set_hide_on_ok(true);

+ 3 - 3
editor/editor_settings_dialog.h

@@ -53,6 +53,7 @@ class EditorSettingsDialog : public AcceptDialog {
 
 	LineEdit *search_box = nullptr;
 	LineEdit *shortcut_search_box = nullptr;
+	EventListenerLineEdit *shortcut_search_by_event = nullptr;
 	SectionedInspector *inspector = nullptr;
 
 	// Shortcuts
@@ -64,7 +65,6 @@ class EditorSettingsDialog : public AcceptDialog {
 	};
 
 	Tree *shortcuts = nullptr;
-	String shortcut_filter;
 
 	InputEventConfigurationDialog *shortcut_editor = nullptr;
 
@@ -103,13 +103,13 @@ class EditorSettingsDialog : public AcceptDialog {
 	void _focus_current_search_box();
 
 	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) const;
 
 	void _update_shortcuts();
 	void _shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button = MouseButton::LEFT);
 	void _shortcut_cell_double_clicked();
 
-	void _builtin_action_popup_index_pressed(int p_index);
-
 	static void _undo_redo_callback(void *p_self, const String &p_name);
 
 	Label *restart_label = nullptr;

+ 4 - 4
editor/plugins/input_event_editor_plugin.cpp

@@ -63,13 +63,13 @@ void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) {
 	Ref<InputEventJoypadMotion> jm = p_event;
 
 	if (k.is_valid()) {
-		config_dialog->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_KEY);
+		config_dialog->set_allowed_input_types(INPUT_KEY);
 	} else if (m.is_valid()) {
-		config_dialog->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_MOUSE_BUTTON);
+		config_dialog->set_allowed_input_types(INPUT_MOUSE_BUTTON);
 	} else if (jb.is_valid()) {
-		config_dialog->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_JOY_BUTTON);
+		config_dialog->set_allowed_input_types(INPUT_JOY_BUTTON);
 	} else if (jm.is_valid()) {
-		config_dialog->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_JOY_MOTION);
+		config_dialog->set_allowed_input_types(INPUT_JOY_MOTION);
 	}
 
 	input_event = p_event;

+ 1 - 2
scene/gui/line_edit.h

@@ -199,8 +199,6 @@ private:
 		float base_scale = 1.0;
 	} theme_cache;
 
-	bool _is_over_clear_button(const Point2 &p_pos) const;
-
 	void _clear_undo_stack();
 	void _clear_redo();
 	void _create_undo_state();
@@ -240,6 +238,7 @@ private:
 	void _ensure_menu();
 
 protected:
+	bool _is_over_clear_button(const Point2 &p_pos) const;
 	virtual void _update_theme_item_cache() override;
 	void _notification(int p_what);
 	static void _bind_methods();