1/**************************************************************************/
2/* input_event_configuration_dialog.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "editor/input_event_configuration_dialog.h"
32#include "core/input/input_map.h"
33#include "editor/editor_scale.h"
34#include "editor/editor_string_names.h"
35#include "editor/event_listener_line_edit.h"
36#include "scene/gui/check_box.h"
37#include "scene/gui/line_edit.h"
38#include "scene/gui/option_button.h"
39#include "scene/gui/separator.h"
40#include "scene/gui/tree.h"
41
42void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection) {
43 if (p_event.is_valid()) {
44 event = p_event;
45 original_event = p_original_event;
46
47 // If the event is changed to something which is not the same as the listener,
48 // clear out the event from the listener text box to avoid confusion.
49 const Ref<InputEvent> listener_event = event_listener->get_event();
50 if (listener_event.is_valid() && !listener_event->is_match(p_event)) {
51 event_listener->clear_event();
52 }
53
54 // Update Label
55 event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
56
57 Ref<InputEventKey> k = p_event;
58 Ref<InputEventMouseButton> mb = p_event;
59 Ref<InputEventJoypadButton> joyb = p_event;
60 Ref<InputEventJoypadMotion> joym = p_event;
61 Ref<InputEventWithModifiers> mod = p_event;
62
63 // Update option values and visibility
64 bool show_mods = false;
65 bool show_device = false;
66 bool show_key = false;
67
68 if (mod.is_valid()) {
69 show_mods = true;
70 mod_checkboxes[MOD_ALT]->set_pressed(mod->is_alt_pressed());
71 mod_checkboxes[MOD_SHIFT]->set_pressed(mod->is_shift_pressed());
72 mod_checkboxes[MOD_CTRL]->set_pressed(mod->is_ctrl_pressed());
73 mod_checkboxes[MOD_META]->set_pressed(mod->is_meta_pressed());
74
75 autoremap_command_or_control_checkbox->set_pressed(mod->is_command_or_control_autoremap());
76 }
77
78 if (k.is_valid()) {
79 show_key = true;
80 if (k->get_keycode() == Key::NONE && k->get_physical_keycode() == Key::NONE && k->get_key_label() != Key::NONE) {
81 key_mode->select(KEYMODE_UNICODE);
82 } else if (k->get_keycode() != Key::NONE) {
83 key_mode->select(KEYMODE_KEYCODE);
84 } else if (k->get_physical_keycode() != Key::NONE) {
85 key_mode->select(KEYMODE_PHY_KEYCODE);
86 } else {
87 // Invalid key.
88 event = Ref<InputEvent>();
89 original_event = Ref<InputEvent>();
90 event_listener->clear_event();
91 event_as_text->set_text(TTR("No Event Configured"));
92
93 additional_options_container->hide();
94 input_list_tree->deselect_all();
95 _update_input_list();
96 return;
97 }
98 } else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) {
99 show_device = true;
100 _set_current_device(event->get_device());
101 }
102
103 mod_container->set_visible(show_mods);
104 device_container->set_visible(show_device);
105 key_mode->set_visible(show_key);
106 additional_options_container->show();
107
108 // Update mode selector based on original key event.
109 Ref<InputEventKey> ko = p_original_event;
110 if (ko.is_valid()) {
111 if (ko->get_keycode() == Key::NONE) {
112 if (ko->get_physical_keycode() != Key::NONE) {
113 ko->set_keycode(ko->get_physical_keycode());
114 }
115 if (ko->get_key_label() != Key::NONE) {
116 ko->set_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));
117 }
118 }
119
120 if (ko->get_physical_keycode() == Key::NONE) {
121 if (ko->get_keycode() != Key::NONE) {
122 ko->set_physical_keycode(ko->get_keycode());
123 }
124 if (ko->get_key_label() != Key::NONE) {
125 ko->set_physical_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));
126 }
127 }
128
129 if (ko->get_key_label() == Key::NONE) {
130 if (ko->get_keycode() != Key::NONE) {
131 ko->set_key_label(fix_key_label((char32_t)ko->get_keycode(), Key::NONE));
132 }
133 if (ko->get_physical_keycode() != Key::NONE) {
134 ko->set_key_label(fix_key_label((char32_t)ko->get_physical_keycode(), Key::NONE));
135 }
136 }
137
138 key_mode->set_item_disabled(KEYMODE_KEYCODE, ko->get_keycode() == Key::NONE);
139 key_mode->set_item_disabled(KEYMODE_PHY_KEYCODE, ko->get_physical_keycode() == Key::NONE);
140 key_mode->set_item_disabled(KEYMODE_UNICODE, ko->get_key_label() == Key::NONE);
141 }
142
143 // Update selected item in input list.
144 if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) {
145 in_tree_update = true;
146 TreeItem *category = input_list_tree->get_root()->get_first_child();
147 while (category) {
148 TreeItem *input_item = category->get_first_child();
149
150 if (input_item != nullptr) {
151 // input_type should always be > 0, unless the tree structure has been misconfigured.
152 int input_type = input_item->get_parent()->get_meta("__type", 0);
153 if (input_type == 0) {
154 in_tree_update = false;
155 return;
156 }
157
158 // If event type matches input types of this category.
159 if ((k.is_valid() && input_type == INPUT_KEY) || (joyb.is_valid() && input_type == INPUT_JOY_BUTTON) || (joym.is_valid() && input_type == INPUT_JOY_MOTION) || (mb.is_valid() && input_type == INPUT_MOUSE_BUTTON)) {
160 // Loop through all items of this category until one matches.
161 while (input_item) {
162 bool key_match = k.is_valid() && (Variant(k->get_keycode()) == input_item->get_meta("__keycode") || Variant(k->get_physical_keycode()) == input_item->get_meta("__keycode"));
163 bool joyb_match = joyb.is_valid() && Variant(joyb->get_button_index()) == input_item->get_meta("__index");
164 bool joym_match = joym.is_valid() && Variant(joym->get_axis()) == input_item->get_meta("__axis") && joym->get_axis_value() == (float)input_item->get_meta("__value");
165 bool mb_match = mb.is_valid() && Variant(mb->get_button_index()) == input_item->get_meta("__index");
166 if (key_match || joyb_match || joym_match || mb_match) {
167 category->set_collapsed(false);
168 input_item->select(0);
169 input_list_tree->ensure_cursor_is_visible();
170 in_tree_update = false;
171 return;
172 }
173 input_item = input_item->get_next();
174 }
175 }
176 }
177
178 category->set_collapsed(true); // Event not in this category, so collapse;
179 category = category->get_next();
180 }
181 in_tree_update = false;
182 }
183 } else {
184 // Event is not valid, reset dialog
185 event = Ref<InputEvent>();
186 original_event = Ref<InputEvent>();
187 event_listener->clear_event();
188 event_as_text->set_text(TTR("No Event Configured"));
189
190 additional_options_container->hide();
191 input_list_tree->deselect_all();
192 _update_input_list();
193 }
194}
195
196void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEvent> &p_event) {
197 // Ignore if invalid, echo or not pressed
198 if (p_event.is_null() || p_event->is_echo() || !p_event->is_pressed()) {
199 return;
200 }
201
202 // Create an editable reference and a copy of full event.
203 Ref<InputEvent> received_event = p_event;
204 Ref<InputEvent> received_original_event = received_event->duplicate();
205
206 // Check what the type is and if it is allowed.
207 Ref<InputEventKey> k = received_event;
208 Ref<InputEventJoypadButton> joyb = received_event;
209 Ref<InputEventJoypadMotion> joym = received_event;
210 Ref<InputEventMouseButton> mb = received_event;
211
212 int type = 0;
213 if (k.is_valid()) {
214 type = INPUT_KEY;
215 } else if (joyb.is_valid()) {
216 type = INPUT_JOY_BUTTON;
217 } else if (joym.is_valid()) {
218 type = INPUT_JOY_MOTION;
219 } else if (mb.is_valid()) {
220 type = INPUT_MOUSE_BUTTON;
221 }
222
223 if (!(allowed_input_types & type)) {
224 return;
225 }
226
227 if (joym.is_valid()) {
228 joym->set_axis_value(SIGN(joym->get_axis_value()));
229 }
230
231 if (k.is_valid()) {
232 k->set_pressed(false); // To avoid serialization of 'pressed' property - doesn't matter for actions anyway.
233 if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
234 k->set_physical_keycode(Key::NONE);
235 k->set_key_label(Key::NONE);
236 } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
237 k->set_keycode(Key::NONE);
238 k->set_key_label(Key::NONE);
239 } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
240 k->set_physical_keycode(Key::NONE);
241 k->set_keycode(Key::NONE);
242 }
243 }
244
245 Ref<InputEventWithModifiers> mod = received_event;
246 if (mod.is_valid()) {
247 mod->set_window_id(0);
248 }
249
250 // Maintain device selection.
251 received_event->set_device(_get_current_device());
252
253 _set_event(received_event, received_original_event);
254}
255
256void InputEventConfigurationDialog::_on_listen_focus_changed() {
257 if (event_listener->has_focus()) {
258 set_close_on_escape(false);
259 } else {
260 set_close_on_escape(true);
261 }
262}
263
264void InputEventConfigurationDialog::_search_term_updated(const String &) {
265 _update_input_list();
266}
267
268void InputEventConfigurationDialog::_update_input_list() {
269 input_list_tree->clear();
270
271 TreeItem *root = input_list_tree->create_item();
272 String search_term = input_list_search->get_text();
273
274 bool collapse = input_list_search->get_text().is_empty();
275
276 if (allowed_input_types & INPUT_KEY) {
277 TreeItem *kb_root = input_list_tree->create_item(root);
278 kb_root->set_text(0, TTR("Keyboard Keys"));
279 kb_root->set_icon(0, icon_cache.keyboard);
280 kb_root->set_collapsed(collapse);
281 kb_root->set_meta("__type", INPUT_KEY);
282
283 for (int i = 0; i < keycode_get_count(); i++) {
284 String name = keycode_get_name_by_index(i);
285
286 if (!search_term.is_empty() && name.findn(search_term) == -1) {
287 continue;
288 }
289
290 TreeItem *item = input_list_tree->create_item(kb_root);
291 item->set_text(0, name);
292 item->set_meta("__keycode", keycode_get_value_by_index(i));
293 }
294 }
295
296 if (allowed_input_types & INPUT_MOUSE_BUTTON) {
297 TreeItem *mouse_root = input_list_tree->create_item(root);
298 mouse_root->set_text(0, TTR("Mouse Buttons"));
299 mouse_root->set_icon(0, icon_cache.mouse);
300 mouse_root->set_collapsed(collapse);
301 mouse_root->set_meta("__type", INPUT_MOUSE_BUTTON);
302
303 MouseButton mouse_buttons[9] = { MouseButton::LEFT, MouseButton::RIGHT, MouseButton::MIDDLE, MouseButton::WHEEL_UP, MouseButton::WHEEL_DOWN, MouseButton::WHEEL_LEFT, MouseButton::WHEEL_RIGHT, MouseButton::MB_XBUTTON1, MouseButton::MB_XBUTTON2 };
304 for (int i = 0; i < 9; i++) {
305 Ref<InputEventMouseButton> mb;
306 mb.instantiate();
307 mb->set_button_index(mouse_buttons[i]);
308 String desc = EventListenerLineEdit::get_event_text(mb, false);
309
310 if (!search_term.is_empty() && desc.findn(search_term) == -1) {
311 continue;
312 }
313
314 TreeItem *item = input_list_tree->create_item(mouse_root);
315 item->set_text(0, desc);
316 item->set_meta("__index", mouse_buttons[i]);
317 }
318 }
319
320 if (allowed_input_types & INPUT_JOY_BUTTON) {
321 TreeItem *joyb_root = input_list_tree->create_item(root);
322 joyb_root->set_text(0, TTR("Joypad Buttons"));
323 joyb_root->set_icon(0, icon_cache.joypad_button);
324 joyb_root->set_collapsed(collapse);
325 joyb_root->set_meta("__type", INPUT_JOY_BUTTON);
326
327 for (int i = 0; i < (int)JoyButton::MAX; i++) {
328 Ref<InputEventJoypadButton> joyb;
329 joyb.instantiate();
330 joyb->set_button_index((JoyButton)i);
331 String desc = EventListenerLineEdit::get_event_text(joyb, false);
332
333 if (!search_term.is_empty() && desc.findn(search_term) == -1) {
334 continue;
335 }
336
337 TreeItem *item = input_list_tree->create_item(joyb_root);
338 item->set_text(0, desc);
339 item->set_meta("__index", i);
340 }
341 }
342
343 if (allowed_input_types & INPUT_JOY_MOTION) {
344 TreeItem *joya_root = input_list_tree->create_item(root);
345 joya_root->set_text(0, TTR("Joypad Axes"));
346 joya_root->set_icon(0, icon_cache.joypad_axis);
347 joya_root->set_collapsed(collapse);
348 joya_root->set_meta("__type", INPUT_JOY_MOTION);
349
350 for (int i = 0; i < (int)JoyAxis::MAX * 2; i++) {
351 int axis = i / 2;
352 int direction = (i & 1) ? 1 : -1;
353 Ref<InputEventJoypadMotion> joym;
354 joym.instantiate();
355 joym->set_axis((JoyAxis)axis);
356 joym->set_axis_value(direction);
357 String desc = EventListenerLineEdit::get_event_text(joym, false);
358
359 if (!search_term.is_empty() && desc.findn(search_term) == -1) {
360 continue;
361 }
362
363 TreeItem *item = input_list_tree->create_item(joya_root);
364 item->set_text(0, desc);
365 item->set_meta("__axis", i >> 1);
366 item->set_meta("__value", (i & 1) ? 1 : -1);
367 }
368 }
369}
370
371void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) {
372 Ref<InputEventWithModifiers> ie = event;
373
374 // Not event with modifiers
375 if (ie.is_null()) {
376 return;
377 }
378
379 if (p_index == 0) {
380 ie->set_alt_pressed(p_checked);
381 } else if (p_index == 1) {
382 ie->set_shift_pressed(p_checked);
383 } else if (p_index == 2) {
384 if (!autoremap_command_or_control_checkbox->is_pressed()) {
385 ie->set_ctrl_pressed(p_checked);
386 }
387 } else if (p_index == 3) {
388 if (!autoremap_command_or_control_checkbox->is_pressed()) {
389 ie->set_meta_pressed(p_checked);
390 }
391 }
392
393 _set_event(ie, original_event);
394}
395
396void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p_checked) {
397 Ref<InputEventWithModifiers> ie = event;
398 if (ie.is_valid()) {
399 ie->set_command_or_control_autoremap(p_checked);
400 _set_event(ie, original_event);
401 }
402
403 if (p_checked) {
404 mod_checkboxes[MOD_META]->hide();
405 mod_checkboxes[MOD_CTRL]->hide();
406 } else {
407 mod_checkboxes[MOD_META]->show();
408 mod_checkboxes[MOD_CTRL]->show();
409 }
410}
411
412void InputEventConfigurationDialog::_key_mode_selected(int p_mode) {
413 Ref<InputEventKey> k = event;
414 Ref<InputEventKey> ko = original_event;
415 if (k.is_null() || ko.is_null()) {
416 return;
417 }
418
419 if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
420 k->set_keycode(ko->get_keycode());
421 k->set_physical_keycode(Key::NONE);
422 k->set_key_label(Key::NONE);
423 } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
424 k->set_keycode(Key::NONE);
425 k->set_physical_keycode(ko->get_physical_keycode());
426 k->set_key_label(Key::NONE);
427 } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
428 k->set_physical_keycode(Key::NONE);
429 k->set_keycode(Key::NONE);
430 k->set_key_label(ko->get_key_label());
431 }
432
433 _set_event(k, original_event);
434}
435
436void InputEventConfigurationDialog::_input_list_item_selected() {
437 TreeItem *selected = input_list_tree->get_selected();
438
439 // Called form _set_event, do not update for a second time.
440 if (in_tree_update) {
441 return;
442 }
443
444 // Invalid tree selection - type only exists on the "category" items, which are not a valid selection.
445 if (selected->has_meta("__type")) {
446 return;
447 }
448
449 InputType input_type = (InputType)(int)selected->get_parent()->get_meta("__type");
450
451 switch (input_type) {
452 case INPUT_KEY: {
453 Key keycode = (Key)(int)selected->get_meta("__keycode");
454 Ref<InputEventKey> k;
455 k.instantiate();
456
457 k->set_physical_keycode(keycode);
458 k->set_keycode(keycode);
459 k->set_key_label(keycode);
460
461 // Maintain modifier state from checkboxes.
462 k->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());
463 k->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());
464 if (autoremap_command_or_control_checkbox->is_pressed()) {
465 k->set_command_or_control_autoremap(true);
466 } else {
467 k->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());
468 k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());
469 }
470
471 Ref<InputEventKey> ko = k->duplicate();
472
473 if (key_mode->get_selected_id() == KEYMODE_UNICODE) {
474 key_mode->select(KEYMODE_PHY_KEYCODE);
475 }
476
477 if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {
478 k->set_physical_keycode(Key::NONE);
479 k->set_keycode(keycode);
480 k->set_key_label(Key::NONE);
481 } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {
482 k->set_physical_keycode(keycode);
483 k->set_keycode(Key::NONE);
484 k->set_key_label(Key::NONE);
485 }
486
487 _set_event(k, ko, false);
488 } break;
489 case INPUT_MOUSE_BUTTON: {
490 MouseButton idx = (MouseButton)(int)selected->get_meta("__index");
491 Ref<InputEventMouseButton> mb;
492 mb.instantiate();
493 mb->set_button_index(idx);
494 // Maintain modifier state from checkboxes
495 mb->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());
496 mb->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());
497 if (autoremap_command_or_control_checkbox->is_pressed()) {
498 mb->set_command_or_control_autoremap(true);
499 } else {
500 mb->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());
501 mb->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());
502 }
503
504 // Maintain selected device
505 mb->set_device(_get_current_device());
506
507 _set_event(mb, mb, false);
508 } break;
509 case INPUT_JOY_BUTTON: {
510 JoyButton idx = (JoyButton)(int)selected->get_meta("__index");
511 Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx);
512
513 // Maintain selected device
514 jb->set_device(_get_current_device());
515
516 _set_event(jb, jb, false);
517 } break;
518 case INPUT_JOY_MOTION: {
519 JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis");
520 int value = selected->get_meta("__value");
521
522 Ref<InputEventJoypadMotion> jm;
523 jm.instantiate();
524 jm->set_axis(axis);
525 jm->set_axis_value(value);
526
527 // Maintain selected device
528 jm->set_device(_get_current_device());
529
530 _set_event(jm, jm, false);
531 } break;
532 }
533}
534
535void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {
536 // Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
537 // and option index 1 corresponds to device 0, etc...
538 event->set_device(p_option_button_index - 1);
539 event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
540}
541
542void InputEventConfigurationDialog::_set_current_device(int p_device) {
543 device_id_option->select(p_device + 1);
544}
545
546int InputEventConfigurationDialog::_get_current_device() const {
547 return device_id_option->get_selected() - 1;
548}
549
550void InputEventConfigurationDialog::_notification(int p_what) {
551 switch (p_what) {
552 case NOTIFICATION_VISIBILITY_CHANGED: {
553 event_listener->grab_focus();
554 } break;
555
556 case NOTIFICATION_ENTER_TREE:
557 case NOTIFICATION_THEME_CHANGED: {
558 input_list_search->set_right_icon(input_list_search->get_editor_theme_icon(SNAME("Search")));
559
560 key_mode->set_item_icon(KEYMODE_KEYCODE, get_editor_theme_icon(SNAME("Keyboard")));
561 key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_editor_theme_icon(SNAME("KeyboardPhysical")));
562 key_mode->set_item_icon(KEYMODE_UNICODE, get_editor_theme_icon(SNAME("KeyboardLabel")));
563
564 icon_cache.keyboard = get_editor_theme_icon(SNAME("Keyboard"));
565 icon_cache.mouse = get_editor_theme_icon(SNAME("Mouse"));
566 icon_cache.joypad_button = get_editor_theme_icon(SNAME("JoyButton"));
567 icon_cache.joypad_axis = get_editor_theme_icon(SNAME("JoyAxis"));
568
569 event_as_text->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)));
570
571 _update_input_list();
572 } break;
573 }
574}
575
576void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event) {
577 if (p_event.is_valid()) {
578 _set_event(p_event->duplicate(), p_event);
579 } else {
580 // Clear Event
581 _set_event(Ref<InputEvent>(), Ref<InputEvent>());
582
583 // Clear Checkbox Values
584 for (int i = 0; i < MOD_MAX; i++) {
585 mod_checkboxes[i]->set_pressed(false);
586 }
587
588 // Enable the Physical Key by default to encourage its use.
589 // Physical Key should be used for most game inputs as it allows keys to work
590 // on non-QWERTY layouts out of the box.
591 // This is especially important for WASD movement layouts.
592
593 key_mode->select(KEYMODE_PHY_KEYCODE);
594 autoremap_command_or_control_checkbox->set_pressed(false);
595
596 // Select "All Devices" by default.
597 device_id_option->select(0);
598 }
599
600 popup_centered(Size2(0, 400) * EDSCALE);
601}
602
603Ref<InputEvent> InputEventConfigurationDialog::get_event() const {
604 return event;
605}
606
607void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) {
608 allowed_input_types = p_type_masks;
609 event_listener->set_allowed_input_types(p_type_masks);
610}
611
612InputEventConfigurationDialog::InputEventConfigurationDialog() {
613 allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;
614
615 set_title(TTR("Event Configuration"));
616 set_min_size(Size2i(550 * EDSCALE, 0)); // Min width
617
618 VBoxContainer *main_vbox = memnew(VBoxContainer);
619 add_child(main_vbox);
620
621 event_as_text = memnew(Label);
622 event_as_text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
623 event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
624 event_as_text->add_theme_font_size_override("font_size", 18 * EDSCALE);
625 main_vbox->add_child(event_as_text);
626
627 event_listener = memnew(EventListenerLineEdit);
628 event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);
629 event_listener->set_stretch_ratio(0.75);
630 event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));
631 event_listener->connect("focus_entered", callable_mp(this, &InputEventConfigurationDialog::_on_listen_focus_changed));
632 event_listener->connect("focus_exited", callable_mp(this, &InputEventConfigurationDialog::_on_listen_focus_changed));
633 main_vbox->add_child(event_listener);
634
635 main_vbox->add_child(memnew(HSeparator));
636
637 // List of all input options to manually select from.
638 VBoxContainer *manual_vbox = memnew(VBoxContainer);
639 manual_vbox->set_name(TTR("Manual Selection"));
640 manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
641 main_vbox->add_child(manual_vbox);
642
643 input_list_search = memnew(LineEdit);
644 input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
645 input_list_search->set_placeholder(TTR("Filter Inputs"));
646 input_list_search->set_clear_button_enabled(true);
647 input_list_search->connect("text_changed", callable_mp(this, &InputEventConfigurationDialog::_search_term_updated));
648 manual_vbox->add_child(input_list_search);
649
650 input_list_tree = memnew(Tree);
651 input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree
652 input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected));
653 input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
654 manual_vbox->add_child(input_list_tree);
655
656 input_list_tree->set_hide_root(true);
657 input_list_tree->set_columns(1);
658
659 _update_input_list();
660
661 // Additional Options
662 additional_options_container = memnew(VBoxContainer);
663 additional_options_container->hide();
664
665 Label *opts_label = memnew(Label);
666 opts_label->set_theme_type_variation("HeaderSmall");
667 opts_label->set_text(TTR("Additional Options"));
668 additional_options_container->add_child(opts_label);
669
670 // Device Selection
671 device_container = memnew(HBoxContainer);
672 device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
673
674 Label *device_label = memnew(Label);
675 device_label->set_theme_type_variation("HeaderSmall");
676 device_label->set_text(TTR("Device:"));
677 device_container->add_child(device_label);
678
679 device_id_option = memnew(OptionButton);
680 device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
681 for (int i = -1; i < 8; i++) {
682 device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
683 }
684 device_id_option->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
685 _set_current_device(InputMap::ALL_DEVICES);
686 device_container->add_child(device_id_option);
687
688 device_container->hide();
689 additional_options_container->add_child(device_container);
690
691 // Modifier Selection
692 mod_container = memnew(HBoxContainer);
693 for (int i = 0; i < MOD_MAX; i++) {
694 String name = mods[i];
695 mod_checkboxes[i] = memnew(CheckBox);
696 mod_checkboxes[i]->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_mod_toggled).bind(i));
697 mod_checkboxes[i]->set_text(name);
698 mod_checkboxes[i]->set_tooltip_text(TTR(mods_tip[i]));
699 mod_container->add_child(mod_checkboxes[i]);
700 }
701
702 mod_container->add_child(memnew(VSeparator));
703
704 autoremap_command_or_control_checkbox = memnew(CheckBox);
705 autoremap_command_or_control_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_autoremap_command_or_control_toggled));
706 autoremap_command_or_control_checkbox->set_pressed(false);
707 autoremap_command_or_control_checkbox->set_text(TTR("Command / Control (auto)"));
708 autoremap_command_or_control_checkbox->set_tooltip_text(TTR("Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform."));
709 mod_container->add_child(autoremap_command_or_control_checkbox);
710
711 mod_container->hide();
712 additional_options_container->add_child(mod_container);
713
714 // Key Mode Selection
715
716 key_mode = memnew(OptionButton);
717 key_mode->add_item(TTR("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE);
718 key_mode->add_item(TTR("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE);
719 key_mode->add_item(TTR("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE);
720 key_mode->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));
721 key_mode->hide();
722 additional_options_container->add_child(key_mode);
723
724 main_vbox->add_child(additional_options_container);
725}
726