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