diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 7fff2e4af2d..07d8946236a 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -679,6 +679,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ONESHOT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GROUP_ENABLE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INPUT_NAME);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 85c35c05b3a..c1f74fec97b 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -1668,7 +1668,7 @@ void InputEventAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_event_index", "index"), &InputEventAction::set_event_index);
ClassDB::bind_method(D_METHOD("get_event_index"), &InputEventAction::get_event_index);
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action"), "set_action", "get_action");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action", PROPERTY_HINT_INPUT_NAME, "show_builtin,loose_mode"), "set_action", "get_action");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_strength", "get_strength");
ADD_PROPERTY(PropertyInfo(Variant::INT, "event_index", PROPERTY_HINT_RANGE, "-1,31,1"), "set_event_index", "get_event_index"); // The max value equals to Input::MAX_EVENT - 1.
diff --git a/core/object/object.h b/core/object/object.h
index c53fd8458bb..dc84b97ec91 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -94,6 +94,7 @@ enum PropertyHint {
PROPERTY_HINT_ONESHOT, ///< the property will be changed by self after setting, such as AudioStreamPlayer.playing, Particles.emitting.
PROPERTY_HINT_NO_NODEPATH, /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock.
PROPERTY_HINT_GROUP_ENABLE, ///< used to make the property's group checkable. Only use for boolean types.
+ PROPERTY_HINT_INPUT_NAME,
PROPERTY_HINT_MAX,
};
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 43f51c2c2d1..28505d6cc43 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2957,7 +2957,12 @@
Hints that a boolean property will enable the feature associated with the group that it occurs in. Only works within a group or subgroup.
-
+
+ Hints that a [String] or [StringName] property is the name of an input action. This allows the selection of any action name from the Input Map in the Project Settings. The hint string may contain two options separated by commas:
+ - If it contains [code]"show_builtin"[/code], built-in input actions are included in the selection.
+ - If it contains [code]"loose_mode"[/code], loose mode is enabled. This allows inserting any action name even if it's not present in the input map.
+
+
Represents the size of the [enum PropertyHint] enum.
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 68c54de2b83..66583df1c69 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -31,6 +31,7 @@
#include "editor_properties.h"
#include "core/config/project_settings.h"
+#include "core/input/input_map.h"
#include "editor/create_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_properties_array_dict.h"
@@ -3570,6 +3571,38 @@ static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const Stri
return hint;
}
+static EditorProperty *get_input_action_editor(const String &p_hint_text, bool is_string_name) {
+ // TODO: Should probably use a better editor GUI with a search bar.
+ // Said GUI could also handle showing builtin options, requiring 1 less hint.
+ EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
+ Vector options;
+ Vector builtin_options;
+ List pinfo;
+ ProjectSettings::get_singleton()->get_property_list(&pinfo);
+ Vector hints = p_hint_text.remove_char(' ').split(",", false);
+
+ HashMap>> builtins = InputMap::get_singleton()->get_builtins();
+ bool show_builtin = hints.has("show_builtin");
+
+ for (const PropertyInfo &pi : pinfo) {
+ if (!pi.name.begins_with("input/")) {
+ continue;
+ }
+
+ const String action_name = pi.name.get_slicec('/', 1);
+ if (builtins.has(action_name)) {
+ if (show_builtin) {
+ builtin_options.append(action_name);
+ }
+ } else {
+ options.append(action_name);
+ }
+ }
+ options.append_array(builtin_options);
+ editor->setup(options, is_string_name, hints.has("loose_mode"));
+ return editor;
+}
+
EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField p_usage, const bool p_wide) {
double default_float_step = EDITOR_GET("interface/inspector/default_float_step");
@@ -3680,6 +3713,8 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
Vector options = p_hint_text.split(",", false);
editor->setup(options, false, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));
return editor;
+ } else if (p_hint == PROPERTY_HINT_INPUT_NAME) {
+ return get_input_action_editor(p_hint_text, false);
} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
return editor;
@@ -3832,6 +3867,8 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
Vector options = p_hint_text.split(",", false);
editor->setup(options, true, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));
return editor;
+ } else if (p_hint == PROPERTY_HINT_INPUT_NAME) {
+ return get_input_action_editor(p_hint_text, true);
} else {
EditorPropertyText *editor = memnew(EditorPropertyText);
if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {
diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd
index 225bcb3008a..4e715e14eac 100644
--- a/modules/gdscript/tests/scripts/utils.notest.gd
+++ b/modules/gdscript/tests/scripts/utils.notest.gd
@@ -206,6 +206,8 @@ static func get_property_hint_name(hint: PropertyHint) -> String:
return "PROPERTY_HINT_PASSWORD"
PROPERTY_HINT_TOOL_BUTTON:
return "PROPERTY_HINT_TOOL_BUTTON"
+ PROPERTY_HINT_INPUT_NAME:
+ return "PROPERTY_HINT_INPUT_NAME"
printerr("Argument `hint` is invalid. Use `PROPERTY_HINT_*` constants.")
return ""
diff --git a/scene/2d/physics/touch_screen_button.cpp b/scene/2d/physics/touch_screen_button.cpp
index 93ff87d7590..a76a3a3fc8a 100644
--- a/scene/2d/physics/touch_screen_button.cpp
+++ b/scene/2d/physics/touch_screen_button.cpp
@@ -438,7 +438,7 @@ void TouchScreenButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), "set_shape_centered", "is_shape_centered");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_visible"), "set_shape_visible", "is_shape_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "passby_press"), "set_passby_press", "is_passby_press_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action"), "set_action", "get_action");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action", PROPERTY_HINT_INPUT_NAME, "show_builtin,loose_mode"), "set_action", "get_action");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Always,TouchScreen Only"), "set_visibility_mode", "get_visibility_mode");
ADD_SIGNAL(MethodInfo("pressed"));