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