Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDScript: Add array support to @export_enum annotation #72951

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2676,7 +2676,7 @@
<constant name="PROPERTY_HINT_FLAGS" value="6" enum="PropertyHint">
Hints that an [int] property is a bitmask with named bit flags.
The hint string is a comma separated list of names such as [code]"Bit0,Bit1,Bit2,Bit3"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. The first name in the list has value 1, the next 2, then 4, 8, 16 and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"A:4,B:8,C:16"[/code]. You can also combine several flags ([code]"A:4,B:8,AB:12,C:16"[/code]).
[b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code].
[b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code]. This means you can use up to 32 different bit flags.
[b]Note:[/b] Unlike [constant PROPERTY_HINT_ENUM], the previous explicit value is not taken into account. For the hint [code]"A:16,B,C"[/code], A is 16, B is 2, C is 4.
</constant>
<constant name="PROPERTY_HINT_LAYERS_2D_RENDER" value="7" enum="PropertyHint">
Expand Down Expand Up @@ -2772,6 +2772,7 @@
hint_string = "%d:%d/%d:Texture2D" % [TYPE_ARRAY, TYPE_OBJECT, PROPERTY_HINT_RESOURCE_TYPE] # Two-dimensional array of textures.
[/gdscript]
[csharp]
hintString = $"{Variant.Type.Int:D}:"; // Array of integers.
hintString = $"{Variant.Type.Int:D}/{PropertyHint.Range:D}:1,10,1"; // Array of integers (in range from 1 to 10).
hintString = $"{Variant.Type.Int:D}/{PropertyHint.Enum:D}:Zero,One,Two"; // Array of integers (an enum).
hintString = $"{Variant.Type.Int:D}/{PropertyHint.Enum:D}:Zero,One,Three:3,Six:6"; // Array of integers (an enum).
Expand Down
15 changes: 12 additions & 3 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,17 @@
<return type="void" />
<param index="0" name="names" type="String" />
<description>
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 explicit values using a colon. If the property is a [String], then the value is stored.
See also [constant PROPERTY_HINT_ENUM].
Export an [int], [String], [StringName] or [Array] property as an enumerated list of options (or an array of options). If the property is an [int], then the value is stored. By default it's annotation argument index, starting from 0. You can specify explicit values using a colon. If the property is a [String] or [StringName], then the key is stored. If the property is an [Array] then the array of keys or values is stored (the array type must be [code]Array[/code], [code]Array[int][/code], [code]Array[String] or [code]Array[StringName][/code]).
See also [constant PROPERTY_HINT_ENUM] and [constant PROPERTY_HINT_TYPE_STRING].
[codeblock]
@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
@export_enum("red", "blue") var character_team: StringName

@export_enum("Sword", "Spear", "Mace") var character_items: Array[int]
@export_enum("double_jump", "climb", "dash") var character_skills: Array[String]
@export_enum("fast", "nimble", "strong") var character_traits: Array[StringName]
[/codeblock]
If you want to set an initial value, you must specify it explicitly:
[codeblock]
Expand All @@ -344,7 +349,11 @@
[codeblock]
enum CharacterName {REBECCA, MARY, LEAH}
@export var character_name: CharacterName

enum CharacterItem {SWORD, SPEAR, MACE}
@export var character_items: Array[CharacterItem]
[/codeblock]
[b]Note:[/b] If the variable is untyped and uninitialized, the value will be stored as an integer. If the variable's type or initializer is an untyped array, then the value will be stored as an array of integers, regardless of the contents of the initializer array.
</description>
</annotation>
<annotation name="@export_exp_easing" qualifiers="vararg">
Expand Down Expand Up @@ -391,7 +400,7 @@
@export_flags("Self:4", "Allies:8", "Self and Allies:12", "Foes:16")
var spell_targets = 0
[/codeblock]
[b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code].
[b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code]. This means you can use up to 32 different bit flags.
[b]Note:[/b] Unlike [annotation @export_enum], the previous explicit value is not taken into account. In the following example, A is 16, B is 2, C is 4.
[codeblock]
@export_flags("A:16", "B", "C") var x
Expand Down
77 changes: 69 additions & 8 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4058,21 +4058,82 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
} 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;
bool has_error = false;

if (export_type.kind == DataType::BUILTIN) {
switch (export_type.builtin_type) {
case Variant::INT:
case Variant::STRING:
case Variant::STRING_NAME:
case Variant::ARRAY:
enum_type = export_type.builtin_type;
break;
default:
has_error = true;
break;
}
} 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;
if (initializer_type.kind == DataType::BUILTIN) {
switch (initializer_type.builtin_type) {
case Variant::INT:
case Variant::STRING:
case Variant::STRING_NAME:
case Variant::ARRAY:
enum_type = initializer_type.builtin_type;
break;
default:
break; // No error since it's variant.
}
}
} else if (!export_type.is_variant()) {
has_error = true;
}

if (has_error) {
push_error(vformat(R"("@export_enum" annotation requires a variable of type "int", "String", "StringName" or "Array" but type "%s" was given instead.)", export_type.to_string()), variable);
return false;
}

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;
if (enum_type == Variant::ARRAY) {
Variant::Type enum_element_type = Variant::INT;

if (export_type.has_container_element_type()) {
DataType element_type = export_type.get_container_element_type();
switch (element_type.kind) {
case DataType::VARIANT:
break; // `Array[Variant]` is `Array`.
case DataType::BUILTIN:
switch (element_type.builtin_type) {
case Variant::INT:
case Variant::STRING:
case Variant::STRING_NAME:
enum_element_type = element_type.builtin_type;
break;
default:
has_error = true;
break;
}
break;
default:
has_error = true;
break;
}
}

if (has_error) {
push_error(vformat(R"("@export_enum": The array type must be "Array", "Array[int]", "Array[String]" or "Array[StringName]" but type "%s" was given instead.)", export_type.to_string()), variable);
return false;
}

String hint_prefix = itos(enum_element_type);
if (variable->export_info.hint) {
hint_prefix += "/" + itos(variable->export_info.hint);
}
variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
}
} else {
// Validate variable type with export.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@export_enum("A", "B", "C") var x: Array[Color]

func test():
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
"@export_enum": The array type must be "Array", "Array[int]", "Array[String]" or "Array[StringName]" but type "Array[Color]" was given instead.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@export_enum("A", "B", "C") var x: Color

func test():
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
"@export_enum" annotation requires a variable of type "int", "String", "StringName" or "Array" but type "Color" was given instead.
26 changes: 21 additions & 5 deletions modules/gdscript/tests/scripts/parser/features/export_enum.gd
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
@export_enum("Red", "Green", "Blue") var untyped
@export_enum("Red:10", "Green:20", "Blue:30") var with_values

var temp_array_variant: Array[Variant]
var temp_array_int: Array[int]
var temp_array_string: Array[String]
var temp_array_string_name: Array[StringName]

@export_enum("Red", "Green", "Blue") var weak_variant
@export_enum("Red", "Green", "Blue") var weak_int = 0
@export_enum("Red", "Green", "Blue") var weak_string = ""
@export_enum("Red", "Green", "Blue") var weak_string_name = &""
@export_enum("Red", "Green", "Blue") var weak_array = []
@export_enum("Red", "Green", "Blue") var weak_array_variant = temp_array_variant
@export_enum("Red", "Green", "Blue") var weak_array_int = temp_array_int
@export_enum("Red", "Green", "Blue") var weak_array_string = temp_array_string
@export_enum("Red", "Green", "Blue") var weak_array_string_name = temp_array_string_name

@export_enum("Red", "Green", "Blue") var hard_variant: Variant
@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
@export_enum("Red", "Green", "Blue") var hard_string_name: StringName
@export_enum("Red", "Green", "Blue") var hard_array: Array
@export_enum("Red", "Green", "Blue") var hard_array_variant: Array[Variant]
@export_enum("Red", "Green", "Blue") var hard_array_int: Array[int]
@export_enum("Red", "Green", "Blue") var hard_array_string: Array[String]
@export_enum("Red", "Green", "Blue") var hard_array_string_name: Array[StringName]

func test():
for property in get_property_list():
if property.name in ["untyped", "weak_int", "weak_string", "hard_int",
"hard_string", "with_values"]:
if property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE and not str(property.name).begins_with("temp_"):
prints(property.name, property.type, property.hint_string)
17 changes: 15 additions & 2 deletions modules/gdscript/tests/scripts/parser/features/export_enum.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
GDTEST_OK
untyped 2 Red,Green,Blue
with_values 2 Red:10,Green:20,Blue:30
weak_variant 2 Red,Green,Blue
weak_int 2 Red,Green,Blue
weak_string 4 Red,Green,Blue
weak_string_name 21 Red,Green,Blue
weak_array 28 2/2:Red,Green,Blue
weak_array_variant 28 2/2:Red,Green,Blue
weak_array_int 28 2/2:Red,Green,Blue
weak_array_string 28 4/2:Red,Green,Blue
weak_array_string_name 28 21/2:Red,Green,Blue
hard_variant 2 Red,Green,Blue
hard_int 2 Red,Green,Blue
hard_string 4 Red,Green,Blue
with_values 2 Red:10,Green:20,Blue:30
hard_string_name 21 Red,Green,Blue
hard_array 28 2/2:Red,Green,Blue
hard_array_variant 28 2/2:Red,Green,Blue
hard_array_int 28 2/2:Red,Green,Blue
hard_array_string 28 4/2:Red,Green,Blue
hard_array_string_name 28 21/2:Red,Green,Blue