Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/input/input_map.cpp
20843 views
1
/**************************************************************************/
2
/* input_map.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_map.h"
32
#include "input_map.compat.inc"
33
34
#include "core/config/project_settings.h"
35
#include "core/input/input.h"
36
#include "core/os/keyboard.h"
37
#include "core/os/os.h"
38
#include "core/variant/typed_array.h"
39
40
void InputMap::_bind_methods() {
41
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
42
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::get_actions);
43
ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(DEFAULT_DEADZONE));
44
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
45
46
ClassDB::bind_method(D_METHOD("get_action_description", "action"), &InputMap::get_action_description);
47
48
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
49
ClassDB::bind_method(D_METHOD("action_get_deadzone", "action"), &InputMap::action_get_deadzone);
50
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
51
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
52
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
53
ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events);
54
ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events);
55
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false));
56
ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings);
57
}
58
59
/**
60
* Returns an nonexistent action error message with a suggestion of the closest
61
* matching action name (if possible).
62
*/
63
String InputMap::suggest_actions(const StringName &p_action) const {
64
StringName closest_action;
65
float closest_similarity = 0.0;
66
67
// Find the most action with the most similar name.
68
for (const KeyValue<StringName, Action> &kv : input_map) {
69
const float similarity = String(kv.key).similarity(p_action);
70
71
if (similarity > closest_similarity) {
72
closest_action = kv.key;
73
closest_similarity = similarity;
74
}
75
}
76
77
String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action);
78
79
if (closest_similarity >= 0.4) {
80
// Only include a suggestion in the error message if it's similar enough.
81
error_message += vformat(" Did you mean \"%s\"?", closest_action);
82
}
83
return error_message;
84
}
85
86
#ifdef TOOLS_ENABLED
87
void InputMap::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
88
const String pf = p_function;
89
bool first_argument_is_action = false;
90
if (p_idx == 0) {
91
first_argument_is_action = (pf == "has_action" || pf == "erase_action" ||
92
pf == "action_set_deadzone" || pf == "action_get_deadzone" ||
93
pf == "action_has_event" || pf == "action_add_event" || pf == "action_get_events" ||
94
pf == "action_erase_event" || pf == "action_erase_events");
95
}
96
if (first_argument_is_action || (p_idx == 1 && pf == "event_is_action")) {
97
// Cannot rely on `get_actions()`, otherwise the actions would be in the context of the Editor (no user-defined actions).
98
List<PropertyInfo> pinfo;
99
ProjectSettings::get_singleton()->get_property_list(&pinfo);
100
101
for (const PropertyInfo &pi : pinfo) {
102
if (!pi.name.begins_with("input/")) {
103
continue;
104
}
105
106
String name = pi.name.substr(pi.name.find_char('/') + 1);
107
r_options->push_back(name.quote());
108
}
109
}
110
111
Object::get_argument_options(p_function, p_idx, r_options);
112
}
113
#endif
114
115
void InputMap::add_action(const StringName &p_action, float p_deadzone) {
116
ERR_FAIL_COND_MSG(input_map.has(p_action), vformat("InputMap already has action \"%s\".", String(p_action)));
117
input_map[p_action] = Action();
118
static int last_id = 1;
119
input_map[p_action].id = last_id;
120
input_map[p_action].deadzone = p_deadzone;
121
last_id++;
122
}
123
124
void InputMap::erase_action(const StringName &p_action) {
125
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
126
127
input_map.erase(p_action);
128
}
129
130
TypedArray<StringName> InputMap::get_actions() {
131
TypedArray<StringName> ret;
132
133
ret.resize(input_map.size());
134
135
uint32_t i = 0;
136
for (const KeyValue<StringName, Action> &kv : input_map) {
137
ret[i] = kv.key;
138
i++;
139
}
140
141
return ret;
142
}
143
144
List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
145
ERR_FAIL_COND_V(p_event.is_null(), nullptr);
146
147
int i = 0;
148
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
149
int device = E->get()->get_device();
150
if (device == ALL_DEVICES || device == p_event->get_device()) {
151
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
152
if (r_event_index) {
153
*r_event_index = i;
154
}
155
return E;
156
}
157
}
158
i++;
159
}
160
161
return nullptr;
162
}
163
164
bool InputMap::has_action(const StringName &p_action) const {
165
return input_map.has(p_action);
166
}
167
168
String InputMap::get_action_description(const StringName &p_action) const {
169
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), String(), suggest_actions(p_action));
170
171
String ret;
172
const List<Ref<InputEvent>> &inputs = input_map[p_action].inputs;
173
for (Ref<InputEventKey> iek : inputs) {
174
if (iek.is_valid()) {
175
if (!ret.is_empty()) {
176
ret += RTR(" or ");
177
}
178
ret += iek->as_text();
179
}
180
}
181
if (ret.is_empty()) {
182
ret = RTR("Action has no bound inputs");
183
}
184
return ret;
185
}
186
187
float InputMap::action_get_deadzone(const StringName &p_action) {
188
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, suggest_actions(p_action));
189
190
return input_map[p_action].deadzone;
191
}
192
193
void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
194
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
195
196
input_map[p_action].deadzone = p_deadzone;
197
}
198
199
void InputMap::action_add_event(const StringName &p_action, RequiredParam<InputEvent> rp_event) {
200
EXTRACT_PARAM_OR_FAIL_MSG(p_event, rp_event, "It's not a reference to a valid InputEvent object.");
201
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
202
if (_find_event(input_map[p_action], p_event, true)) {
203
return; // Already added.
204
}
205
206
input_map[p_action].inputs.push_back(p_event);
207
}
208
209
bool InputMap::action_has_event(const StringName &p_action, RequiredParam<InputEvent> rp_event) {
210
EXTRACT_PARAM_OR_FAIL_V(p_event, rp_event, false);
211
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, suggest_actions(p_action));
212
return (_find_event(input_map[p_action], p_event, true) != nullptr);
213
}
214
215
void InputMap::action_erase_event(const StringName &p_action, RequiredParam<InputEvent> rp_event) {
216
EXTRACT_PARAM_OR_FAIL(p_event, rp_event);
217
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
218
219
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
220
if (E) {
221
input_map[p_action].inputs.erase(E);
222
223
if (Input::get_singleton()->is_action_pressed(p_action)) {
224
Input::get_singleton()->action_release(p_action);
225
}
226
}
227
}
228
229
void InputMap::action_erase_events(const StringName &p_action) {
230
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
231
232
if (Input::get_singleton()->is_action_pressed(p_action)) {
233
Input::get_singleton()->action_release(p_action);
234
}
235
236
input_map[p_action].inputs.clear();
237
}
238
239
TypedArray<InputEvent> InputMap::_action_get_events(const StringName &p_action) {
240
TypedArray<InputEvent> ret;
241
const List<Ref<InputEvent>> *al = action_get_events(p_action);
242
if (al) {
243
for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) {
244
ret.push_back(E->get());
245
}
246
}
247
248
return ret;
249
}
250
251
const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) {
252
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
253
if (!E) {
254
return nullptr;
255
}
256
257
return &E->value.inputs;
258
}
259
260
bool InputMap::event_is_action(RequiredParam<InputEvent> rp_event, const StringName &p_action, bool p_exact_match) const {
261
EXTRACT_PARAM_OR_FAIL_V(p_event, rp_event, false);
262
return event_get_action_status(p_event, p_action, p_exact_match);
263
}
264
265
int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
266
int index = -1;
267
bool valid = event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
268
return valid ? index : -1;
269
}
270
271
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
272
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
273
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
274
275
Ref<InputEventAction> input_event_action = p_event;
276
if (input_event_action.is_valid()) {
277
const bool pressed = input_event_action->is_pressed();
278
if (r_pressed != nullptr) {
279
*r_pressed = pressed;
280
}
281
const float strength = pressed ? input_event_action->get_strength() : 0.0f;
282
if (r_strength != nullptr) {
283
*r_strength = strength;
284
}
285
if (r_raw_strength != nullptr) {
286
*r_raw_strength = strength;
287
}
288
if (r_event_index) {
289
if (input_event_action->get_event_index() >= 0) {
290
*r_event_index = input_event_action->get_event_index();
291
} else {
292
*r_event_index = E->value.inputs.size();
293
}
294
}
295
return input_event_action->get_action() == p_action;
296
}
297
298
List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index);
299
return event != nullptr;
300
}
301
302
const HashMap<StringName, InputMap::Action> &InputMap::get_action_map() const {
303
return input_map;
304
}
305
306
void InputMap::load_from_project_settings() {
307
input_map.clear();
308
309
List<PropertyInfo> pinfo;
310
ProjectSettings::get_singleton()->get_property_list(&pinfo);
311
312
for (const PropertyInfo &pi : pinfo) {
313
if (!pi.name.begins_with("input/")) {
314
continue;
315
}
316
317
String name = pi.name.substr(pi.name.find_char('/') + 1);
318
319
Dictionary action = GLOBAL_GET(pi.name);
320
321
if (!action.has("events")) {
322
continue;
323
}
324
325
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
326
Array events = action["events"];
327
328
add_action(name, deadzone);
329
for (int i = 0; i < events.size(); i++) {
330
Ref<InputEvent> event = events[i];
331
if (event.is_null()) {
332
continue;
333
}
334
action_add_event(name, event);
335
}
336
}
337
}
338
339
struct _BuiltinActionDisplayName {
340
const char *name;
341
const char *display_name;
342
};
343
344
static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
345
/* clang-format off */
346
{ "ui_accept", TTRC("Accept") },
347
{ "ui_select", TTRC("Select") },
348
{ "ui_cancel", TTRC("Cancel") },
349
{ "ui_close_dialog", TTRC("Close Dialog") },
350
{ "ui_focus_next", TTRC("Focus Next") },
351
{ "ui_focus_prev", TTRC("Focus Prev") },
352
{ "ui_left", TTRC("Left") },
353
{ "ui_right", TTRC("Right") },
354
{ "ui_up", TTRC("Up") },
355
{ "ui_down", TTRC("Down") },
356
{ "ui_page_up", TTRC("Page Up") },
357
{ "ui_page_down", TTRC("Page Down") },
358
{ "ui_home", TTRC("Home") },
359
{ "ui_end", TTRC("End") },
360
{ "ui_cut", TTRC("Cut") },
361
{ "ui_copy", TTRC("Copy") },
362
{ "ui_paste", TTRC("Paste") },
363
{ "ui_focus_mode", TTRC("Toggle Tab Focus Mode") },
364
{ "ui_undo", TTRC("Undo") },
365
{ "ui_redo", TTRC("Redo") },
366
{ "ui_text_completion_query", TTRC("Completion Query") },
367
{ "ui_text_newline", TTRC("New Line") },
368
{ "ui_text_newline_blank", TTRC("New Blank Line") },
369
{ "ui_text_newline_above", TTRC("New Line Above") },
370
{ "ui_text_indent", TTRC("Indent") },
371
{ "ui_text_dedent", TTRC("Dedent") },
372
{ "ui_text_backspace", TTRC("Backspace") },
373
{ "ui_text_backspace_word", TTRC("Backspace Word") },
374
{ "ui_text_backspace_all_to_left", TTRC("Backspace all to Left") },
375
{ "ui_text_delete", TTRC("Delete") },
376
{ "ui_text_delete_word", TTRC("Delete Word") },
377
{ "ui_text_delete_all_to_right", TTRC("Delete all to Right") },
378
{ "ui_text_caret_left", TTRC("Caret Left") },
379
{ "ui_text_caret_word_left", TTRC("Caret Word Left") },
380
{ "ui_text_caret_right", TTRC("Caret Right") },
381
{ "ui_text_caret_word_right", TTRC("Caret Word Right") },
382
{ "ui_text_caret_up", TTRC("Caret Up") },
383
{ "ui_text_caret_down", TTRC("Caret Down") },
384
{ "ui_text_caret_line_start", TTRC("Caret Line Start") },
385
{ "ui_text_caret_line_end", TTRC("Caret Line End") },
386
{ "ui_text_caret_page_up", TTRC("Caret Page Up") },
387
{ "ui_text_caret_page_down", TTRC("Caret Page Down") },
388
{ "ui_text_caret_document_start", TTRC("Caret Document Start") },
389
{ "ui_text_caret_document_end", TTRC("Caret Document End") },
390
{ "ui_text_caret_add_below", TTRC("Caret Add Below") },
391
{ "ui_text_caret_add_above", TTRC("Caret Add Above") },
392
{ "ui_text_scroll_up", TTRC("Scroll Up") },
393
{ "ui_text_scroll_down", TTRC("Scroll Down") },
394
{ "ui_text_select_all", TTRC("Select All") },
395
{ "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
396
{ "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") },
397
{ "ui_text_skip_selection_for_next_occurrence", TTRC("Skip Selection for Next Occurrence") },
398
{ "ui_text_clear_carets_and_selection", TTRC("Clear Carets and Selection") },
399
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
400
{ "ui_text_submit", TTRC("Submit Text") },
401
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
402
{ "ui_graph_delete", TTRC("Delete Nodes") },
403
{ "ui_graph_follow_left", TTRC("Follow Input Port Connection") },
404
{ "ui_graph_follow_right", TTRC("Follow Output Port Connection") },
405
{ "ui_filedialog_delete", TTRC("Delete") },
406
{ "ui_filedialog_up_one_level", TTRC("Go Up One Level") },
407
{ "ui_filedialog_refresh", TTRC("Refresh") },
408
{ "ui_filedialog_show_hidden", TTRC("Show Hidden") },
409
{ "ui_filedialog_find", TTRC("Find") },
410
{ "ui_filedialog_focus_path", TTRC("Focus Path") },
411
{ "ui_swap_input_direction", TTRC("Swap Input Direction") },
412
{ "ui_unicode_start", TTRC("Start Unicode Character Input") },
413
{ "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") },
414
{ "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") },
415
{ "", ""}
416
/* clang-format on */
417
};
418
419
String InputMap::get_builtin_display_name(const String &p_name) const {
420
const String name = p_name.get_slicec('.', 0);
421
constexpr int len = std_size(_builtin_action_display_names);
422
for (int i = 0; i < len; i++) {
423
if (_builtin_action_display_names[i].name == name) {
424
return _builtin_action_display_names[i].display_name;
425
}
426
}
427
428
return p_name;
429
}
430
431
const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
432
// Return cache if it has already been built.
433
if (default_builtin_cache.size()) {
434
return default_builtin_cache;
435
}
436
437
List<Ref<InputEvent>> inputs;
438
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
439
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
440
inputs.push_back(InputEventKey::create_reference(Key::SPACE));
441
default_builtin_cache.insert("ui_accept", inputs);
442
443
inputs = List<Ref<InputEvent>>();
444
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::Y));
445
inputs.push_back(InputEventKey::create_reference(Key::SPACE));
446
default_builtin_cache.insert("ui_select", inputs);
447
448
inputs = List<Ref<InputEvent>>();
449
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
450
default_builtin_cache.insert("ui_cancel", inputs);
451
452
inputs = List<Ref<InputEvent>>();
453
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
454
default_builtin_cache.insert("ui_close_dialog", inputs);
455
456
inputs = List<Ref<InputEvent>>();
457
inputs.push_back(InputEventKey::create_reference(Key::W | KeyModifierMask::META));
458
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
459
default_builtin_cache.insert("ui_close_dialog.macos", inputs);
460
461
inputs = List<Ref<InputEvent>>();
462
inputs.push_back(InputEventKey::create_reference(Key::TAB));
463
default_builtin_cache.insert("ui_focus_next", inputs);
464
465
inputs = List<Ref<InputEvent>>();
466
inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));
467
default_builtin_cache.insert("ui_focus_prev", inputs);
468
469
inputs = List<Ref<InputEvent>>();
470
inputs.push_back(InputEventKey::create_reference(Key::LEFT));
471
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_LEFT));
472
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, -1.0));
473
default_builtin_cache.insert("ui_left", inputs);
474
475
inputs = List<Ref<InputEvent>>();
476
inputs.push_back(InputEventKey::create_reference(Key::RIGHT));
477
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_RIGHT));
478
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, 1.0));
479
default_builtin_cache.insert("ui_right", inputs);
480
481
inputs = List<Ref<InputEvent>>();
482
inputs.push_back(InputEventKey::create_reference(Key::UP));
483
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_UP));
484
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, -1.0));
485
default_builtin_cache.insert("ui_up", inputs);
486
487
inputs = List<Ref<InputEvent>>();
488
inputs.push_back(InputEventKey::create_reference(Key::DOWN));
489
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_DOWN));
490
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, 1.0));
491
default_builtin_cache.insert("ui_down", inputs);
492
493
inputs = List<Ref<InputEvent>>();
494
inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));
495
default_builtin_cache.insert("ui_page_up", inputs);
496
497
inputs = List<Ref<InputEvent>>();
498
inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));
499
default_builtin_cache.insert("ui_page_down", inputs);
500
501
inputs = List<Ref<InputEvent>>();
502
inputs.push_back(InputEventKey::create_reference(Key::HOME));
503
default_builtin_cache.insert("ui_home", inputs);
504
505
inputs = List<Ref<InputEvent>>();
506
inputs.push_back(InputEventKey::create_reference(Key::END));
507
default_builtin_cache.insert("ui_end", inputs);
508
509
inputs = List<Ref<InputEvent>>();
510
default_builtin_cache.insert("ui_accessibility_drag_and_drop", inputs);
511
512
// ///// UI basic Shortcuts /////
513
514
inputs = List<Ref<InputEvent>>();
515
inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD_OR_CTRL));
516
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::SHIFT));
517
default_builtin_cache.insert("ui_cut", inputs);
518
519
inputs = List<Ref<InputEvent>>();
520
inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD_OR_CTRL));
521
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD_OR_CTRL));
522
default_builtin_cache.insert("ui_copy", inputs);
523
524
inputs = List<Ref<InputEvent>>();
525
inputs.push_back(InputEventKey::create_reference(Key::M | KeyModifierMask::CTRL));
526
default_builtin_cache.insert("ui_focus_mode", inputs);
527
528
inputs = List<Ref<InputEvent>>();
529
inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD_OR_CTRL));
530
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT));
531
default_builtin_cache.insert("ui_paste", inputs);
532
533
inputs = List<Ref<InputEvent>>();
534
inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL));
535
default_builtin_cache.insert("ui_undo", inputs);
536
537
inputs = List<Ref<InputEvent>>();
538
inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT));
539
inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD_OR_CTRL));
540
default_builtin_cache.insert("ui_redo", inputs);
541
542
// ///// UI Text Input Shortcuts /////
543
inputs = List<Ref<InputEvent>>();
544
inputs.push_back(InputEventKey::create_reference(Key::SPACE | KeyModifierMask::CTRL));
545
default_builtin_cache.insert("ui_text_completion_query", inputs);
546
547
inputs = List<Ref<InputEvent>>();
548
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB));
549
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER));
550
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER));
551
default_builtin_cache.insert("ui_text_completion_accept", inputs);
552
553
inputs = List<Ref<InputEvent>>();
554
inputs.push_back(InputEventKey::create_reference(Key::TAB));
555
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
556
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
557
default_builtin_cache.insert("ui_text_completion_replace", inputs);
558
559
// Newlines
560
inputs = List<Ref<InputEvent>>();
561
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
562
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
563
default_builtin_cache.insert("ui_text_newline", inputs);
564
565
inputs = List<Ref<InputEvent>>();
566
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL));
567
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL));
568
default_builtin_cache.insert("ui_text_newline_blank", inputs);
569
570
inputs = List<Ref<InputEvent>>();
571
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
572
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
573
default_builtin_cache.insert("ui_text_newline_above", inputs);
574
575
// Indentation
576
inputs = List<Ref<InputEvent>>();
577
inputs.push_back(InputEventKey::create_reference(Key::TAB));
578
default_builtin_cache.insert("ui_text_indent", inputs);
579
580
inputs = List<Ref<InputEvent>>();
581
inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));
582
default_builtin_cache.insert("ui_text_dedent", inputs);
583
584
// Text Backspace and Delete
585
inputs = List<Ref<InputEvent>>();
586
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));
587
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::SHIFT));
588
default_builtin_cache.insert("ui_text_backspace", inputs);
589
590
inputs = List<Ref<InputEvent>>();
591
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL));
592
default_builtin_cache.insert("ui_text_backspace_word", inputs);
593
594
inputs = List<Ref<InputEvent>>();
595
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT));
596
default_builtin_cache.insert("ui_text_backspace_word.macos", inputs);
597
598
inputs = List<Ref<InputEvent>>();
599
default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs);
600
601
inputs = List<Ref<InputEvent>>();
602
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL));
603
default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs);
604
605
inputs = List<Ref<InputEvent>>();
606
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
607
default_builtin_cache.insert("ui_text_delete", inputs);
608
609
inputs = List<Ref<InputEvent>>();
610
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL));
611
default_builtin_cache.insert("ui_text_delete_word", inputs);
612
613
inputs = List<Ref<InputEvent>>();
614
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::ALT));
615
default_builtin_cache.insert("ui_text_delete_word.macos", inputs);
616
617
inputs = List<Ref<InputEvent>>();
618
default_builtin_cache.insert("ui_text_delete_all_to_right", inputs);
619
620
inputs = List<Ref<InputEvent>>();
621
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL));
622
default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs);
623
624
// Text Caret Movement Left/Right
625
626
inputs = List<Ref<InputEvent>>();
627
inputs.push_back(InputEventKey::create_reference(Key::LEFT));
628
default_builtin_cache.insert("ui_text_caret_left", inputs);
629
630
inputs = List<Ref<InputEvent>>();
631
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
632
default_builtin_cache.insert("ui_text_caret_word_left", inputs);
633
634
inputs = List<Ref<InputEvent>>();
635
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT));
636
default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs);
637
638
inputs = List<Ref<InputEvent>>();
639
inputs.push_back(InputEventKey::create_reference(Key::RIGHT));
640
default_builtin_cache.insert("ui_text_caret_right", inputs);
641
642
inputs = List<Ref<InputEvent>>();
643
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
644
default_builtin_cache.insert("ui_text_caret_word_right", inputs);
645
646
inputs = List<Ref<InputEvent>>();
647
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT));
648
default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs);
649
650
// Text Caret Movement Up/Down
651
652
inputs = List<Ref<InputEvent>>();
653
inputs.push_back(InputEventKey::create_reference(Key::UP));
654
default_builtin_cache.insert("ui_text_caret_up", inputs);
655
656
inputs = List<Ref<InputEvent>>();
657
inputs.push_back(InputEventKey::create_reference(Key::DOWN));
658
default_builtin_cache.insert("ui_text_caret_down", inputs);
659
660
// Text Caret Movement Line Start/End
661
662
inputs = List<Ref<InputEvent>>();
663
inputs.push_back(InputEventKey::create_reference(Key::HOME));
664
default_builtin_cache.insert("ui_text_caret_line_start", inputs);
665
666
inputs = List<Ref<InputEvent>>();
667
inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CTRL));
668
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
669
inputs.push_back(InputEventKey::create_reference(Key::HOME));
670
default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs);
671
672
inputs = List<Ref<InputEvent>>();
673
inputs.push_back(InputEventKey::create_reference(Key::END));
674
default_builtin_cache.insert("ui_text_caret_line_end", inputs);
675
676
inputs = List<Ref<InputEvent>>();
677
inputs.push_back(InputEventKey::create_reference(Key::E | KeyModifierMask::CTRL));
678
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
679
inputs.push_back(InputEventKey::create_reference(Key::END));
680
default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs);
681
682
// Text Caret Movement Page Up/Down
683
684
inputs = List<Ref<InputEvent>>();
685
inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));
686
default_builtin_cache.insert("ui_text_caret_page_up", inputs);
687
688
inputs = List<Ref<InputEvent>>();
689
inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));
690
default_builtin_cache.insert("ui_text_caret_page_down", inputs);
691
692
// Text Caret Movement Document Start/End
693
694
inputs = List<Ref<InputEvent>>();
695
inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL));
696
default_builtin_cache.insert("ui_text_caret_document_start", inputs);
697
698
inputs = List<Ref<InputEvent>>();
699
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL));
700
inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL));
701
default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs);
702
703
inputs = List<Ref<InputEvent>>();
704
inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL));
705
default_builtin_cache.insert("ui_text_caret_document_end", inputs);
706
707
inputs = List<Ref<InputEvent>>();
708
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
709
inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL));
710
default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);
711
712
// Text Caret Addition Below/Above
713
714
inputs = List<Ref<InputEvent>>();
715
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
716
default_builtin_cache.insert("ui_text_caret_add_below", inputs);
717
718
inputs = List<Ref<InputEvent>>();
719
inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
720
default_builtin_cache.insert("ui_text_caret_add_below.macos", inputs);
721
722
inputs = List<Ref<InputEvent>>();
723
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
724
default_builtin_cache.insert("ui_text_caret_add_above", inputs);
725
726
inputs = List<Ref<InputEvent>>();
727
inputs.push_back(InputEventKey::create_reference(Key::O | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
728
default_builtin_cache.insert("ui_text_caret_add_above.macos", inputs);
729
730
// Text Scrolling
731
732
inputs = List<Ref<InputEvent>>();
733
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL));
734
default_builtin_cache.insert("ui_text_scroll_up", inputs);
735
736
inputs = List<Ref<InputEvent>>();
737
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
738
default_builtin_cache.insert("ui_text_scroll_up.macos", inputs);
739
740
inputs = List<Ref<InputEvent>>();
741
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
742
default_builtin_cache.insert("ui_text_scroll_down", inputs);
743
744
inputs = List<Ref<InputEvent>>();
745
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
746
default_builtin_cache.insert("ui_text_scroll_down.macos", inputs);
747
748
// Text Misc
749
750
inputs = List<Ref<InputEvent>>();
751
inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD_OR_CTRL));
752
default_builtin_cache.insert("ui_text_select_all", inputs);
753
754
inputs = List<Ref<InputEvent>>();
755
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT));
756
default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);
757
758
inputs = List<Ref<InputEvent>>();
759
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::CTRL | KeyModifierMask::META));
760
default_builtin_cache.insert("ui_text_select_word_under_caret.macos", inputs);
761
762
inputs = List<Ref<InputEvent>>();
763
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
764
default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs);
765
766
inputs = List<Ref<InputEvent>>();
767
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
768
default_builtin_cache.insert("ui_text_skip_selection_for_next_occurrence", inputs);
769
770
inputs = List<Ref<InputEvent>>();
771
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
772
default_builtin_cache.insert("ui_text_clear_carets_and_selection", inputs);
773
774
inputs = List<Ref<InputEvent>>();
775
inputs.push_back(InputEventKey::create_reference(Key::INSERT));
776
default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);
777
778
inputs = List<Ref<InputEvent>>();
779
inputs.push_back(InputEventKey::create_reference(Key::MENU));
780
default_builtin_cache.insert("ui_menu", inputs);
781
782
inputs = List<Ref<InputEvent>>();
783
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
784
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
785
default_builtin_cache.insert("ui_text_submit", inputs);
786
787
inputs = List<Ref<InputEvent>>();
788
inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT));
789
default_builtin_cache.insert("ui_unicode_start", inputs);
790
791
// ///// UI Graph Shortcuts /////
792
793
inputs = List<Ref<InputEvent>>();
794
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
795
default_builtin_cache.insert("ui_graph_duplicate", inputs);
796
797
inputs = List<Ref<InputEvent>>();
798
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
799
default_builtin_cache.insert("ui_graph_delete", inputs);
800
801
inputs = List<Ref<InputEvent>>();
802
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
803
default_builtin_cache.insert("ui_graph_follow_left", inputs);
804
805
inputs = List<Ref<InputEvent>>();
806
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT));
807
default_builtin_cache.insert("ui_graph_follow_left.macos", inputs);
808
809
inputs = List<Ref<InputEvent>>();
810
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
811
default_builtin_cache.insert("ui_graph_follow_right", inputs);
812
813
inputs = List<Ref<InputEvent>>();
814
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT));
815
default_builtin_cache.insert("ui_graph_follow_right.macos", inputs);
816
817
// ///// UI File Dialog Shortcuts /////
818
inputs = List<Ref<InputEvent>>();
819
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
820
default_builtin_cache.insert("ui_filedialog_delete", inputs);
821
822
inputs = List<Ref<InputEvent>>();
823
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));
824
default_builtin_cache.insert("ui_filedialog_up_one_level", inputs);
825
826
inputs = List<Ref<InputEvent>>();
827
inputs.push_back(InputEventKey::create_reference(Key::F5));
828
default_builtin_cache.insert("ui_filedialog_refresh", inputs);
829
830
inputs = List<Ref<InputEvent>>();
831
inputs.push_back(InputEventKey::create_reference(Key::H));
832
default_builtin_cache.insert("ui_filedialog_show_hidden", inputs);
833
834
inputs = List<Ref<InputEvent>>();
835
inputs.push_back(InputEventKey::create_reference(Key::F | KeyModifierMask::CMD_OR_CTRL));
836
default_builtin_cache.insert("ui_filedialog_find", inputs);
837
838
inputs = List<Ref<InputEvent>>();
839
// Ctrl + L (matches most Windows/Linux file managers' "focus on path bar" shortcut,
840
// plus macOS Safari's "focus on address bar" shortcut).
841
inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::CMD_OR_CTRL));
842
default_builtin_cache.insert("ui_filedialog_focus_path", inputs);
843
844
inputs = List<Ref<InputEvent>>();
845
// Cmd + Shift + G (matches Finder's "Go To" shortcut).
846
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::CMD_OR_CTRL));
847
inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::CMD_OR_CTRL));
848
default_builtin_cache.insert("ui_filedialog_focus_path.macos", inputs);
849
850
inputs = List<Ref<InputEvent>>();
851
inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD_OR_CTRL));
852
default_builtin_cache.insert("ui_swap_input_direction", inputs);
853
854
// ///// UI ColorPicker Shortcuts /////
855
inputs = List<Ref<InputEvent>>();
856
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::X));
857
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
858
default_builtin_cache.insert("ui_colorpicker_delete_preset", inputs);
859
860
return default_builtin_cache;
861
}
862
863
const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() {
864
if (default_builtin_with_overrides_cache.size() > 0) {
865
return default_builtin_with_overrides_cache;
866
}
867
868
const HashMap<String, List<Ref<InputEvent>>> &builtins = get_builtins();
869
870
// Get a list of all built in inputs which are valid overrides for the OS
871
// Key = builtin name (e.g. ui_accept)
872
// Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature)
873
HashMap<String, Vector<String>> builtins_with_overrides;
874
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
875
String fullname = E.key;
876
877
const String &name = fullname.get_slicec('.', 0);
878
String override_for = fullname.get_slice_count(".") > 1 ? fullname.get_slicec('.', 1) : String();
879
880
if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) {
881
builtins_with_overrides[name].push_back(override_for);
882
}
883
}
884
885
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
886
String fullname = E.key;
887
888
const String &name = fullname.get_slicec('.', 0);
889
String override_for = fullname.get_slice_count(".") > 1 ? fullname.get_slicec('.', 1) : String();
890
891
if (builtins_with_overrides.has(name) && override_for.is_empty()) {
892
// Builtin has an override but this particular one is not an override, so skip.
893
continue;
894
}
895
896
if (!override_for.is_empty() && !OS::get_singleton()->has_feature(override_for)) {
897
// OS does not support this override - skip.
898
continue;
899
}
900
901
default_builtin_with_overrides_cache.insert(name, E.value);
902
}
903
904
return default_builtin_with_overrides_cache;
905
}
906
907
void InputMap::load_default() {
908
HashMap<String, List<Ref<InputEvent>>> builtins(get_builtins_with_feature_overrides_applied());
909
910
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
911
String name = E.key;
912
913
add_action(name);
914
915
const List<Ref<InputEvent>> &inputs = E.value;
916
for (const List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) {
917
Ref<InputEventKey> iek = I->get();
918
919
// For the editor, only add keyboard actions.
920
if (iek.is_valid()) {
921
action_add_event(name, I->get());
922
}
923
}
924
}
925
}
926
927
InputMap::InputMap() {
928
ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exists.");
929
singleton = this;
930
}
931
932
InputMap::~InputMap() {
933
singleton = nullptr;
934
}
935
936