summaryrefslogtreecommitdiffstats
path: root/core/input/input_map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/input/input_map.cpp')
-rw-r--r--core/input/input_map.cpp93
1 files changed, 67 insertions, 26 deletions
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 7d85fd6492..83ec70757e 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -45,6 +45,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
+ ClassDB::bind_method(D_METHOD("action_get_deadzone", "action"), &InputMap::action_get_deadzone);
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
@@ -54,8 +55,36 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings);
}
+/**
+ * Returns an nonexistent action error message with a suggestion of the closest
+ * matching action name (if possible).
+ */
+String InputMap::_suggest_actions(const StringName &p_action) const {
+ List<StringName> actions = get_actions();
+ StringName closest_action;
+ float closest_similarity = 0.0;
+
+ // Find the most action with the most similar name.
+ for (const StringName &action : actions) {
+ const float similarity = String(action).similarity(p_action);
+
+ if (similarity > closest_similarity) {
+ closest_action = action;
+ closest_similarity = similarity;
+ }
+ }
+
+ String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action);
+
+ if (closest_similarity >= 0.4) {
+ // Only include a suggestion in the error message if it's similar enough.
+ error_message += vformat(" Did you mean \"%s\"?", closest_action);
+ }
+ return error_message;
+}
+
void InputMap::add_action(const StringName &p_action, float p_deadzone) {
- ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\".");
input_map[p_action] = Action();
static int last_id = 1;
input_map[p_action].id = last_id;
@@ -64,7 +93,8 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) {
}
void InputMap::erase_action(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
+
input_map.erase(p_action);
}
@@ -75,8 +105,8 @@ Array InputMap::_get_actions() {
return ret;
}
- for (const List<StringName>::Element *E = actions.front(); E; E = E->next()) {
- ret.push_back(E->get());
+ for (const StringName &E : actions) {
+ ret.push_back(E);
}
return ret;
@@ -99,16 +129,11 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
- const Ref<InputEvent> e = E->get();
-
- //if (e.type != Ref<InputEvent>::KEY && e.device != p_event.device) -- unsure about the KEY comparison, why is this here?
- // continue;
-
- int device = e->get_device();
+ int device = E->get()->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
- if (p_exact_match && e->shortcut_match(p_event)) {
+ if (p_exact_match && E->get()->is_match(p_event, true)) {
return E;
- } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
+ } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
return E;
}
}
@@ -122,34 +147,34 @@ bool InputMap::has_action(const StringName &p_action) const {
}
float InputMap::action_get_deadzone(const StringName &p_action) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, _suggest_actions(p_action));
return input_map[p_action].deadzone;
}
void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
input_map[p_action].deadzone = p_deadzone;
}
void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object.");
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
if (_find_event(input_map[p_action], p_event, true)) {
- return; // Already addded.
+ return; // Already added.
}
input_map[p_action].inputs.push_back(p_event);
}
bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action));
return (_find_event(input_map[p_action], p_event, true) != nullptr);
}
void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
if (E) {
@@ -161,7 +186,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
}
void InputMap::action_erase_events(const StringName &p_action) {
- ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action));
input_map[p_action].inputs.clear();
}
@@ -193,15 +218,16 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
- ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'.");
+ ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action));
Ref<InputEventAction> input_event_action = p_event;
if (input_event_action.is_valid()) {
+ bool pressed = input_event_action->is_pressed();
if (p_pressed != nullptr) {
- *p_pressed = input_event_action->is_pressed();
+ *p_pressed = pressed;
}
if (p_strength != nullptr) {
- *p_strength = (p_pressed != nullptr && *p_pressed) ? input_event_action->get_strength() : 0.0f;
+ *p_strength = pressed ? input_event_action->get_strength() : 0.0f;
}
return input_event_action->get_action() == p_action;
}
@@ -236,9 +262,7 @@ void InputMap::load_from_project_settings() {
List<PropertyInfo> pinfo;
ProjectSettings::get_singleton()->get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- const PropertyInfo &pi = E->get();
-
+ for (const PropertyInfo &pi : pinfo) {
if (!pi.name.begins_with("input/")) {
continue;
}
@@ -324,7 +348,9 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_text_scroll_down", TTRC("Scroll Down") },
{ "ui_text_scroll_down.OSX", TTRC("Scroll Down") },
{ "ui_text_select_all", TTRC("Select All") },
+ { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
+ { "ui_text_submit", TTRC("Text Submitted") },
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
{ "ui_graph_delete", TTRC("Delete Nodes") },
{ "ui_filedialog_up_one_level", TTRC("Go Up One Level") },
@@ -444,9 +470,14 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
default_builtin_cache.insert("ui_text_completion_query", inputs);
inputs = List<Ref<InputEvent>>();
- inputs.push_back(InputEventKey::create_reference(KEY_TAB));
+ inputs.push_back(InputEventKey::create_reference(KEY_ENTER));
+ inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER));
default_builtin_cache.insert("ui_text_completion_accept", inputs);
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(KEY_TAB));
+ default_builtin_cache.insert("ui_text_completion_replace", inputs);
+
// Newlines
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_ENTER));
@@ -476,6 +507,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
// Text Backspace and Delete
inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE));
+ inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_SHIFT));
default_builtin_cache.insert("ui_text_backspace", inputs);
inputs = List<Ref<InputEvent>>();
@@ -621,6 +653,10 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
default_builtin_cache.insert("ui_text_select_all", inputs);
inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(KEY_D | KEY_MASK_CMD));
+ default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);
+
+ inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(KEY_INSERT));
default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);
@@ -628,6 +664,11 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(KEY_MENU));
default_builtin_cache.insert("ui_menu", inputs);
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(KEY_ENTER));
+ inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER));
+ default_builtin_cache.insert("ui_text_submit", inputs);
+
// ///// UI Graph Shortcuts /////
inputs = List<Ref<InputEvent>>();