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

Add export_resource_uid annotation #91815

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
1 change: 1 addition & 0 deletions core/core_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_FILE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_UID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXPRESSION);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT);
Expand Down
1 change: 1 addition & 0 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ enum PropertyHint {
PROPERTY_HINT_HIDE_QUATERNION_EDIT, /// Only Node3D::transform should hide the quaternion editor.
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_RESOURCE_UID,
PROPERTY_HINT_MAX,
};

Expand Down
5 changes: 4 additions & 1 deletion doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,9 @@
<constant name="PROPERTY_HINT_RESOURCE_TYPE" value="17" enum="PropertyHint">
Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture2D"[/code]). Editing it will show a popup menu of valid resource types to instantiate.
</constant>
<constant name="PROPERTY_HINT_RESOURCE_UID" value="38" enum="PropertyHint">
Hints that a [String] property is a unique identifier of a [Resource]-derived type, specified via the hint string. Editing it will show a popup menu of valid resource types to instantiate.
</constant>
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="18" enum="PropertyHint">
Hints that a [String] property is text with line breaks. Editing it will show a text input field where line breaks can be typed.
</constant>
Expand Down Expand Up @@ -2935,7 +2938,7 @@
<constant name="PROPERTY_HINT_PASSWORD" value="36" enum="PropertyHint">
Hints that a string property is a password, and every character is replaced with the secret character.
</constant>
<constant name="PROPERTY_HINT_MAX" value="38" enum="PropertyHint">
<constant name="PROPERTY_HINT_MAX" value="39" enum="PropertyHint">
Represents the size of the [enum PropertyHint] enum.
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/EditorResourcePicker.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
<member name="edited_resource" type="Resource" setter="set_edited_resource" getter="get_edited_resource">
The edited resource value.
</member>
<member name="path_only" type="bool" setter="set_path_only" getter="is_path_only" default="false" keywords="path_only, disabled, enabled">
If [code]true[/code], menu items related to creating resources will be hidden.
</member>
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" default="false">
If [code]true[/code], the main button with the resource preview works in the toggle mode. Use [method set_toggle_pressed] to manually set the state.
</member>
Expand Down
58 changes: 57 additions & 1 deletion editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3433,6 +3433,59 @@ EditorPropertyResource::EditorPropertyResource() {
has_borders = true;
}

////////////// UID //////////////////////

void EditorPropertyUID::_set_read_only(bool p_read_only) {
resource_picker->set_editable(!p_read_only);
}

void EditorPropertyUID::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {
emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);
}

void EditorPropertyUID::_resource_changed(const Ref<Resource> &p_resource) {
String res;
if (p_resource.is_valid() && !p_resource->get_path().is_empty()) {
res = ResourceUID::get_singleton()->id_to_text(ResourceLoader::get_resource_uid(p_resource->get_path()));
}
emit_changed(get_edited_property(), res);
update_property();
}

void EditorPropertyUID::update_property() {
String path = get_edited_property_value();
Ref<Resource> res;
if (ResourceLoader::exists(path)) {
res = ResourceLoader::load(path);
}
resource_picker->set_edited_resource_no_check(res);
}

void EditorPropertyUID::setup(const String &p_base_type) {
if (resource_picker) {
memdelete(resource_picker);
resource_picker = nullptr;
}

resource_picker = memnew(EditorResourcePicker);

resource_picker->set_base_type(p_base_type);
resource_picker->set_editable(true);
resource_picker->set_path_only(true);
resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(resource_picker);

resource_picker->connect("resource_selected", callable_mp(this, &EditorPropertyUID::_resource_selected));
resource_picker->connect("resource_changed", callable_mp(this, &EditorPropertyUID::_resource_changed));

for (int i = 0; i < resource_picker->get_child_count(); i++) {
Button *b = Object::cast_to<Button>(resource_picker->get_child(i));
if (b) {
add_focusable(b);
}
}
}

////////////// DEFAULT PLUGIN //////////////////////

bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {
Expand Down Expand Up @@ -3586,7 +3639,6 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
editor->setup(p_hint_text);
return editor;

} else {
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);

Expand Down Expand Up @@ -3655,6 +3707,10 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
editor->set_save_mode();
}
return editor;
} else if (p_hint == PROPERTY_HINT_RESOURCE_UID) {
EditorPropertyUID *editor = memnew(EditorPropertyUID);
editor->setup(p_hint_text);
return editor;
} else {
EditorPropertyText *editor = memnew(EditorPropertyText);
if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {
Expand Down
16 changes: 16 additions & 0 deletions editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,22 @@ class EditorPropertyResource : public EditorProperty {
EditorPropertyResource();
};

class EditorPropertyUID : public EditorProperty {
GDCLASS(EditorPropertyUID, EditorProperty);

EditorResourcePicker *resource_picker = nullptr;

void _resource_selected(const Ref<Resource> &p_resource, bool p_inspect);
void _resource_changed(const Ref<Resource> &p_resource);

protected:
virtual void _set_read_only(bool p_read_only) override;

public:
virtual void update_property() override;
void setup(const String &p_base_type);
};

///////////////////////////////////////////////////
/// \brief The EditorInspectorDefaultPlugin class
///
Expand Down
58 changes: 41 additions & 17 deletions editor/editor_resource_picker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,24 +217,27 @@ void EditorResourcePicker::_update_menu_items() {

if (is_editable()) {
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);

// Check whether the resource has subresources.
List<PropertyInfo> property_list;
edited_resource->get_property_list(&property_list);
bool has_subresources = false;
for (PropertyInfo &p : property_list) {
if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script") && ((Object *)edited_resource->get(p.name) != nullptr)) {
has_subresources = true;
break;

if (!is_path_only()) {
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);

// Check whether the resource has subresources.
List<PropertyInfo> property_list;
edited_resource->get_property_list(&property_list);
bool has_subresources = false;
for (PropertyInfo &p : property_list) {
if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script") && ((Object *)edited_resource->get(p.name) != nullptr)) {
has_subresources = true;
break;
}
}
if (has_subresources) {
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE);
}
}
if (has_subresources) {
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE);
}

edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Save")), TTR("Save"), OBJ_MENU_SAVE);
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Save")), TTR("Save As..."), OBJ_MENU_SAVE_AS);
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Save")), TTR("Save"), OBJ_MENU_SAVE);
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Save")), TTR("Save As..."), OBJ_MENU_SAVE_AS);
}
}

if (edited_resource->get_path().is_resource_file()) {
Expand Down Expand Up @@ -264,6 +267,12 @@ void EditorResourcePicker::_update_menu_items() {
}
}

if (paste_valid && is_path_only()) {
if (EditorSettings::get_singleton()->get_resource_clipboard()->get_path().is_empty()) {
paste_valid = false;
}
}

if (edited_resource.is_valid() || paste_valid) {
edit_menu->add_separator();

Expand All @@ -277,7 +286,7 @@ void EditorResourcePicker::_update_menu_items() {
}

// Add options to convert existing resource to another type of resource.
if (is_editable() && edited_resource.is_valid()) {
if (is_editable() && !is_path_only() && edited_resource.is_valid()) {
Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(edited_resource);
if (conversions.size()) {
edit_menu->add_separator();
Expand Down Expand Up @@ -478,6 +487,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
}

void EditorResourcePicker::set_create_options(Object *p_menu_node) {
if (is_path_only()) {
return;
}

_ensure_resource_menu();
// If a subclass implements this method, use it to replace all create items.
if (GDVIRTUAL_CALL(_set_create_options, p_menu_node)) {
Expand Down Expand Up @@ -781,13 +794,16 @@ void EditorResourcePicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_toggle_pressed", "pressed"), &EditorResourcePicker::set_toggle_pressed);
ClassDB::bind_method(D_METHOD("set_editable", "enable"), &EditorResourcePicker::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &EditorResourcePicker::is_editable);
ClassDB::bind_method(D_METHOD("set_path_only", "enable"), &EditorResourcePicker::set_path_only);
ClassDB::bind_method(D_METHOD("is_path_only"), &EditorResourcePicker::is_path_only);

GDVIRTUAL_BIND(_set_create_options, "menu_node");
GDVIRTUAL_BIND(_handle_menu_selected, "id");

ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type"), "set_base_type", "get_base_type");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource", PROPERTY_USAGE_NONE), "set_edited_resource", "get_edited_resource");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_only"), "set_path_only", "is_path_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");

ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::BOOL, "inspect")));
Expand Down Expand Up @@ -940,6 +956,14 @@ bool EditorResourcePicker::is_editable() const {
return editable;
}

void EditorResourcePicker::set_path_only(bool p_path_only) {
path_only = p_path_only;
}

bool EditorResourcePicker::is_path_only() const {
return path_only;
}

void EditorResourcePicker::_ensure_resource_menu() {
if (edit_menu) {
return;
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_resource_picker.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class EditorResourcePicker : public HBoxContainer {
Ref<Resource> edited_resource;

bool editable = true;
bool path_only = false;
bool dropping = false;

Vector<String> inheritors_array;
Expand Down Expand Up @@ -139,6 +140,9 @@ class EditorResourcePicker : public HBoxContainer {
void set_editable(bool p_editable);
bool is_editable() const;

void set_path_only(bool p_path_only);
bool is_path_only() const;

virtual void set_create_options(Object *p_menu_node);
virtual bool handle_menu_selected(int p_which);

Expand Down
15 changes: 15 additions & 0 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,21 @@
[/codeblock]
</description>
</annotation>
<annotation name="@export_resource_uid">
<return type="void" />
<param index="0" name="type" type="String" />
<description>
Export a [String] property as a unique identifier to a resource.
Used to reference resources without preloading or cyclic dependencies.
[codeblock]
@export_resource_uid("PackedScene") var scene_to_load: String

func load_scene():
var scene = load(scene_to_load)
return scene.instantiate()
[/codeblock]
</description>
</annotation>
<annotation name="@export_storage">
<return type="void" />
<description>
Expand Down
1 change: 1 addition & 0 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
register_annotation(MethodInfo("@export_resource_uid", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RESOURCE_UID, Variant::STRING>);
register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const Utils = preload("../../utils.notest.gd")
@export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var test_node_path := ^"hello"
@export var test_node: Node
@export var test_node_array: Array[Node]
@export_resource_uid("PackedScene") var test_resource_uid: String = "uid://<invalid>"

func test():
for property in get_property_list():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ var test_node: Node = null
hint=NODE_TYPE hint_string="Node" usage=DEFAULT|SCRIPT_VARIABLE
var test_node_array: Array = Array[Node]([])
hint=TYPE_STRING hint_string="Object/NODE_TYPE:Node" usage=DEFAULT|SCRIPT_VARIABLE
var test_resource_uid: String = "uid://<invalid>"
hint=RESOURCE_UID hint_string="PackedScene" usage=DEFAULT|SCRIPT_VARIABLE
2 changes: 2 additions & 0 deletions modules/gdscript/tests/scripts/utils.notest.gd
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ static func get_property_hint_name(hint: PropertyHint) -> String:
return "PROPERTY_HINT_GLOBAL_DIR"
PROPERTY_HINT_RESOURCE_TYPE:
return "PROPERTY_HINT_RESOURCE_TYPE"
PROPERTY_HINT_RESOURCE_UID:
return "PROPERTY_HINT_RESOURCE_UID"
PROPERTY_HINT_MULTILINE_TEXT:
return "PROPERTY_HINT_MULTILINE_TEXT"
PROPERTY_HINT_EXPRESSION:
Expand Down
Loading