summaryrefslogtreecommitdiffstats
path: root/editor/action_map_editor.cpp
diff options
context:
space:
mode:
authorEricEzaM <itsjusteza@gmail.com>2022-10-03 00:50:05 +1000
committerEricEzaM <itsjusteza@gmail.com>2022-10-03 19:56:34 +1000
commitcb6d7fd0596ff86f2361f7791493b6f1b30c6935 (patch)
tree7f83f2e45d34322cf7309e8f14228d1b1cd9bdb2 /editor/action_map_editor.cpp
parente69b7083d45c5d8698508cce7086d361c4b1f44c (diff)
downloadredot-engine-cb6d7fd0596ff86f2361f7791493b6f1b30c6935.tar.gz
Add searching by event for Editor Settings shortcuts and Project Settings input map.
* Focus into the LineEdit, then perform input to search the list of events by the events assigned. * New specialised editor-only control for this: EventListenerLineEdit. Line edit is a good candidate for such a control because you can focus it, override it's input handling, and show the event all in one control. Update InputEventConfigurationDialog to use event listener line edit rather than the separate tabs. * Cleaner look - no need for tabs. * Simpler code.
Diffstat (limited to 'editor/action_map_editor.cpp')
-rw-r--r--editor/action_map_editor.cpp289
1 files changed, 184 insertions, 105 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index b6348c5952..4d48dd5ac5 100644
--- a/editor/action_map_editor.cpp
+++ b/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