Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/input/input_map.cpp
9903 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, const Ref<InputEvent> &p_event) {
200
ERR_FAIL_COND_MSG(p_event.is_null(), "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, const Ref<InputEvent> &p_event) {
210
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, suggest_actions(p_action));
211
return (_find_event(input_map[p_action], p_event, true) != nullptr);
212
}
213
214
void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
215
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
216
217
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
218
if (E) {
219
input_map[p_action].inputs.erase(E);
220
221
if (Input::get_singleton()->is_action_pressed(p_action)) {
222
Input::get_singleton()->action_release(p_action);
223
}
224
}
225
}
226
227
void InputMap::action_erase_events(const StringName &p_action) {
228
ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action));
229
230
input_map[p_action].inputs.clear();
231
}
232
233
TypedArray<InputEvent> InputMap::_action_get_events(const StringName &p_action) {
234
TypedArray<InputEvent> ret;
235
const List<Ref<InputEvent>> *al = action_get_events(p_action);
236
if (al) {
237
for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) {
238
ret.push_back(E->get());
239
}
240
}
241
242
return ret;
243
}
244
245
const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) {
246
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
247
if (!E) {
248
return nullptr;
249
}
250
251
return &E->value.inputs;
252
}
253
254
bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
255
return event_get_action_status(p_event, p_action, p_exact_match);
256
}
257
258
int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
259
int index = -1;
260
bool valid = event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
261
return valid ? index : -1;
262
}
263
264
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 {
265
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
266
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
267
268
Ref<InputEventAction> input_event_action = p_event;
269
if (input_event_action.is_valid()) {
270
const bool pressed = input_event_action->is_pressed();
271
if (r_pressed != nullptr) {
272
*r_pressed = pressed;
273
}
274
const float strength = pressed ? input_event_action->get_strength() : 0.0f;
275
if (r_strength != nullptr) {
276
*r_strength = strength;
277
}
278
if (r_raw_strength != nullptr) {
279
*r_raw_strength = strength;
280
}
281
if (r_event_index) {
282
if (input_event_action->get_event_index() >= 0) {
283
*r_event_index = input_event_action->get_event_index();
284
} else {
285
*r_event_index = E->value.inputs.size();
286
}
287
}
288
return input_event_action->get_action() == p_action;
289
}
290
291
List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index);
292
return event != nullptr;
293
}
294
295
const HashMap<StringName, InputMap::Action> &InputMap::get_action_map() const {
296
return input_map;
297
}
298
299
void InputMap::load_from_project_settings() {
300
input_map.clear();
301
302
List<PropertyInfo> pinfo;
303
ProjectSettings::get_singleton()->get_property_list(&pinfo);
304
305
for (const PropertyInfo &pi : pinfo) {
306
if (!pi.name.begins_with("input/")) {
307
continue;
308
}
309
310
String name = pi.name.substr(pi.name.find_char('/') + 1);
311
312
Dictionary action = GLOBAL_GET(pi.name);
313
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
314
Array events = action["events"];
315
316
add_action(name, deadzone);
317
for (int i = 0; i < events.size(); i++) {
318
Ref<InputEvent> event = events[i];
319
if (event.is_null()) {
320
continue;
321
}
322
action_add_event(name, event);
323
}
324
}
325
}
326
327
struct _BuiltinActionDisplayName {
328
const char *name;
329
const char *display_name;
330
};
331
332
static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
333
/* clang-format off */
334
{ "ui_accept", TTRC("Accept") },
335
{ "ui_select", TTRC("Select") },
336
{ "ui_cancel", TTRC("Cancel") },
337
{ "ui_focus_next", TTRC("Focus Next") },
338
{ "ui_focus_prev", TTRC("Focus Prev") },
339
{ "ui_left", TTRC("Left") },
340
{ "ui_right", TTRC("Right") },
341
{ "ui_up", TTRC("Up") },
342
{ "ui_down", TTRC("Down") },
343
{ "ui_page_up", TTRC("Page Up") },
344
{ "ui_page_down", TTRC("Page Down") },
345
{ "ui_home", TTRC("Home") },
346
{ "ui_end", TTRC("End") },
347
{ "ui_cut", TTRC("Cut") },
348
{ "ui_copy", TTRC("Copy") },
349
{ "ui_paste", TTRC("Paste") },
350
{ "ui_focus_mode", TTRC("Toggle Tab Focus Mode") },
351
{ "ui_undo", TTRC("Undo") },
352
{ "ui_redo", TTRC("Redo") },
353
{ "ui_text_completion_query", TTRC("Completion Query") },
354
{ "ui_text_newline", TTRC("New Line") },
355
{ "ui_text_newline_blank", TTRC("New Blank Line") },
356
{ "ui_text_newline_above", TTRC("New Line Above") },
357
{ "ui_text_indent", TTRC("Indent") },
358
{ "ui_text_dedent", TTRC("Dedent") },
359
{ "ui_text_backspace", TTRC("Backspace") },
360
{ "ui_text_backspace_word", TTRC("Backspace Word") },
361
{ "ui_text_backspace_word.macos", TTRC("Backspace Word") },
362
{ "ui_text_backspace_all_to_left", TTRC("Backspace all to Left") },
363
{ "ui_text_backspace_all_to_left.macos", TTRC("Backspace all to Left") },
364
{ "ui_text_delete", TTRC("Delete") },
365
{ "ui_text_delete_word", TTRC("Delete Word") },
366
{ "ui_text_delete_word.macos", TTRC("Delete Word") },
367
{ "ui_text_delete_all_to_right", TTRC("Delete all to Right") },
368
{ "ui_text_delete_all_to_right.macos", TTRC("Delete all to Right") },
369
{ "ui_text_caret_left", TTRC("Caret Left") },
370
{ "ui_text_caret_word_left", TTRC("Caret Word Left") },
371
{ "ui_text_caret_word_left.macos", TTRC("Caret Word Left") },
372
{ "ui_text_caret_right", TTRC("Caret Right") },
373
{ "ui_text_caret_word_right", TTRC("Caret Word Right") },
374
{ "ui_text_caret_word_right.macos", TTRC("Caret Word Right") },
375
{ "ui_text_caret_up", TTRC("Caret Up") },
376
{ "ui_text_caret_down", TTRC("Caret Down") },
377
{ "ui_text_caret_line_start", TTRC("Caret Line Start") },
378
{ "ui_text_caret_line_start.macos", TTRC("Caret Line Start") },
379
{ "ui_text_caret_line_end", TTRC("Caret Line End") },
380
{ "ui_text_caret_line_end.macos", TTRC("Caret Line End") },
381
{ "ui_text_caret_page_up", TTRC("Caret Page Up") },
382
{ "ui_text_caret_page_down", TTRC("Caret Page Down") },
383
{ "ui_text_caret_document_start", TTRC("Caret Document Start") },
384
{ "ui_text_caret_document_start.macos", TTRC("Caret Document Start") },
385
{ "ui_text_caret_document_end", TTRC("Caret Document End") },
386
{ "ui_text_caret_document_end.macos", TTRC("Caret Document End") },
387
{ "ui_text_caret_add_below", TTRC("Caret Add Below") },
388
{ "ui_text_caret_add_below.macos", TTRC("Caret Add Below") },
389
{ "ui_text_caret_add_above", TTRC("Caret Add Above") },
390
{ "ui_text_caret_add_above.macos", TTRC("Caret Add Above") },
391
{ "ui_text_scroll_up", TTRC("Scroll Up") },
392
{ "ui_text_scroll_up.macos", TTRC("Scroll Up") },
393
{ "ui_text_scroll_down", TTRC("Scroll Down") },
394
{ "ui_text_scroll_down.macos", TTRC("Scroll Down") },
395
{ "ui_text_select_all", TTRC("Select All") },
396
{ "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
397
{ "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") },
398
{ "ui_text_skip_selection_for_next_occurrence", TTRC("Skip Selection for Next Occurrence") },
399
{ "ui_text_clear_carets_and_selection", TTRC("Clear Carets and Selection") },
400
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
401
{ "ui_text_submit", TTRC("Submit Text") },
402
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
403
{ "ui_graph_delete", TTRC("Delete Nodes") },
404
{ "ui_graph_follow_left", TTRC("Follow Input Port Connection") },
405
{ "ui_graph_follow_right", TTRC("Follow Output Port Connection") },
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_swap_input_direction ", TTRC("Swap Input Direction") },
410
{ "ui_unicode_start", TTRC("Start Unicode Character Input") },
411
{ "ui_colorpicker_delete_preset", TTRC("ColorPicker: Delete Preset") },
412
{ "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") },
413
{ "", ""}
414
/* clang-format on */
415
};
416
417
String InputMap::get_builtin_display_name(const String &p_name) const {
418
constexpr int len = std::size(_builtin_action_display_names);
419
420
for (int i = 0; i < len; i++) {
421
if (_builtin_action_display_names[i].name == p_name) {
422
return _builtin_action_display_names[i].display_name;
423
}
424
}
425
426
return p_name;
427
}
428
429
const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
430
// Return cache if it has already been built.
431
if (default_builtin_cache.size()) {
432
return default_builtin_cache;
433
}
434
435
List<Ref<InputEvent>> inputs;
436
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
437
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
438
inputs.push_back(InputEventKey::create_reference(Key::SPACE));
439
default_builtin_cache.insert("ui_accept", inputs);
440
441
inputs = List<Ref<InputEvent>>();
442
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::Y));
443
inputs.push_back(InputEventKey::create_reference(Key::SPACE));
444
default_builtin_cache.insert("ui_select", inputs);
445
446
inputs = List<Ref<InputEvent>>();
447
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
448
default_builtin_cache.insert("ui_cancel", inputs);
449
450
inputs = List<Ref<InputEvent>>();
451
inputs.push_back(InputEventKey::create_reference(Key::TAB));
452
default_builtin_cache.insert("ui_focus_next", inputs);
453
454
inputs = List<Ref<InputEvent>>();
455
inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));
456
default_builtin_cache.insert("ui_focus_prev", inputs);
457
458
inputs = List<Ref<InputEvent>>();
459
inputs.push_back(InputEventKey::create_reference(Key::LEFT));
460
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_LEFT));
461
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, -1.0));
462
default_builtin_cache.insert("ui_left", inputs);
463
464
inputs = List<Ref<InputEvent>>();
465
inputs.push_back(InputEventKey::create_reference(Key::RIGHT));
466
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_RIGHT));
467
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, 1.0));
468
default_builtin_cache.insert("ui_right", inputs);
469
470
inputs = List<Ref<InputEvent>>();
471
inputs.push_back(InputEventKey::create_reference(Key::UP));
472
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_UP));
473
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, -1.0));
474
default_builtin_cache.insert("ui_up", inputs);
475
476
inputs = List<Ref<InputEvent>>();
477
inputs.push_back(InputEventKey::create_reference(Key::DOWN));
478
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_DOWN));
479
inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, 1.0));
480
default_builtin_cache.insert("ui_down", inputs);
481
482
inputs = List<Ref<InputEvent>>();
483
inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));
484
default_builtin_cache.insert("ui_page_up", inputs);
485
486
inputs = List<Ref<InputEvent>>();
487
inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));
488
default_builtin_cache.insert("ui_page_down", inputs);
489
490
inputs = List<Ref<InputEvent>>();
491
inputs.push_back(InputEventKey::create_reference(Key::HOME));
492
default_builtin_cache.insert("ui_home", inputs);
493
494
inputs = List<Ref<InputEvent>>();
495
inputs.push_back(InputEventKey::create_reference(Key::END));
496
default_builtin_cache.insert("ui_end", inputs);
497
498
inputs = List<Ref<InputEvent>>();
499
default_builtin_cache.insert("ui_accessibility_drag_and_drop", inputs);
500
501
// ///// UI basic Shortcuts /////
502
503
inputs = List<Ref<InputEvent>>();
504
inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD_OR_CTRL));
505
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::SHIFT));
506
default_builtin_cache.insert("ui_cut", inputs);
507
508
inputs = List<Ref<InputEvent>>();
509
inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD_OR_CTRL));
510
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD_OR_CTRL));
511
default_builtin_cache.insert("ui_copy", inputs);
512
513
inputs = List<Ref<InputEvent>>();
514
inputs.push_back(InputEventKey::create_reference(Key::M | KeyModifierMask::CTRL));
515
default_builtin_cache.insert("ui_focus_mode", inputs);
516
517
inputs = List<Ref<InputEvent>>();
518
inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD_OR_CTRL));
519
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT));
520
default_builtin_cache.insert("ui_paste", inputs);
521
522
inputs = List<Ref<InputEvent>>();
523
inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL));
524
default_builtin_cache.insert("ui_undo", inputs);
525
526
inputs = List<Ref<InputEvent>>();
527
inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT));
528
inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD_OR_CTRL));
529
default_builtin_cache.insert("ui_redo", inputs);
530
531
// ///// UI Text Input Shortcuts /////
532
inputs = List<Ref<InputEvent>>();
533
inputs.push_back(InputEventKey::create_reference(Key::SPACE | KeyModifierMask::CTRL));
534
default_builtin_cache.insert("ui_text_completion_query", inputs);
535
536
inputs = List<Ref<InputEvent>>();
537
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB));
538
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER));
539
inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER));
540
default_builtin_cache.insert("ui_text_completion_accept", inputs);
541
542
inputs = List<Ref<InputEvent>>();
543
inputs.push_back(InputEventKey::create_reference(Key::TAB));
544
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
545
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
546
default_builtin_cache.insert("ui_text_completion_replace", inputs);
547
548
// Newlines
549
inputs = List<Ref<InputEvent>>();
550
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
551
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
552
default_builtin_cache.insert("ui_text_newline", inputs);
553
554
inputs = List<Ref<InputEvent>>();
555
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL));
556
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL));
557
default_builtin_cache.insert("ui_text_newline_blank", inputs);
558
559
inputs = List<Ref<InputEvent>>();
560
inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
561
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
562
default_builtin_cache.insert("ui_text_newline_above", inputs);
563
564
// Indentation
565
inputs = List<Ref<InputEvent>>();
566
inputs.push_back(InputEventKey::create_reference(Key::TAB));
567
default_builtin_cache.insert("ui_text_indent", inputs);
568
569
inputs = List<Ref<InputEvent>>();
570
inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT));
571
default_builtin_cache.insert("ui_text_dedent", inputs);
572
573
// Text Backspace and Delete
574
inputs = List<Ref<InputEvent>>();
575
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));
576
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::SHIFT));
577
default_builtin_cache.insert("ui_text_backspace", inputs);
578
579
inputs = List<Ref<InputEvent>>();
580
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL));
581
default_builtin_cache.insert("ui_text_backspace_word", inputs);
582
583
inputs = List<Ref<InputEvent>>();
584
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT));
585
default_builtin_cache.insert("ui_text_backspace_word.macos", inputs);
586
587
inputs = List<Ref<InputEvent>>();
588
default_builtin_cache.insert("ui_text_backspace_all_to_left", 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_all_to_left.macos", inputs);
593
594
inputs = List<Ref<InputEvent>>();
595
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
596
default_builtin_cache.insert("ui_text_delete", inputs);
597
598
inputs = List<Ref<InputEvent>>();
599
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL));
600
default_builtin_cache.insert("ui_text_delete_word", inputs);
601
602
inputs = List<Ref<InputEvent>>();
603
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::ALT));
604
default_builtin_cache.insert("ui_text_delete_word.macos", inputs);
605
606
inputs = List<Ref<InputEvent>>();
607
default_builtin_cache.insert("ui_text_delete_all_to_right", 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_all_to_right.macos", inputs);
612
613
// Text Caret Movement Left/Right
614
615
inputs = List<Ref<InputEvent>>();
616
inputs.push_back(InputEventKey::create_reference(Key::LEFT));
617
default_builtin_cache.insert("ui_text_caret_left", inputs);
618
619
inputs = List<Ref<InputEvent>>();
620
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
621
default_builtin_cache.insert("ui_text_caret_word_left", inputs);
622
623
inputs = List<Ref<InputEvent>>();
624
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT));
625
default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs);
626
627
inputs = List<Ref<InputEvent>>();
628
inputs.push_back(InputEventKey::create_reference(Key::RIGHT));
629
default_builtin_cache.insert("ui_text_caret_right", inputs);
630
631
inputs = List<Ref<InputEvent>>();
632
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
633
default_builtin_cache.insert("ui_text_caret_word_right", inputs);
634
635
inputs = List<Ref<InputEvent>>();
636
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT));
637
default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs);
638
639
// Text Caret Movement Up/Down
640
641
inputs = List<Ref<InputEvent>>();
642
inputs.push_back(InputEventKey::create_reference(Key::UP));
643
default_builtin_cache.insert("ui_text_caret_up", inputs);
644
645
inputs = List<Ref<InputEvent>>();
646
inputs.push_back(InputEventKey::create_reference(Key::DOWN));
647
default_builtin_cache.insert("ui_text_caret_down", inputs);
648
649
// Text Caret Movement Line Start/End
650
651
inputs = List<Ref<InputEvent>>();
652
inputs.push_back(InputEventKey::create_reference(Key::HOME));
653
default_builtin_cache.insert("ui_text_caret_line_start", inputs);
654
655
inputs = List<Ref<InputEvent>>();
656
inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CTRL));
657
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
658
inputs.push_back(InputEventKey::create_reference(Key::HOME));
659
default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs);
660
661
inputs = List<Ref<InputEvent>>();
662
inputs.push_back(InputEventKey::create_reference(Key::END));
663
default_builtin_cache.insert("ui_text_caret_line_end", inputs);
664
665
inputs = List<Ref<InputEvent>>();
666
inputs.push_back(InputEventKey::create_reference(Key::E | KeyModifierMask::CTRL));
667
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
668
inputs.push_back(InputEventKey::create_reference(Key::END));
669
default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs);
670
671
// Text Caret Movement Page Up/Down
672
673
inputs = List<Ref<InputEvent>>();
674
inputs.push_back(InputEventKey::create_reference(Key::PAGEUP));
675
default_builtin_cache.insert("ui_text_caret_page_up", inputs);
676
677
inputs = List<Ref<InputEvent>>();
678
inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN));
679
default_builtin_cache.insert("ui_text_caret_page_down", inputs);
680
681
// Text Caret Movement Document Start/End
682
683
inputs = List<Ref<InputEvent>>();
684
inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL));
685
default_builtin_cache.insert("ui_text_caret_document_start", inputs);
686
687
inputs = List<Ref<InputEvent>>();
688
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL));
689
inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL));
690
default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs);
691
692
inputs = List<Ref<InputEvent>>();
693
inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL));
694
default_builtin_cache.insert("ui_text_caret_document_end", inputs);
695
696
inputs = List<Ref<InputEvent>>();
697
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
698
inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL));
699
default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);
700
701
// Text Caret Addition Below/Above
702
703
inputs = List<Ref<InputEvent>>();
704
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
705
default_builtin_cache.insert("ui_text_caret_add_below", inputs);
706
707
inputs = List<Ref<InputEvent>>();
708
inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
709
default_builtin_cache.insert("ui_text_caret_add_below.macos", inputs);
710
711
inputs = List<Ref<InputEvent>>();
712
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
713
default_builtin_cache.insert("ui_text_caret_add_above", inputs);
714
715
inputs = List<Ref<InputEvent>>();
716
inputs.push_back(InputEventKey::create_reference(Key::O | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
717
default_builtin_cache.insert("ui_text_caret_add_above.macos", inputs);
718
719
// Text Scrolling
720
721
inputs = List<Ref<InputEvent>>();
722
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL));
723
default_builtin_cache.insert("ui_text_scroll_up", inputs);
724
725
inputs = List<Ref<InputEvent>>();
726
inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
727
default_builtin_cache.insert("ui_text_scroll_up.macos", inputs);
728
729
inputs = List<Ref<InputEvent>>();
730
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
731
default_builtin_cache.insert("ui_text_scroll_down", inputs);
732
733
inputs = List<Ref<InputEvent>>();
734
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
735
default_builtin_cache.insert("ui_text_scroll_down.macos", inputs);
736
737
// Text Misc
738
739
inputs = List<Ref<InputEvent>>();
740
inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD_OR_CTRL));
741
default_builtin_cache.insert("ui_text_select_all", inputs);
742
743
inputs = List<Ref<InputEvent>>();
744
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT));
745
default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);
746
747
inputs = List<Ref<InputEvent>>();
748
inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::CTRL | KeyModifierMask::META));
749
default_builtin_cache.insert("ui_text_select_word_under_caret.macos", inputs);
750
751
inputs = List<Ref<InputEvent>>();
752
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
753
default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs);
754
755
inputs = List<Ref<InputEvent>>();
756
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT));
757
default_builtin_cache.insert("ui_text_skip_selection_for_next_occurrence", inputs);
758
759
inputs = List<Ref<InputEvent>>();
760
inputs.push_back(InputEventKey::create_reference(Key::ESCAPE));
761
default_builtin_cache.insert("ui_text_clear_carets_and_selection", inputs);
762
763
inputs = List<Ref<InputEvent>>();
764
inputs.push_back(InputEventKey::create_reference(Key::INSERT));
765
default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);
766
767
inputs = List<Ref<InputEvent>>();
768
inputs.push_back(InputEventKey::create_reference(Key::MENU));
769
default_builtin_cache.insert("ui_menu", inputs);
770
771
inputs = List<Ref<InputEvent>>();
772
inputs.push_back(InputEventKey::create_reference(Key::ENTER));
773
inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER));
774
default_builtin_cache.insert("ui_text_submit", inputs);
775
776
inputs = List<Ref<InputEvent>>();
777
inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT));
778
default_builtin_cache.insert("ui_unicode_start", inputs);
779
780
// ///// UI Graph Shortcuts /////
781
782
inputs = List<Ref<InputEvent>>();
783
inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
784
default_builtin_cache.insert("ui_graph_duplicate", inputs);
785
786
inputs = List<Ref<InputEvent>>();
787
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
788
default_builtin_cache.insert("ui_graph_delete", inputs);
789
790
inputs = List<Ref<InputEvent>>();
791
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
792
default_builtin_cache.insert("ui_graph_follow_left", inputs);
793
794
inputs = List<Ref<InputEvent>>();
795
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT));
796
default_builtin_cache.insert("ui_graph_follow_left.macos", inputs);
797
798
inputs = List<Ref<InputEvent>>();
799
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
800
default_builtin_cache.insert("ui_graph_follow_right", inputs);
801
802
inputs = List<Ref<InputEvent>>();
803
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT));
804
default_builtin_cache.insert("ui_graph_follow_right.macos", inputs);
805
806
// ///// UI File Dialog Shortcuts /////
807
inputs = List<Ref<InputEvent>>();
808
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));
809
default_builtin_cache.insert("ui_filedialog_up_one_level", inputs);
810
811
inputs = List<Ref<InputEvent>>();
812
inputs.push_back(InputEventKey::create_reference(Key::F5));
813
default_builtin_cache.insert("ui_filedialog_refresh", inputs);
814
815
inputs = List<Ref<InputEvent>>();
816
inputs.push_back(InputEventKey::create_reference(Key::H));
817
default_builtin_cache.insert("ui_filedialog_show_hidden", inputs);
818
819
inputs = List<Ref<InputEvent>>();
820
inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD_OR_CTRL));
821
default_builtin_cache.insert("ui_swap_input_direction", inputs);
822
823
// ///// UI ColorPicker Shortcuts /////
824
inputs = List<Ref<InputEvent>>();
825
inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::X));
826
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
827
default_builtin_cache.insert("ui_colorpicker_delete_preset", inputs);
828
829
return default_builtin_cache;
830
}
831
832
const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() {
833
if (default_builtin_with_overrides_cache.size() > 0) {
834
return default_builtin_with_overrides_cache;
835
}
836
837
const HashMap<String, List<Ref<InputEvent>>> &builtins = get_builtins();
838
839
// Get a list of all built in inputs which are valid overrides for the OS
840
// Key = builtin name (e.g. ui_accept)
841
// Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature)
842
HashMap<String, Vector<String>> builtins_with_overrides;
843
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
844
String fullname = E.key;
845
846
Vector<String> split = fullname.split(".");
847
const String &name = split[0];
848
String override_for = split.size() > 1 ? split[1] : String();
849
850
if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) {
851
builtins_with_overrides[name].push_back(override_for);
852
}
853
}
854
855
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
856
String fullname = E.key;
857
858
Vector<String> split = fullname.split(".");
859
const String &name = split[0];
860
String override_for = split.size() > 1 ? split[1] : String();
861
862
if (builtins_with_overrides.has(name) && override_for.is_empty()) {
863
// Builtin has an override but this particular one is not an override, so skip.
864
continue;
865
}
866
867
if (!override_for.is_empty() && !OS::get_singleton()->has_feature(override_for)) {
868
// OS does not support this override - skip.
869
continue;
870
}
871
872
default_builtin_with_overrides_cache.insert(name, E.value);
873
}
874
875
return default_builtin_with_overrides_cache;
876
}
877
878
void InputMap::load_default() {
879
HashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied();
880
881
for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) {
882
String name = E.key;
883
884
add_action(name);
885
886
const List<Ref<InputEvent>> &inputs = E.value;
887
for (const List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) {
888
Ref<InputEventKey> iek = I->get();
889
890
// For the editor, only add keyboard actions.
891
if (iek.is_valid()) {
892
action_add_event(name, I->get());
893
}
894
}
895
}
896
}
897
898
InputMap::InputMap() {
899
ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exists.");
900
singleton = this;
901
}
902
903
InputMap::~InputMap() {
904
singleton = nullptr;
905
}
906
907