Skip to content

Commit

Permalink
Add export_resource_uid annotation
Browse files Browse the repository at this point in the history
Resource UIDs can now be selected from the inspector
in the same way a Resource would be selected.

EditorResourcePicker has new path_only property
that hides menu items unrelated to UID selection.
  • Loading branch information
huwpascoe committed May 10, 2024
1 parent 2ba22d1 commit ee54c26
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 18 deletions.
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 an [int] 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 @@ -46,6 +46,9 @@
<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true" keywords="readonly, disabled, enabled">
If [code]true[/code], the value can be selected and edited.
</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="edited_resource" type="Resource" setter="set_edited_resource" getter="get_edited_resource">
The edited resource value.
</member>
Expand Down
57 changes: 57 additions & 0 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3441,6 +3441,59 @@ EditorPropertyResource::EditorPropertyResource() {
use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
}

////////////// 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) {
if (p_resource.is_valid() && !p_resource->get_path().is_empty()) {
emit_changed(get_edited_property(), ResourceLoader::get_resource_uid(p_resource->get_path()));
} else {
emit_changed(get_edited_property(), ResourceUID::INVALID_ID);
}
update_property();
}

void EditorPropertyUID::update_property() {
ResourceUID::ID id = get_edited_property_value();
Ref<Resource> res;
if (id != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(id)) {
res = ResourceLoader::load(ResourceUID::get_singleton()->get_id_path(id));
}
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 @@ -3594,7 +3647,11 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
editor->setup(p_hint_text);
return editor;
} else if (p_hint == PROPERTY_HINT_RESOURCE_UID) {
EditorPropertyUID *editor = memnew(EditorPropertyUID);
editor->setup(p_hint_text);

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

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 an [int] 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 : int

func load_scene():
var scene = load(ResourceUID.get_id_path(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::INT>);
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

0 comments on commit ee54c26

Please sign in to comment.