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

Added support for generic type exported properties in gdscript #33080

Closed
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
Added support for generic type properties in gdscript
This change allows scripts to use untyped properties.
Their type can be changed in the inspector the same way as for
elements in Array or Dictionary.

This implementation uses properties of type TYPE_NIL with the
existing usage flag PROPERTY_USAGE_NIL_IS_VARIANT.

Syntax for property list:

{
	"name": "generic_var",
	"type": TYPE_NIL,
	"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT,
	"hint": PROPERTY_HINT_NONE,
	"hint_string" : "",
}

Syntax for exported variable:
export(Variant) var generic_export_var
  • Loading branch information
pouleyKetchoupp committed Jul 1, 2020
commit 9b3b618f56c8bc92e0a1e046313797bc768cc8cb
1 change: 1 addition & 0 deletions core/global_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NIL_IS_VARIANT);

BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL);
Expand Down
2 changes: 1 addition & 1 deletion core/script_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c
StringName n = E->get().name;
new_values.insert(n);

if (!values.has(n) || values[n].get_type() != E->get().type) {
if (!values.has(n) || (values[n].get_type() != E->get().type && !(E->get().usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
if (p_values.has(n)) {
values[n] = p_values[n];
}
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,9 @@
<constant name="PROPERTY_USAGE_SCRIPT_VARIABLE" value="8192" enum="PropertyUsageFlags">
The property is a script variable which should be serialized and saved in the scene file.
</constant>
<constant name="PROPERTY_USAGE_NIL_IS_VARIANT" value="524288" enum="PropertyUsageFlags">
The property stores a variable of type [Variant]. Used with type flag [code]TYPE_NIL[/code].
</constant>
<constant name="PROPERTY_USAGE_DEFAULT" value="7" enum="PropertyUsageFlags">
Default usage (storage, editor and network).
</constant>
Expand Down
105 changes: 103 additions & 2 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "editor_properties.h"

#include "core/io/marshalls.h"
#include "editor/editor_resource_preview.h"
#include "editor/filesystem_dock.h"
#include "editor_node.h"
Expand All @@ -48,6 +49,101 @@ EditorPropertyNil::EditorPropertyNil() {
add_child(label);
}

///////////////////// GENERIC /////////////////////////

void EditorPropertyGeneric::update_property() {
Object *object = get_edited_object();
StringName edited_prop = get_edited_property();
Variant value = object->get(edited_prop);
Variant::Type value_type = value.get_type();

// remove previous controls
while (hbox->get_child_count() > 0) {
Node *child = hbox->get_child(0);
child->queue_delete();
hbox->remove_child(child);
}

EditorProperty *prop = nullptr;
if (value_type == Variant::NIL) {
prop = memnew(EditorPropertyNil);
} else if (value_type == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(value)) {
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
editor->setup("Object");
prop = editor;
} else {
prop = EditorInspector::instantiate_property_editor(nullptr, value_type, "", PROPERTY_HINT_NONE, "", 0);
}

prop->set_object_and_property(object, edited_prop);
prop->set_name_split_ratio(0.0);
prop->set_selectable(false);
prop->connect("property_changed", callable_mp(this, &EditorPropertyGeneric::_property_changed));
prop->connect("object_id_selected", callable_mp(this, &EditorPropertyGeneric::_object_id_selected));
prop->set_h_size_flags(SIZE_EXPAND_FILL);

hbox->add_child(prop);

Button *edit_button = memnew(Button);
edit_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
edit_button->connect("pressed", callable_mp(this, &EditorPropertyGeneric::_change_type), varray(edit_button));
hbox->add_child(edit_button);

prop->update_property();
}

void EditorPropertyGeneric::_property_changed(const String &p_prop, Variant p_value, const String &p_name, bool changing) {
emit_changed(get_edited_property(), p_value, p_name, changing);
}

void EditorPropertyGeneric::_object_id_selected(const String &p_property, ObjectID p_id) {
emit_signal("object_id_selected", p_property, p_id);
}

void EditorPropertyGeneric::_change_type(Object *p_button) {
Button *button = Object::cast_to<Button>(p_button);
Rect2 rect = button->get_global_rect();
change_type->set_as_minsize();
change_type->set_position(rect.position + rect.size - Vector2(change_type->get_contents_minimum_size().x, 0));
change_type->popup();
}

void EditorPropertyGeneric::_change_type_menu(int p_index) {
Object *object = get_edited_object();
StringName edited_prop = get_edited_property();
Variant value = object->get(edited_prop);
Variant::Type value_type = value.get_type();

Variant::Type new_value_type = Variant::Type(p_index);
if (new_value_type == value_type) {
return;
}

Callable::CallError ce;
Variant new_value = Variant::construct(new_value_type, nullptr, 0, ce);

emit_changed(edited_prop, new_value, "", true);
update_property();
}

EditorPropertyGeneric::EditorPropertyGeneric() {
hbox = memnew(HBoxContainer);
hbox->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(hbox);

change_type = memnew(PopupMenu);
add_child(change_type);
change_type->connect("id_pressed", callable_mp(this, &EditorPropertyGeneric::_change_type_menu));

for (int i = 0; i < Variant::VARIANT_MAX; i++) {
String type = Variant::get_type_name(Variant::Type(i));
change_type->add_item(type, i);
}
}

void EditorPropertyGeneric::_bind_methods() {
}

///////////////////// TEXT /////////////////////////

void EditorPropertyText::_text_entered(const String &p_string) {
Expand Down Expand Up @@ -3121,8 +3217,13 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
switch (p_type) {
// atomic types
case Variant::NIL: {
EditorPropertyNil *editor = memnew(EditorPropertyNil);
add_property_editor(p_path, editor);
if (p_usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
EditorPropertyGeneric *editor = memnew(EditorPropertyGeneric);
add_property_editor(p_path, editor);
} else {
EditorPropertyNil *editor = memnew(EditorPropertyNil);
add_property_editor(p_path, editor);
}
} break;
case Variant::BOOL: {
EditorPropertyCheck *editor = memnew(EditorPropertyCheck);
Expand Down
19 changes: 18 additions & 1 deletion editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,30 @@

class EditorPropertyNil : public EditorProperty {
GDCLASS(EditorPropertyNil, EditorProperty);
LineEdit *text;

public:
virtual void update_property();
EditorPropertyNil();
};

class EditorPropertyGeneric : public EditorProperty {
GDCLASS(EditorPropertyGeneric, EditorProperty);
HBoxContainer *hbox;
PopupMenu *change_type;

void _property_changed(const String &p_prop, Variant p_value, const String &p_name = String(), bool changing = false);
void _object_id_selected(const String &p_property, ObjectID p_id);
void _change_type(Object *p_button);
void _change_type_menu(int p_index);

protected:
static void _bind_methods();

public:
virtual void update_property();
EditorPropertyGeneric();
};

class EditorPropertyText : public EditorProperty {
GDCLASS(EditorPropertyText, EditorProperty);
LineEdit *text;
Expand Down
4 changes: 3 additions & 1 deletion modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {

for (int i = 0; i < c->variables.size(); i++) {
if (c->variables[i]._export.type == Variant::NIL) {
continue;
if (!(c->variables[i]._export.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
continue;
}
}

members_cache.push_back(c->variables[i]._export);
Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/gdscript_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
prop_info.name = name;
PropertyInfo export_info = p_class->variables[i]._export;

if (export_info.type != Variant::NIL) {
if (export_info.type != Variant::NIL || export_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
if (!minfo.data_type.has_type) {
prop_info.type = export_info.type;
prop_info.class_name = export_info.class_name;
Expand Down
8 changes: 6 additions & 2 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4604,7 +4604,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
}
}

} else if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "Variant") {
current_export.type = Variant::Type::NIL;
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
current_export.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
tokenizer->advance();
} else {
parenthesis++;
Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
Expand Down Expand Up @@ -4832,7 +4836,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
ClassNode::Member member;

bool autoexport = tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_EXPORT;
if (current_export.type != Variant::NIL) {
if (current_export.type != Variant::NIL || current_export.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
member._export = current_export;
current_export = PropertyInfo();
}
Expand Down