You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-21 14:57:09 +00:00
Merge pull request #109458 from SatLess/DumbRes
Add indicator to linked resources
This commit is contained in:
@@ -1641,7 +1641,7 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St
|
|||||||
saving_resources_in_path.erase(p_resource);
|
saving_resources_in_path.erase(p_resource);
|
||||||
|
|
||||||
_resource_saved(p_resource, path);
|
_resource_saved(p_resource, path);
|
||||||
|
clear_node_reference(p_resource); // // Check if Resource is saved to disk to potentially remove it from resource_count
|
||||||
emit_signal(SNAME("resource_saved"), p_resource);
|
emit_signal(SNAME("resource_saved"), p_resource);
|
||||||
editor_data.notify_resource_saved(p_resource);
|
editor_data.notify_resource_saved(p_resource);
|
||||||
}
|
}
|
||||||
@@ -1743,6 +1743,161 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
|
|||||||
file->popup_file_dialog();
|
file->popup_file_dialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EditorNode::is_resource_internal_to_scene(Ref<Resource> p_resource) {
|
||||||
|
bool inside_scene = !get_edited_scene() || get_edited_scene()->get_scene_file_path() == p_resource->get_path().get_slice("::", 0);
|
||||||
|
return inside_scene || p_resource->get_path().is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorNode::gather_resources(const Variant &p_variant, List<Ref<Resource>> &r_list, bool p_subresources, bool p_allow_external) {
|
||||||
|
Variant::Type type = p_variant.get_type();
|
||||||
|
if (type == Variant::OBJECT && p_variant.get_validated_object() == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != Variant::OBJECT && type != Variant::ARRAY && type != Variant::DICTIONARY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Variant::ARRAY) {
|
||||||
|
Array arr = p_variant;
|
||||||
|
for (const Variant &v : arr) {
|
||||||
|
Ref<Resource> res = v;
|
||||||
|
if (res.is_valid()) {
|
||||||
|
if (p_allow_external) {
|
||||||
|
r_list.push_back(res);
|
||||||
|
} else if (is_resource_internal_to_scene(res)) {
|
||||||
|
r_list.push_back(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gather_resources(v, r_list, p_subresources, p_allow_external);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Variant::DICTIONARY) {
|
||||||
|
Dictionary dict = p_variant;
|
||||||
|
for (const KeyValue<Variant, Variant> &kv : dict) {
|
||||||
|
Ref<Resource> res_key = kv.key;
|
||||||
|
Ref<Resource> res_value = kv.value;
|
||||||
|
if (res_key.is_valid()) {
|
||||||
|
if (p_allow_external) {
|
||||||
|
r_list.push_back(res_key);
|
||||||
|
} else if (is_resource_internal_to_scene(res_key)) {
|
||||||
|
r_list.push_back(res_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res_value.is_valid()) {
|
||||||
|
if (p_allow_external) {
|
||||||
|
r_list.push_back(res_value);
|
||||||
|
} else if (is_resource_internal_to_scene(res_value)) {
|
||||||
|
r_list.push_back(res_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gather_resources(kv.key, r_list, p_subresources, p_allow_external);
|
||||||
|
gather_resources(kv.value, r_list, p_subresources, p_allow_external);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PropertyInfo> pinfo;
|
||||||
|
p_variant.get_property_list(&pinfo);
|
||||||
|
|
||||||
|
for (const PropertyInfo &E : pinfo) {
|
||||||
|
if (!(E.usage & PROPERTY_USAGE_EDITOR) || E.name == "script") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant property_value = p_variant.get(E.name);
|
||||||
|
Variant::Type property_type = property_value.get_type();
|
||||||
|
|
||||||
|
if (property_type == Variant::ARRAY || property_type == Variant::DICTIONARY) {
|
||||||
|
gather_resources(property_value, r_list, p_subresources, p_allow_external);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Resource> res = property_value;
|
||||||
|
if (res.is_null()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!p_allow_external) {
|
||||||
|
if (!res->is_built_in() || res->get_path().get_slice("::", 0) != get_edited_scene()->get_scene_file_path()) {
|
||||||
|
if (!res->get_path().is_empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r_list.push_back(res);
|
||||||
|
if (p_subresources) {
|
||||||
|
gather_resources(res, r_list, p_subresources, p_allow_external);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorNode::update_resource_count(Node *p_node, bool p_remove) {
|
||||||
|
if (!get_edited_scene()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Ref<Resource>> res_list;
|
||||||
|
gather_resources(p_node, res_list, true);
|
||||||
|
|
||||||
|
for (Ref<Resource> &R : res_list) {
|
||||||
|
List<Node *>::Element *E = resource_count[R].find(p_node);
|
||||||
|
if (E) {
|
||||||
|
if (p_remove) {
|
||||||
|
resource_count[R].erase(E);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resource_count[R].push_back(p_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_signal(SNAME("resource_counter_changed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int EditorNode::get_resource_count(Ref<Resource> p_res) {
|
||||||
|
List<Node *> *L = resource_count.getptr(p_res);
|
||||||
|
return L ? L->size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Node *> EditorNode::get_resource_node_list(Ref<Resource> p_res) {
|
||||||
|
List<Node *> *L = resource_count.getptr(p_res);
|
||||||
|
return L == nullptr ? List<Node *>() : *L;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorNode::update_node_reference(const Variant &p_value, Node *p_node, bool p_remove) {
|
||||||
|
List<Ref<Resource>> list;
|
||||||
|
Ref<Resource> res = p_value;
|
||||||
|
gather_resources(p_value, list, true); //Gather all Resources and their SubResources to remove p_node from their lists.
|
||||||
|
|
||||||
|
if (res.is_valid() && is_resource_internal_to_scene(res)) {
|
||||||
|
// Avoid external Resources from being added in.
|
||||||
|
list.push_back(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Ref<Resource> &R : list) {
|
||||||
|
if (!p_remove) {
|
||||||
|
resource_count[R].push_back(p_node);
|
||||||
|
} else {
|
||||||
|
List<Node *>::Element *E = resource_count[R].find(p_node);
|
||||||
|
if (E) {
|
||||||
|
resource_count[R].erase(E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit_signal(SNAME("resource_counter_changed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorNode::clear_node_reference(Ref<Resource> p_res) {
|
||||||
|
if (is_resource_internal_to_scene(p_res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Node *> *node_list = resource_count.getptr(p_res);
|
||||||
|
if (node_list != nullptr) {
|
||||||
|
node_list->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EditorNode::_menu_option(int p_option) {
|
void EditorNode::_menu_option(int p_option) {
|
||||||
_menu_option_confirm(p_option, false);
|
_menu_option_confirm(p_option, false);
|
||||||
}
|
}
|
||||||
@@ -4311,6 +4466,7 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) {
|
|||||||
|
|
||||||
Node *old_scene = get_editor_data().get_edited_scene_root();
|
Node *old_scene = get_editor_data().get_edited_scene_root();
|
||||||
|
|
||||||
|
resource_count.clear();
|
||||||
editor_selection->clear();
|
editor_selection->clear();
|
||||||
SceneTreeDock::get_singleton()->clear_previous_node_selection();
|
SceneTreeDock::get_singleton()->clear_previous_node_selection();
|
||||||
editor_data.set_edited_scene(p_idx);
|
editor_data.set_edited_scene(p_idx);
|
||||||
@@ -7362,6 +7518,8 @@ void EditorNode::_bind_methods() {
|
|||||||
|
|
||||||
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
|
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("update_node_reference", "value", "node", "remove"), &EditorNode::update_node_reference, DEFVAL(false));
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("request_help_search"));
|
ADD_SIGNAL(MethodInfo("request_help_search"));
|
||||||
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
|
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
|
||||||
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
|
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
|
||||||
@@ -7369,6 +7527,7 @@ void EditorNode::_bind_methods() {
|
|||||||
ADD_SIGNAL(MethodInfo("scene_changed"));
|
ADD_SIGNAL(MethodInfo("scene_changed"));
|
||||||
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path")));
|
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path")));
|
||||||
ADD_SIGNAL(MethodInfo("preview_locale_changed"));
|
ADD_SIGNAL(MethodInfo("preview_locale_changed"));
|
||||||
|
ADD_SIGNAL(MethodInfo("resource_counter_changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Node *_resource_get_edited_scene() {
|
static Node *_resource_get_edited_scene() {
|
||||||
|
|||||||
@@ -465,6 +465,7 @@ private:
|
|||||||
|
|
||||||
Ref<Resource> saving_resource;
|
Ref<Resource> saving_resource;
|
||||||
HashSet<Ref<Resource>> saving_resources_in_path;
|
HashSet<Ref<Resource>> saving_resources_in_path;
|
||||||
|
HashMap<Ref<Resource>, List<Node *>> resource_count; // Keeps track of linked Resources from a Scene.
|
||||||
|
|
||||||
uint64_t update_spinner_step_msec = 0;
|
uint64_t update_spinner_step_msec = 0;
|
||||||
uint64_t update_spinner_step_frame = 0;
|
uint64_t update_spinner_step_frame = 0;
|
||||||
@@ -809,6 +810,13 @@ public:
|
|||||||
void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path);
|
void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path);
|
||||||
void save_resource(const Ref<Resource> &p_resource);
|
void save_resource(const Ref<Resource> &p_resource);
|
||||||
void save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path = String());
|
void save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path = String());
|
||||||
|
bool is_resource_internal_to_scene(Ref<Resource> p_resource);
|
||||||
|
void gather_resources(const Variant &p_variant, List<Ref<Resource>> &r_list, bool p_subresources = false, bool p_allow_external = false);
|
||||||
|
void update_resource_count(Node *p_node, bool p_remove = false);
|
||||||
|
void update_node_reference(const Variant &p_value, Node *p_node, bool p_remove = false);
|
||||||
|
void clear_node_reference(Ref<Resource> p_res);
|
||||||
|
int get_resource_count(Ref<Resource> p_res);
|
||||||
|
List<Node *> get_resource_node_list(Ref<Resource> p_res);
|
||||||
|
|
||||||
void show_about() { _menu_option_confirm(HELP_ABOUT, false); }
|
void show_about() { _menu_option_confirm(HELP_ABOUT, false); }
|
||||||
|
|
||||||
|
|||||||
@@ -5175,12 +5175,23 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
|
|||||||
undo_redo->add_do_property(object, p_name, p_value);
|
undo_redo->add_do_property(object, p_name, p_value);
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
Variant value = object->get(p_name, &valid);
|
Variant value = object->get(p_name, &valid);
|
||||||
|
Variant::Type type = p_value.get_type();
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (Object::cast_to<Control>(object) && (p_name == "anchors_preset" || p_name == "layout_mode")) {
|
if (Object::cast_to<Control>(object) && (p_name == "anchors_preset" || p_name == "layout_mode")) {
|
||||||
undo_redo->add_undo_method(object, "_edit_set_state", Object::cast_to<Control>(object)->_edit_get_state());
|
undo_redo->add_undo_method(object, "_edit_set_state", Object::cast_to<Control>(object)->_edit_get_state());
|
||||||
} else {
|
} else {
|
||||||
undo_redo->add_undo_property(object, p_name, value);
|
undo_redo->add_undo_property(object, p_name, value);
|
||||||
}
|
}
|
||||||
|
// We'll use Editor Selection to get the currently edited Node.
|
||||||
|
Node *N = Object::cast_to<Node>(object);
|
||||||
|
if (N && (type == Variant::OBJECT || type == Variant::ARRAY || type == Variant::DICTIONARY) && value != p_value) {
|
||||||
|
undo_redo->add_do_method(EditorNode::get_singleton(), "update_node_reference", value, N, true);
|
||||||
|
undo_redo->add_do_method(EditorNode::get_singleton(), "update_node_reference", p_value, N, false);
|
||||||
|
// Perhaps an inefficient way of updating the resource count.
|
||||||
|
// We could go in depth and check which Resource values changed/got removed and which ones stayed the same, but this is more readable at the moment.
|
||||||
|
undo_redo->add_undo_method(EditorNode::get_singleton(), "update_node_reference", p_value, N, true);
|
||||||
|
undo_redo->add_undo_method(EditorNode::get_singleton(), "update_node_reference", value, N, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<StringName> linked_properties;
|
List<StringName> linked_properties;
|
||||||
@@ -5229,7 +5240,20 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
Resource *r = Object::cast_to<Resource>(object);
|
Resource *r = Object::cast_to<Resource>(object);
|
||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
|
//Setting a Subresource. Since there's possibly multiple Nodes referencing 'r', we need to link them to the Subresource.
|
||||||
|
List<Node *> shared_nodes = EditorNode::get_singleton()->get_resource_node_list(r);
|
||||||
|
for (Node *N : shared_nodes) {
|
||||||
|
if ((type == Variant::OBJECT || type == Variant::ARRAY || type == Variant::DICTIONARY) && value != p_value) {
|
||||||
|
undo_redo->add_do_method(EditorNode::get_singleton(), "update_node_reference", value, N, true);
|
||||||
|
undo_redo->add_do_method(EditorNode::get_singleton(), "update_node_reference", p_value, N, false);
|
||||||
|
// Perhaps an inefficient way of updating the resource count.
|
||||||
|
// We could go in depth and check which Resource values changed/got removed and which ones stayed the same, but this is more readable at the moment.
|
||||||
|
undo_redo->add_undo_method(EditorNode::get_singleton(), "update_node_reference", p_value, N, true);
|
||||||
|
undo_redo->add_undo_method(EditorNode::get_singleton(), "update_node_reference", value, N, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (String(p_name) == "resource_local_to_scene") {
|
if (String(p_name) == "resource_local_to_scene") {
|
||||||
bool prev = object->get(p_name);
|
bool prev = object->get(p_name);
|
||||||
bool next = p_value;
|
bool next = p_value;
|
||||||
|
|||||||
@@ -89,12 +89,45 @@ void EditorResourcePicker::_update_resource() {
|
|||||||
preview_rect->set_texture(Ref<Texture2D>());
|
preview_rect->set_texture(Ref<Texture2D>());
|
||||||
|
|
||||||
assign_button->set_custom_minimum_size(assign_button_min_size);
|
assign_button->set_custom_minimum_size(assign_button_min_size);
|
||||||
|
|
||||||
if (edited_resource == Ref<Resource>()) {
|
if (edited_resource == Ref<Resource>()) {
|
||||||
assign_button->set_button_icon(Ref<Texture2D>());
|
assign_button->set_button_icon(Ref<Texture2D>());
|
||||||
assign_button->set_text(TTR("<empty>"));
|
assign_button->set_text(TTR("<empty>"));
|
||||||
assign_button->set_tooltip_text("");
|
assign_button->set_tooltip_text("");
|
||||||
|
make_unique_button->set_disabled(true);
|
||||||
|
make_unique_button->set_visible(false);
|
||||||
} else {
|
} else {
|
||||||
|
Ref<Resource> parent_res = _has_parent_resource();
|
||||||
|
bool unique_enable = _is_uniqueness_enabled();
|
||||||
|
bool unique_recursive_enabled = _is_uniqueness_enabled(true);
|
||||||
|
bool is_internal = EditorNode::get_singleton()->is_resource_internal_to_scene(edited_resource);
|
||||||
|
int num_of_copies = EditorNode::get_singleton()->get_resource_count(edited_resource);
|
||||||
|
make_unique_button->set_button_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||||
|
make_unique_button->set_visible((num_of_copies > 1 || !is_internal) && !Object::cast_to<Script>(edited_resource.ptr()));
|
||||||
|
make_unique_button->set_disabled((!unique_enable && !unique_recursive_enabled) || !editable);
|
||||||
|
|
||||||
|
String tooltip;
|
||||||
|
|
||||||
|
if (num_of_copies > 1) {
|
||||||
|
tooltip = vformat(TTR("This Resource is used in (%d) places."), num_of_copies);
|
||||||
|
} else if (!is_internal) {
|
||||||
|
tooltip = TTR("This Resource is external to scene.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!editable) {
|
||||||
|
tooltip += "\n" + TTR("The Resource cannot be edited in the inspector and can't be made unique directly.") + "\n";
|
||||||
|
} else {
|
||||||
|
tooltip += unique_enable ? TTR(" Left-click to make it unique.") + "\n" : "\n";
|
||||||
|
|
||||||
|
if (unique_recursive_enabled) {
|
||||||
|
tooltip += TTR("It is possible to make its subresources unique. Right-click to make them unique.") + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unique_enable && EditorNode::get_singleton()->get_editor_selection()->get_full_selected_node_list().size() == 1) {
|
||||||
|
tooltip += TTR("In order to duplicate it, make its parent Resource unique.") + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
make_unique_button->set_tooltip_text(tooltip);
|
||||||
assign_button->set_button_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->()));
|
assign_button->set_button_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->()));
|
||||||
|
|
||||||
if (!edited_resource->get_name().is_empty()) {
|
if (!edited_resource->get_name().is_empty()) {
|
||||||
@@ -134,7 +167,10 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p_preview.is_valid()) {
|
if (p_preview.is_valid()) {
|
||||||
|
int thumbnail_size = (int)EDITOR_GET("filesystem/file_dialog/thumbnail_size") * EDSCALE;
|
||||||
|
if (assign_button->get_button_icon().is_valid()) {
|
||||||
preview_rect->set_offset(SIDE_LEFT, assign_button->get_button_icon()->get_width() + assign_button->get_theme_stylebox(CoreStringName(normal))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button")));
|
preview_rect->set_offset(SIDE_LEFT, assign_button->get_button_icon()->get_width() + assign_button->get_theme_stylebox(CoreStringName(normal))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button")));
|
||||||
|
}
|
||||||
|
|
||||||
// Resource-specific stretching.
|
// Resource-specific stretching.
|
||||||
if (Ref<GradientTexture1D>(edited_resource).is_valid() || Ref<Gradient>(edited_resource).is_valid()) {
|
if (Ref<GradientTexture1D>(edited_resource).is_valid() || Ref<Gradient>(edited_resource).is_valid()) {
|
||||||
@@ -142,13 +178,15 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const
|
|||||||
assign_button->set_custom_minimum_size(assign_button_min_size);
|
assign_button->set_custom_minimum_size(assign_button_min_size);
|
||||||
} else {
|
} else {
|
||||||
preview_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
preview_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
||||||
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
|
||||||
thumbnail_size *= EDSCALE;
|
|
||||||
assign_button->set_custom_minimum_size(assign_button_min_size.max(Size2(1, thumbnail_size)));
|
assign_button->set_custom_minimum_size(assign_button_min_size.max(Size2(1, thumbnail_size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
preview_rect->set_texture(p_preview);
|
preview_rect->set_texture(p_preview);
|
||||||
assign_button->set_text("");
|
assign_button->set_text("");
|
||||||
|
|
||||||
|
if (preview_rect->get_size().x <= thumbnail_size) {
|
||||||
|
assign_button->set_button_icon(Ref<Texture2D>());
|
||||||
|
preview_rect->set_offset(SIDE_LEFT, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,10 +302,28 @@ void EditorResourcePicker::_update_menu_items() {
|
|||||||
if (!_is_custom_type_script()) {
|
if (!_is_custom_type_script()) {
|
||||||
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("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
|
||||||
}
|
}
|
||||||
|
bool unique_enabled = _is_uniqueness_enabled();
|
||||||
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
|
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
|
||||||
|
edit_menu->set_item_disabled(-1, !unique_enabled);
|
||||||
|
|
||||||
|
if (!unique_enabled) {
|
||||||
|
if (EditorNode::get_singleton()->is_resource_internal_to_scene(edited_resource) && EditorNode::get_singleton()->get_resource_count(edited_resource) == 1) {
|
||||||
|
edit_menu->set_item_tooltip(-1, TTRC("This Resource is already unique."));
|
||||||
|
} else if (_has_parent_resource().is_valid()) {
|
||||||
|
edit_menu->set_item_tooltip(-1, TTRC("In order to duplicate it, make its parent Resource unique."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_has_sub_resources(edited_resource)) {
|
if (_has_sub_resources(edited_resource)) {
|
||||||
|
unique_enabled = _is_uniqueness_enabled(true);
|
||||||
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("Duplicate")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE);
|
||||||
|
edit_menu->set_item_disabled(-1, !unique_enabled);
|
||||||
|
if (!unique_enabled) {
|
||||||
|
Ref<Resource> parent_res = _has_parent_resource();
|
||||||
|
if (EditorNode::get_singleton()->get_editor_selection()->get_full_selected_node_list().size() == 1) {
|
||||||
|
edit_menu->set_item_tooltip(-1, (parent_res.is_valid() && EditorNode::get_singleton()->get_resource_count(parent_res) > 1) ? TTRC("In order to duplicate recursively, make its parent Resource unique.") : TTRC("Subresources have already been made unique."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"), OBJ_MENU_SAVE);
|
||||||
@@ -403,7 +459,6 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||||||
if (edited_resource.is_null()) {
|
if (edited_resource.is_null()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Resource> unique_resource = edited_resource->duplicate();
|
Ref<Resource> unique_resource = edited_resource->duplicate();
|
||||||
ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
|
ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
|
||||||
|
|
||||||
@@ -632,6 +687,14 @@ void EditorResourcePicker::_button_input(const Ref<InputEvent> &p_event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorResourcePicker::_on_unique_button_pressed() {
|
||||||
|
if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
|
||||||
|
_edit_menu_cbk(OBJ_MENU_MAKE_UNIQUE);
|
||||||
|
} else if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
|
||||||
|
_edit_menu_cbk(_is_uniqueness_enabled(true) ? OBJ_MENU_MAKE_UNIQUE_RECURSIVE : OBJ_MENU_MAKE_UNIQUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String EditorResourcePicker::_get_owner_path() const {
|
String EditorResourcePicker::_get_owner_path() const {
|
||||||
EditorProperty *property = Object::cast_to<EditorProperty>(get_parent());
|
EditorProperty *property = Object::cast_to<EditorProperty>(get_parent());
|
||||||
if (!property) {
|
if (!property) {
|
||||||
@@ -953,12 +1016,14 @@ void EditorResourcePicker::_bind_methods() {
|
|||||||
void EditorResourcePicker::_notification(int p_what) {
|
void EditorResourcePicker::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ENTER_TREE: {
|
case NOTIFICATION_ENTER_TREE: {
|
||||||
|
EditorNode::get_singleton()->connect("resource_counter_changed", callable_mp(this, &EditorResourcePicker::_update_resource));
|
||||||
_update_resource();
|
_update_resource();
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
case NOTIFICATION_THEME_CHANGED: {
|
case NOTIFICATION_THEME_CHANGED: {
|
||||||
const int icon_width = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
const int icon_width = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||||
assign_button->add_theme_constant_override("icon_max_width", icon_width);
|
assign_button->add_theme_constant_override("icon_max_width", icon_width);
|
||||||
|
make_unique_button->add_theme_constant_override("icon_max_width", icon_width);
|
||||||
if (edit_menu) {
|
if (edit_menu) {
|
||||||
edit_menu->add_theme_constant_override("icon_max_width", icon_width);
|
edit_menu->add_theme_constant_override("icon_max_width", icon_width);
|
||||||
}
|
}
|
||||||
@@ -971,6 +1036,10 @@ void EditorResourcePicker::_notification(int p_what) {
|
|||||||
draw_style_box(get_theme_stylebox(SceneStringName(panel), SNAME("Tree")), Rect2(Point2(), get_size()));
|
draw_style_box(get_theme_stylebox(SceneStringName(panel), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NOTIFICATION_RESIZED: {
|
||||||
|
_update_resource();
|
||||||
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_DRAG_BEGIN: {
|
case NOTIFICATION_DRAG_BEGIN: {
|
||||||
if (editable && _is_drop_valid(get_viewport()->gui_get_drag_data())) {
|
if (editable && _is_drop_valid(get_viewport()->gui_get_drag_data())) {
|
||||||
dropping = true;
|
dropping = true;
|
||||||
@@ -987,9 +1056,13 @@ void EditorResourcePicker::_notification(int p_what) {
|
|||||||
|
|
||||||
case NOTIFICATION_EXIT_TREE: {
|
case NOTIFICATION_EXIT_TREE: {
|
||||||
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
||||||
|
Callable resource_counter_changed = callable_mp(this, &EditorResourcePicker::_update_resource);
|
||||||
if (EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
if (EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
||||||
EditorNode::get_singleton()->disconnect("resource_saved", resource_saved);
|
EditorNode::get_singleton()->disconnect("resource_saved", resource_saved);
|
||||||
}
|
}
|
||||||
|
if (EditorNode::get_singleton()->is_connected("resource_counter_changed", resource_counter_changed)) {
|
||||||
|
EditorNode::get_singleton()->disconnect("resource_counter_changed", resource_counter_changed);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1234,7 +1307,6 @@ void EditorResourcePicker::_duplicate_selected_resources() {
|
|||||||
|
|
||||||
if (meta.size() == 1) { // Root.
|
if (meta.size() == 1) { // Root.
|
||||||
edited_resource = unique_resource;
|
edited_resource = unique_resource;
|
||||||
_resource_changed();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Array parent_meta = item->get_parent()->get_metadata(0);
|
Array parent_meta = item->get_parent()->get_metadata(0);
|
||||||
@@ -1278,9 +1350,71 @@ void EditorResourcePicker::_duplicate_selected_resources() {
|
|||||||
dict[meta[2]] = unique_resource;
|
dict[meta[2]] = unique_resource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_resource_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorResourcePicker::_is_uniqueness_enabled(bool p_check_recursive) {
|
||||||
|
Ref<Resource> parent_resource = _has_parent_resource();
|
||||||
|
EditorNode *en = EditorNode::get_singleton();
|
||||||
|
bool internal_to_scene = en->is_resource_internal_to_scene(edited_resource);
|
||||||
|
List<Node *> node_list = en->get_editor_selection()->get_full_selected_node_list();
|
||||||
|
|
||||||
|
// Todo: Implement a more elegant solution for multiple selected Nodes. This should suffice for the time being.
|
||||||
|
if (node_list.size() > 1 && !p_check_recursive) {
|
||||||
|
return node_list.size() != EditorNode::get_singleton()->get_resource_count(edited_resource) || !internal_to_scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!internal_to_scene) {
|
||||||
|
if (parent_resource.is_valid() && (!EditorNode::get_singleton()->is_resource_internal_to_scene(parent_resource) || en->get_resource_count(parent_resource) > 1)) {
|
||||||
|
return false;
|
||||||
|
} else if (!p_check_recursive) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int parent_counter = en->get_resource_count(parent_resource);
|
||||||
|
bool above_threshold = parent_resource.is_valid() ? (en->get_resource_count(parent_resource) <= 1 && en->get_resource_count(edited_resource) > 1) : en->get_resource_count(edited_resource) > 1;
|
||||||
|
if (!p_check_recursive) {
|
||||||
|
return above_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_check_recursive && parent_counter <= 1) {
|
||||||
|
List<Ref<Resource>> nested_resources;
|
||||||
|
en->gather_resources(edited_resource, nested_resources, true, true);
|
||||||
|
|
||||||
|
for (Ref<Resource> R : nested_resources) {
|
||||||
|
// Take into account Nested External Resources.
|
||||||
|
if (en->get_resource_count(R) > 1 || !EditorNode::get_singleton()->is_resource_internal_to_scene(R)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Resource> EditorResourcePicker::_has_parent_resource() {
|
||||||
|
Node *current_node = this->get_parent();
|
||||||
|
while (current_node != nullptr) {
|
||||||
|
EditorProperty *ep = Object::cast_to<EditorProperty>(current_node);
|
||||||
|
if (ep && Object::cast_to<Resource>(ep->get_edited_object())) {
|
||||||
|
return Object::cast_to<Resource>(ep->get_edited_object());
|
||||||
|
}
|
||||||
|
current_node = current_node->get_parent();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
|
EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
|
||||||
|
make_unique_button = memnew(Button);
|
||||||
|
make_unique_button->set_flat(true);
|
||||||
|
make_unique_button->set_accessibility_name(TTRC("Number of Linked Resources."));
|
||||||
|
make_unique_button->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||||
|
make_unique_button->set_button_mask(MouseButtonMask::LEFT | MouseButtonMask::RIGHT);
|
||||||
|
make_unique_button->set_action_mode(BaseButton::ACTION_MODE_BUTTON_PRESS);
|
||||||
|
add_child(make_unique_button);
|
||||||
|
make_unique_button->connect(SceneStringName(pressed), callable_mp(this, &EditorResourcePicker::_on_unique_button_pressed));
|
||||||
|
|
||||||
assign_button = memnew(Button);
|
assign_button = memnew(Button);
|
||||||
assign_button->set_flat(true);
|
assign_button->set_flat(true);
|
||||||
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class EditorResourcePicker : public HBoxContainer {
|
|||||||
mutable HashSet<StringName> allowed_types_with_convert;
|
mutable HashSet<StringName> allowed_types_with_convert;
|
||||||
|
|
||||||
Button *assign_button = nullptr;
|
Button *assign_button = nullptr;
|
||||||
|
Button *make_unique_button = nullptr;
|
||||||
TextureRect *preview_rect = nullptr;
|
TextureRect *preview_rect = nullptr;
|
||||||
Button *edit_button = nullptr;
|
Button *edit_button = nullptr;
|
||||||
Button *quick_load_button = nullptr;
|
Button *quick_load_button = nullptr;
|
||||||
@@ -102,6 +103,7 @@ class EditorResourcePicker : public HBoxContainer {
|
|||||||
|
|
||||||
void _button_draw();
|
void _button_draw();
|
||||||
void _button_input(const Ref<InputEvent> &p_event);
|
void _button_input(const Ref<InputEvent> &p_event);
|
||||||
|
void _on_unique_button_pressed();
|
||||||
|
|
||||||
String _get_owner_path() const;
|
String _get_owner_path() const;
|
||||||
String _get_resource_type(const Ref<Resource> &p_resource) const;
|
String _get_resource_type(const Ref<Resource> &p_resource) const;
|
||||||
@@ -117,6 +119,8 @@ class EditorResourcePicker : public HBoxContainer {
|
|||||||
void _ensure_resource_menu();
|
void _ensure_resource_menu();
|
||||||
void _gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name = "") const;
|
void _gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name = "") const;
|
||||||
void _duplicate_selected_resources();
|
void _duplicate_selected_resources();
|
||||||
|
bool _is_uniqueness_enabled(bool p_check_recursive = false);
|
||||||
|
Ref<Resource> _has_parent_resource();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void _update_resource();
|
virtual void _update_resource();
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variant new_value;
|
||||||
if (p_value.get_type() == Variant::NODE_PATH) {
|
if (p_value.get_type() == Variant::NODE_PATH) {
|
||||||
NodePath path;
|
NodePath path;
|
||||||
if (node_path_target) {
|
if (node_path_target) {
|
||||||
@@ -81,7 +82,6 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
|
|||||||
n->set(name, path);
|
n->set(name, path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Variant new_value;
|
|
||||||
if (p_field.is_empty()) {
|
if (p_field.is_empty()) {
|
||||||
// whole value
|
// whole value
|
||||||
new_value = p_value;
|
new_value = p_value;
|
||||||
@@ -99,6 +99,16 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
|
|||||||
|
|
||||||
if (p_undo_redo) {
|
if (p_undo_redo) {
|
||||||
ur->add_undo_property(n, name, n->get(name));
|
ur->add_undo_property(n, name, n->get(name));
|
||||||
|
Variant old_value = n->get(p_name);
|
||||||
|
Variant::Type type = old_value.get_type();
|
||||||
|
if ((type == Variant::OBJECT || type == Variant::ARRAY || type == Variant::DICTIONARY) && old_value != new_value) {
|
||||||
|
ur->add_do_method(EditorNode::get_singleton(), "update_node_reference", old_value, n, true);
|
||||||
|
ur->add_do_method(EditorNode::get_singleton(), "update_node_reference", new_value, n, false);
|
||||||
|
// Perhaps an inefficient way of updating the resource count.
|
||||||
|
// We could go in depth and check which Resource values changed/got removed and which ones stayed the same, but this is more readable at the moment.
|
||||||
|
ur->add_undo_method(EditorNode::get_singleton(), "update_node_reference", new_value, n, true);
|
||||||
|
ur->add_undo_method(EditorNode::get_singleton(), "update_node_reference", old_value, n, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -335,6 +335,8 @@ void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, boo
|
|||||||
is_new = true;
|
is_new = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditorNode::get_singleton()->update_resource_count(p_node);
|
||||||
|
|
||||||
if (!(p_force || I->value.dirty)) {
|
if (!(p_force || I->value.dirty)) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
return;
|
return;
|
||||||
@@ -897,7 +899,6 @@ void SceneTreeEditor::_node_removed(Node *p_node) {
|
|||||||
if (p_node != get_scene_node() && !get_scene_node()->is_ancestor_of(p_node)) {
|
if (p_node != get_scene_node() && !get_scene_node()->is_ancestor_of(p_node)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node_cache.remove(p_node);
|
node_cache.remove(p_node);
|
||||||
_update_if_clean();
|
_update_if_clean();
|
||||||
}
|
}
|
||||||
@@ -968,7 +969,6 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) {
|
|||||||
}
|
}
|
||||||
node_cache.current_pinned_node = pinned_node;
|
node_cache.current_pinned_node = pinned_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
_update_node_subtree(get_scene_node(), nullptr, node_cache.force_update);
|
_update_node_subtree(get_scene_node(), nullptr, node_cache.force_update);
|
||||||
_compute_hash(get_scene_node(), last_hash);
|
_compute_hash(get_scene_node(), last_hash);
|
||||||
|
|
||||||
@@ -1225,7 +1225,6 @@ void SceneTreeEditor::_compute_hash(Node *p_node, uint64_t &hash) {
|
|||||||
void SceneTreeEditor::_reset() {
|
void SceneTreeEditor::_reset() {
|
||||||
// Stop any waiting change to tooltip.
|
// Stop any waiting change to tooltip.
|
||||||
update_node_tooltip_delay->stop();
|
update_node_tooltip_delay->stop();
|
||||||
|
|
||||||
tree->clear();
|
tree->clear();
|
||||||
node_cache.clear();
|
node_cache.clear();
|
||||||
}
|
}
|
||||||
@@ -2473,6 +2472,9 @@ void SceneTreeEditor::NodeCache::remove(Node *p_node, bool p_recursive) {
|
|||||||
|
|
||||||
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
|
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
|
||||||
if (I) {
|
if (I) {
|
||||||
|
if (editor->is_scene_tree_dock) {
|
||||||
|
EditorNode::get_singleton()->update_resource_count(I->key, true);
|
||||||
|
}
|
||||||
if (p_recursive) {
|
if (p_recursive) {
|
||||||
int cc = p_node->get_child_count(false);
|
int cc = p_node->get_child_count(false);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user