Merge pull request #66770 from EricEzaM/search-by-event
Add searching by event in Editor Settings shortcuts and Project Settings input map.
This commit is contained in:
commit
132a0e3242
@ -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()) {
|
||||
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 motion
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
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);
|
||||
|
||||
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
|
||||
|
@ -40,19 +40,48 @@
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
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:
|
||||
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);
|
||||
|
||||
public:
|
||||
enum InputType {
|
||||
INPUT_KEY = 1,
|
||||
INPUT_MOUSE_BUTTON = 2,
|
||||
INPUT_JOY_BUTTON = 4,
|
||||
INPUT_JOY_MOTION = 8
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user