summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/@GlobalScope.xml2
-rw-r--r--editor/connections_dialog.cpp83
-rw-r--r--editor/connections_dialog.h3
-rw-r--r--editor/editor_inspector.h8
-rw-r--r--editor/editor_properties.cpp3
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml15
-rw-r--r--modules/gdscript/gdscript_parser.cpp24
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_enum.gd15
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_enum.out7
9 files changed, 109 insertions, 51 deletions
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 485c04da6d..0d6524ccbe 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2647,7 +2647,7 @@
Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians"[/code] for editing radian angles in degrees, [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider.
</constant>
<constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint">
- Hints that an [int], [float], or [String] property is an enumerated value to pick in a list specified via a hint string.
+ Hints that an [int] or [String] property is an enumerated value to pick in a list specified via a hint string.
The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code].
</constant>
<constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="3" enum="PropertyHint">
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 99d4413e07..aaa07e98c8 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -186,7 +186,7 @@ void ConnectDialog::_unbind_count_changed(double p_count) {
void ConnectDialog::_method_selected() {
TreeItem *selected_item = method_tree->get_selected();
- dst_method->set_text(selected_item->get_text(0));
+ dst_method->set_text(selected_item->get_metadata(0));
}
/*
@@ -260,12 +260,8 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p
void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) {
for (const MethodInfo &mi : p_methods) {
TreeItem *method_item = method_tree->create_item(p_parent_item);
- method_item->set_text(0, mi.name);
- if (mi.return_val.type == Variant::NIL) {
- method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons"));
- } else {
- method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons"));
- }
+ method_item->set_text(0, get_signature(mi));
+ method_item->set_metadata(0, mi.name);
}
}
@@ -293,6 +289,11 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
type_mismatch = true;
break;
}
+
+ if (stype == Variant::OBJECT && mtype == Variant::OBJECT && E->get().class_name != F->get().class_name) {
+ type_mismatch = true;
+ break;
+ }
}
if (type_mismatch) {
@@ -488,6 +489,34 @@ Vector<Variant> ConnectDialog::get_binds() const {
return cdbinds->params;
}
+String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArray *r_arg_names) {
+ PackedStringArray signature;
+ signature.append(p_method.name);
+ signature.append("(");
+
+ for (int i = 0; i < p_method.arguments.size(); i++) {
+ if (i > 0) {
+ signature.append(", ");
+ }
+
+ const PropertyInfo &pi = p_method.arguments[i];
+ String tname = "var";
+ if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
+ tname = pi.class_name.operator String();
+ } else if (pi.type != Variant::NIL) {
+ tname = Variant::get_type_name(pi.type);
+ }
+
+ signature.append((pi.name.is_empty() ? String("arg " + itos(i)) : pi.name) + ": " + tname);
+ if (r_arg_names) {
+ r_arg_names->push_back(pi.name + ":" + tname);
+ }
+ }
+
+ signature.append(")");
+ return String().join(signature);
+}
+
bool ConnectDialog::get_deferred() const {
return deferred->is_pressed();
}
@@ -545,7 +574,7 @@ void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_
source_connection_data = p_cd;
}
-void ConnectDialog::popup_dialog(const String &p_for_signal) {
+void ConnectDialog::popup_dialog(const String p_for_signal) {
from_signal->set_text(p_for_signal);
error_label->add_theme_color_override("font_color", error_label->get_theme_color(SNAME("error_color"), SNAME("Editor")));
if (!advanced->is_pressed()) {
@@ -972,8 +1001,6 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
String signal_name = sinfo["name"];
PackedStringArray signal_args = sinfo["args"];
- const String &signal_name_ref = signal_name;
-
Node *dst_node = selected_node->get_owner() ? selected_node->get_owner() : selected_node;
if (!dst_node || dst_node->get_script().is_null()) {
dst_node = _find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root());
@@ -981,10 +1008,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
ConnectDialog::ConnectionData cd;
cd.source = selected_node;
- cd.signal = StringName(signal_name_ref);
+ cd.signal = StringName(signal_name);
cd.target = dst_node;
cd.method = ConnectDialog::generate_method_callback_name(cd.source, signal_name, cd.target);
- connect_dialog->popup_dialog(signal_name_ref);
+ connect_dialog->popup_dialog(signal_name + "(" + String(", ").join(signal_args) + ")");
connect_dialog->init(cd, signal_args);
connect_dialog->set_title(TTR("Connect a Signal to a Method"));
}
@@ -1235,37 +1262,15 @@ void ConnectionsDock::update_tree() {
}
for (MethodInfo &mi : node_signals2) {
- StringName signal_name = mi.name;
- String signaldesc = "(";
- PackedStringArray argnames;
-
- String filter_text = search_box->get_text();
- if (!filter_text.is_subsequence_ofn(signal_name)) {
+ const StringName signal_name = mi.name;
+ if (!search_box->get_text().is_subsequence_ofn(signal_name)) {
continue;
}
-
- if (mi.arguments.size()) {
- for (int i = 0; i < mi.arguments.size(); i++) {
- PropertyInfo &pi = mi.arguments[i];
-
- if (i > 0) {
- signaldesc += ", ";
- }
- String tname = "var";
- if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
- tname = pi.class_name.operator String();
- } else if (pi.type != Variant::NIL) {
- tname = Variant::get_type_name(pi.type);
- }
- signaldesc += (pi.name.is_empty() ? String("arg " + itos(i)) : pi.name) + ": " + tname;
- argnames.push_back(pi.name + ":" + tname);
- }
- }
- signaldesc += ")";
+ PackedStringArray argnames;
// Create the children of the subsection - the actual list of signals.
TreeItem *signal_item = tree->create_item(section_item);
- String signame = String(signal_name) + signaldesc;
+ String signame = connect_dialog->get_signature(mi, &argnames);
signal_item->set_text(0, signame);
if (signame == prev_selected) {
@@ -1313,7 +1318,7 @@ void ConnectionsDock::update_tree() {
}
// "::" separators used in make_custom_tooltip for formatting.
- signal_item->set_tooltip_text(0, String(signal_name) + "::" + signaldesc + "::" + descr);
+ signal_item->set_tooltip_text(0, String(signal_name) + "::" + signame.trim_prefix(mi.name) + "::" + descr);
}
// List existing connections.
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index f70970996d..277ea03cf7 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -172,6 +172,7 @@ public:
void set_dst_method(const StringName &p_method);
int get_unbinds() const;
Vector<Variant> get_binds() const;
+ String get_signature(const MethodInfo &p_method, PackedStringArray *r_arg_names = nullptr);
bool get_deferred() const;
bool get_one_shot() const;
@@ -179,7 +180,7 @@ public:
void init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit = false);
- void popup_dialog(const String &p_for_signal);
+ void popup_dialog(const String p_for_signal);
ConnectDialog();
~ConnectDialog();
};
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 1690302e6e..6a1c77d376 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -47,11 +47,7 @@ class TextureRect;
class EditorPropertyRevert {
public:
- static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true);
- static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig);
- static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
static Variant get_property_revert_value(Object *p_object, const StringName &p_property, bool *r_is_valid);
-
static bool can_property_revert(Object *p_object, const StringName &p_property, const Variant *p_custom_current_value = nullptr);
};
@@ -331,7 +327,7 @@ class EditorInspectorArray : public EditorInspectorSection {
AcceptDialog *resize_dialog = nullptr;
SpinBox *new_size_spin_box = nullptr;
- // Pagination
+ // Pagination.
int page_length = 5;
int page = 0;
int max_page = 0;
@@ -495,7 +491,7 @@ class EditorInspector : public ScrollContainer {
HashMap<ObjectID, int> scroll_cache;
- String property_prefix; //used for sectioned inspector
+ String property_prefix; // Used for sectioned inspector.
String object_class;
Variant property_clipboard;
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 152e77acb7..f022027e65 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1323,10 +1323,12 @@ void EditorPropertyObjectID::update_property() {
ObjectID id = get_edited_object()->get(get_edited_property());
if (id.is_valid()) {
edit->set_text(type + " ID: " + uitos(id));
+ edit->set_tooltip_text(type + " ID: " + uitos(id));
edit->set_disabled(false);
edit->set_icon(EditorNode::get_singleton()->get_class_icon(type));
} else {
edit->set_text(TTR("<empty>"));
+ edit->set_tooltip_text("");
edit->set_disabled(true);
edit->set_icon(Ref<Texture2D>());
}
@@ -1343,6 +1345,7 @@ EditorPropertyObjectID::EditorPropertyObjectID() {
edit = memnew(Button);
add_child(edit);
add_focusable(edit);
+ edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
edit->connect("pressed", callable_mp(this, &EditorPropertyObjectID::_edit_pressed));
}
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 5bed1b9da3..7f9d4ae253 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -316,12 +316,21 @@
<return type="void" />
<param index="0" name="names" type="String" />
<description>
- Export a [String] or integer property as an enumerated list of options. If the property is an integer field, then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon.
+ Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon. If the property is a [String], then the value is stored.
See also [constant PROPERTY_HINT_ENUM].
[codeblock]
- @export_enum("Rebecca", "Mary", "Leah") var character_name: String
@export_enum("Warrior", "Magician", "Thief") var character_class: int
@export_enum("Slow:30", "Average:60", "Very Fast:200") var character_speed: int
+ @export_enum("Rebecca", "Mary", "Leah") var character_name: String
+ [/codeblock]
+ If you want to set an initial value, you must specify it explicitly:
+ [codeblock]
+ @export_enum("Rebecca", "Mary", "Leah") var character_name: String = "Rebecca"
+ [/codeblock]
+ If you want to use named GDScript enums, then use [annotation @export] instead:
+ [codeblock]
+ enum CharacterName {REBECCA, MARY, LEAH}
+ @export var character_name: CharacterName
[/codeblock]
</description>
</annotation>
@@ -507,7 +516,7 @@
<param index="2" name="step" type="float" default="1.0" />
<param index="3" name="extra_hints" type="String" default="&quot;&quot;" />
<description>
- Export a numeric property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting.
+ Export an [int] or [float] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting.
If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget.
Hints also allow to indicate the units for the edited value. Using [code]"radians"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock. [code]"degrees"[/code] allows to add a degree sign as a unit suffix. Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string.
See also [constant PROPERTY_HINT_RANGE].
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 267718207f..06c66b155f 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -122,7 +122,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, varray(), true);
+ register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
@@ -3662,6 +3662,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
String hint_string;
for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
+ if (p_annotation->name != SNAME("@export_placeholder") && String(p_annotation->resolved_arguments[i]).contains(",")) {
+ push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
+ return false;
+ }
if (i > 0) {
hint_string += ",";
}
@@ -3806,6 +3810,24 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
variable->export_info.type = Variant::ARRAY;
}
+ } else if (p_annotation->name == SNAME("@export_enum")) {
+ Variant::Type enum_type = Variant::INT;
+
+ if (export_type.kind == DataType::BUILTIN && export_type.builtin_type == Variant::STRING) {
+ enum_type = Variant::STRING;
+ } else if (export_type.is_variant() && variable->initializer != nullptr) {
+ DataType initializer_type = variable->initializer->get_datatype();
+ if (initializer_type.kind == DataType::BUILTIN && initializer_type.builtin_type == Variant::STRING) {
+ enum_type = Variant::STRING;
+ }
+ }
+
+ variable->export_info.type = enum_type;
+
+ if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != enum_type)) {
+ push_error(vformat(R"("@export_enum" annotation requires a variable of type "int" or "String" but type "%s" was given instead.)", export_type.to_string()), variable);
+ return false;
+ }
} else {
// Validate variable type with export.
if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) {
diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.gd b/modules/gdscript/tests/scripts/parser/features/export_enum.gd
new file mode 100644
index 0000000000..9b2c22dea1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/export_enum.gd
@@ -0,0 +1,15 @@
+@export_enum("Red", "Green", "Blue") var untyped
+
+@export_enum("Red", "Green", "Blue") var weak_int = 0
+@export_enum("Red", "Green", "Blue") var weak_string = ""
+
+@export_enum("Red", "Green", "Blue") var hard_int: int
+@export_enum("Red", "Green", "Blue") var hard_string: String
+
+@export_enum("Red:10", "Green:20", "Blue:30") var with_values
+
+func test():
+ for property in get_property_list():
+ if property.name in ["untyped", "weak_int", "weak_string", "hard_int",
+ "hard_string", "with_values"]:
+ prints(property.name, property.type, property.hint_string)
diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.out b/modules/gdscript/tests/scripts/parser/features/export_enum.out
new file mode 100644
index 0000000000..330b7eaf01
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/export_enum.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+untyped 2 Red,Green,Blue
+weak_int 2 Red,Green,Blue
+weak_string 4 Red,Green,Blue
+hard_int 2 Red,Green,Blue
+hard_string 4 Red,Green,Blue
+with_values 2 Red:10,Green:20,Blue:30