diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 02d84145079..4498396de33 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -57,7 +57,7 @@ #include "scene/resources/mesh.h" #include "scene/resources/visual_shader_nodes.h" -///////////////////// Nil ///////////////////////// +///////////////////// NIL ///////////////////////// void EditorPropertyNil::update_property() { } @@ -68,6 +68,91 @@ EditorPropertyNil::EditorPropertyNil() { add_child(prop_label); } +//////////////////// VARIANT /////////////////////// + +void EditorPropertyVariant::_change_type(int p_to_type) { + new_type = Variant::Type(p_to_type); + + Variant zero; + Callable::CallError ce; + Variant::construct(new_type, zero, nullptr, 0, ce); + emit_changed(get_edited_property(), zero); +} + +void EditorPropertyVariant::_set_read_only(bool p_read_only) { + change_type->set_disabled(p_read_only); + if (sub_property) { + sub_property->set_read_only(p_read_only); + } +} + +void EditorPropertyVariant::_notification(int p_what) { + if (p_what == NOTIFICATION_THEME_CHANGED) { + change_type->set_button_icon(get_editor_theme_icon("Edit")); + + PopupMenu *popup = change_type->get_popup(); + for (int i = 0; i < popup->get_item_count(); i++) { + popup->set_item_icon(i, get_editor_theme_icon(Variant::get_type_name(Variant::Type(popup->get_item_id(i))))); + } + } +} + +void EditorPropertyVariant::update_property() { + const Variant &value = get_edited_property_value(); + if (new_type == Variant::VARIANT_MAX) { + new_type = value.get_type(); + } + + if (new_type != current_type) { + current_type = new_type; + + if (sub_property) { + memdelete(sub_property); + sub_property = nullptr; + } + + if (current_type == Variant::OBJECT) { + sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource", PROPERTY_USAGE_NONE); + } else { + sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE); + } + ERR_FAIL_NULL(sub_property); + + sub_property->set_object_and_property(get_edited_object(), get_edited_property()); + sub_property->set_name_split_ratio(0); + sub_property->set_selectable(false); + sub_property->set_use_folding(is_using_folding()); + sub_property->set_read_only(is_read_only()); + sub_property->set_h_size_flags(SIZE_EXPAND_FILL); + sub_property->connect(SNAME("property_changed"), callable_mp((EditorProperty *)this, &EditorProperty::emit_changed)); + content->add_child(sub_property); + content->move_child(sub_property, 0); + sub_property->update_property(); + } else if (sub_property) { + sub_property->update_property(); + } + new_type = Variant::VARIANT_MAX; +} + +EditorPropertyVariant::EditorPropertyVariant() { + content = memnew(HBoxContainer); + add_child(content); + + change_type = memnew(MenuButton); + change_type->set_flat(false); + + PopupMenu *popup = change_type->get_popup(); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) { + // These types can't be constructed or serialized properly, so skip them. + continue; + } + popup->add_item(Variant::get_type_name(Variant::Type(i)), i); + } + popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type)); + content->add_child(change_type); +} + ///////////////////// TEXT ///////////////////////// void EditorPropertyText::_set_read_only(bool p_read_only) { @@ -3576,8 +3661,11 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ switch (p_type) { // atomic types case Variant::NIL: { - EditorPropertyNil *editor = memnew(EditorPropertyNil); - return editor; + if (p_usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + return memnew(EditorPropertyVariant); + } else { + return memnew(EditorPropertyNil); + } } break; case Variant::BOOL: { EditorPropertyCheck *editor = memnew(EditorPropertyCheck); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 6435825c676..9011071a2d9 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -54,6 +54,27 @@ public: EditorPropertyNil(); }; +class EditorPropertyVariant : public EditorProperty { + GDCLASS(EditorPropertyVariant, EditorProperty); + + HBoxContainer *content = nullptr; + EditorProperty *sub_property = nullptr; + MenuButton *change_type = nullptr; + + Variant::Type current_type = Variant::VARIANT_MAX; + Variant::Type new_type = Variant::VARIANT_MAX; + + void _change_type(int p_to_type); + +protected: + virtual void _set_read_only(bool p_read_only) override; + void _notification(int p_what); + +public: + virtual void update_property() override; + EditorPropertyVariant(); +}; + class EditorPropertyText : public EditorProperty { GDCLASS(EditorPropertyText, EditorProperty); LineEdit *text = nullptr; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 1fd0dee93f0..5ad527b6749 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -1007,7 +1007,7 @@ void TileSourceInspectorPlugin::_confirm_change_id() { } bool TileSourceInspectorPlugin::can_handle(Object *p_object) { - return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject"); + return p_object && (p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject")); } bool TileSourceInspectorPlugin::parse_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) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 698675677af..918fb7946a0 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4568,14 +4568,9 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta return false; } - if (export_type.is_variant() || export_type.has_no_type()) { - if (is_dict) { - // Dictionary allowed to have a variant key/value. - export_type.kind = GDScriptParser::DataType::BUILTIN; - } else { - push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation); - return false; - } + if (export_type.has_no_type()) { + push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation); + return false; } switch (export_type.kind) { @@ -4626,6 +4621,12 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta variable->export_info.class_name = String(export_type.native_type).replace("::", "."); } } break; + case GDScriptParser::DataType::VARIANT: { + if (export_type.is_variant()) { + variable->export_info.type = Variant::NIL; + variable->export_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + } break; default: push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation); return false; diff --git a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs index 8aeb19e08b7..9472105a75f 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs @@ -10,6 +10,11 @@ namespace GodotTools.Inspector { public override bool _CanHandle(GodotObject godotObject) { + if (godotObject == null) + { + return false; + } + foreach (var script in EnumerateScripts(godotObject)) { if (script is CSharpScript)