Skip to content

Commit

Permalink
Merge pull request #63282 from V-Sekai/disable_foreign_resource_edits
Browse files Browse the repository at this point in the history
Disable editing properties in foreign resources
  • Loading branch information
akien-mga authored Aug 24, 2022
2 parents 7055200 + dd814a0 commit 792f7cc
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 101 deletions.
4 changes: 2 additions & 2 deletions doc/classes/EditorResourcePicker.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@
</signal>
<signal name="resource_selected">
<param index="0" name="resource" type="Resource" />
<param index="1" name="edit" type="bool" />
<param index="1" name="inspect" type="bool" />
<description>
Emitted when the resource value was set and user clicked to edit it. When [param edit] is [code]true[/code], the signal was caused by the context menu "Edit" option.
Emitted when the resource value was set and user clicked to edit it. When [param inspect] is [code]true[/code], the signal was caused by the context menu "Edit" or "Inspect" option.
</description>
</signal>
</signals>
Expand Down
8 changes: 4 additions & 4 deletions editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class AnimationTrackKeyEdit : public Object {
return true;
}

bool _read_only() {
bool _is_read_only() {
return animation_read_only;
}

Expand All @@ -70,7 +70,7 @@ class AnimationTrackKeyEdit : public Object {
ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector);
ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path);
ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo);
ClassDB::bind_method(D_METHOD("_read_only"), &AnimationTrackKeyEdit::_read_only);
ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only);
}

void _fix_node_path(Variant &value) {
Expand Down Expand Up @@ -727,7 +727,7 @@ class AnimationMultiTrackKeyEdit : public Object {
return true;
}

bool _read_only() {
bool _is_read_only() {
return animation_read_only;
}

Expand All @@ -737,7 +737,7 @@ class AnimationMultiTrackKeyEdit : public Object {
ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector);
ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path);
ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo);
ClassDB::bind_method(D_METHOD("_read_only"), &AnimationMultiTrackKeyEdit::_read_only);
ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only);
}

void _fix_node_path(Variant &value, NodePath &base) {
Expand Down
1 change: 1 addition & 0 deletions editor/debugger/editor_debugger_inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void EditorDebuggerRemoteObject::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant);
ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear);
ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id);
ClassDB::bind_method(D_METHOD("_is_read_only"), &EditorDebuggerRemoteObject::_is_read_only);

ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
}
Expand Down
1 change: 1 addition & 0 deletions editor/debugger/editor_debugger_inspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class EditorDebuggerRemoteObject : public Object {
HashMap<StringName, Variant> prop_values;

ObjectID get_remote_object_id() { return remote_object_id; };
bool _is_read_only() { return true; };
String get_title();

Variant get_variant(const StringName &p_name);
Expand Down
21 changes: 18 additions & 3 deletions editor/editor_inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2628,15 +2628,28 @@ void EditorInspector::update_tree() {
valid_plugins.push_back(inspector_plugins[i]);
}

// Decide if properties should be drawn with the warning color (yellow).
// Decide if properties should be drawn with the warning color (yellow),
// or if the whole object should be considered read-only.
bool draw_warning = false;
bool all_read_only = false;
if (is_inside_tree()) {
if (object->has_method("_is_read_only")) {
all_read_only = object->call("_is_read_only");
}

Node *nod = Object::cast_to<Node>(object);
Node *es = EditorNode::get_singleton()->get_edited_scene();
if (nod && es != nod && nod->get_owner() != es) {
// Draw in warning color edited nodes that are not in the currently edited scene,
// as changes may be lost in the future.
draw_warning = true;
} else {
if (!all_read_only) {
Resource *res = Object::cast_to<Resource>(object);
if (res) {
all_read_only = EditorNode::get_singleton()->is_resource_read_only(res);
}
}
}
}

Expand Down Expand Up @@ -3179,7 +3192,6 @@ void EditorInspector::update_tree() {
ep->property_usage = p.usage;
//and set label?
}

if (!editors[i].label.is_empty()) {
ep->set_label(editors[i].label);
} else {
Expand All @@ -3206,7 +3218,7 @@ void EditorInspector::update_tree() {
ep->set_checkable(checkable);
ep->set_checked(checked);
ep->set_keying(keying);
ep->set_read_only(property_read_only);
ep->set_read_only(property_read_only || all_read_only);
ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
}

Expand Down Expand Up @@ -3253,6 +3265,9 @@ void EditorInspector::update_tree() {
add_md->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
add_md->connect(SNAME("pressed"), callable_mp(this, &EditorInspector::_show_add_meta_dialog));
main_vbox->add_child(add_md);
if (all_read_only) {
add_md->set_disabled(true);
}
}

// Get the lists of to add at the end.
Expand Down
91 changes: 76 additions & 15 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1250,7 +1250,9 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St
}

void EditorNode::save_resource(const Ref<Resource> &p_resource) {
if (p_resource->get_path().is_resource_file()) {
// If the resource has been imported, ask the user to use a different path in order to save it.
String path = p_resource->get_path();
if (path.is_resource_file() && !FileAccess::exists(path + ".import")) {
save_resource_in_path(p_resource, p_resource->get_path());
} else {
save_resource_as(p_resource);
Expand All @@ -1260,11 +1262,18 @@ void EditorNode::save_resource(const Ref<Resource> &p_resource) {
void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) {
{
String path = p_resource->get_path();
int srpos = path.find("::");
if (srpos != -1) {
String base = path.substr(0, srpos);
if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) {
show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first."));
if (!path.is_resource_file()) {
int srpos = path.find("::");
if (srpos != -1) {
String base = path.substr(0, srpos);
if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) {
show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first."));
return;
}
}
} else {
if (FileAccess::exists(path + ".import")) {
show_warning(TTR("This resource can't be saved because it was imported from another file. Make it unique first."));
return;
}
}
Expand Down Expand Up @@ -2223,7 +2232,14 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
bool stay_in_script_editor_on_node_selected = bool(EDITOR_GET("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected"));
bool skip_main_plugin = false;

String editable_warning; // None by default.
String editable_info; // None by default.
bool info_is_warning = false;

if (current_obj->has_method("_is_read_only")) {
if (current_obj->call("_is_read_only")) {
editable_info = TTR("This object is marked as read-only, so it's not editable.");
}
}

if (is_resource) {
Resource *current_res = Object::cast_to<Resource>(current_obj);
Expand All @@ -2237,16 +2253,25 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
int subr_idx = current_res->get_path().find("::");
if (subr_idx != -1) {
String base_path = current_res->get_path().substr(0, subr_idx);
if (FileAccess::exists(base_path + ".import")) {
editable_warning = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
if (!base_path.is_resource_file()) {
if (FileAccess::exists(base_path + ".import")) {
if (get_edited_scene() && get_edited_scene()->get_scene_file_path() == base_path) {
info_is_warning = true;
}
editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
} else {
if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") {
editable_info = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it must be made inside the original scene.");
}
}
} else {
if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") {
editable_warning = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it won't be kept when saving the current scene.");
if (FileAccess::exists(base_path + ".import")) {
editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
}
}
} else if (current_res->get_path().is_resource_file()) {
if (FileAccess::exists(current_res->get_path() + ".import")) {
editable_warning = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import.");
editable_info = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import.");
}
}
} else if (is_node) {
Expand All @@ -2270,15 +2295,16 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
if (get_edited_scene() && !get_edited_scene()->get_scene_file_path().is_empty()) {
String source_scene = get_edited_scene()->get_scene_file_path();
if (FileAccess::exists(source_scene + ".import")) {
editable_warning = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
editable_info = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
info_is_warning = true;
}
}

} else {
Node *selected_node = nullptr;

if (current_obj->is_class("EditorDebuggerRemoteObject")) {
editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow.");
editable_info = TTR("This is a remote object, so it's not editable.\nPlease read the documentation relevant to debugging to better understand this workflow.");
disable_folding = true;
} else if (current_obj->is_class("MultiNodeEdit")) {
Node *scene = get_edited_scene();
Expand Down Expand Up @@ -2313,7 +2339,10 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
InspectorDock::get_inspector_singleton()->update_tree();
}

InspectorDock::get_singleton()->set_warning(editable_warning);
InspectorDock::get_singleton()->set_info(
info_is_warning ? TTR("Changes may be lost!") : TTR("This object is read-only."),
editable_info,
info_is_warning);

if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) {
InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding);
Expand Down Expand Up @@ -3828,6 +3857,37 @@ void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) {
InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource);
}

bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) {
ERR_FAIL_COND_V(p_resource.is_null(), false);

String path = p_resource->get_path();
if (!path.is_resource_file()) {
// If the resource name contains '::', that means it is a subresource embedded in another resource.
int srpos = path.find("::");
if (srpos != -1) {
String base = path.substr(0, srpos);
// If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene.
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
return true;
}
} else {
// If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only.
if (FileAccess::exists(base + ".import")) {
return true;
}
}
}
} else {
// The resource is not a subresource, but if it has an .import file, it's imported so treat it as read only.
if (FileAccess::exists(path + ".import")) {
return true;
}
}

return false;
}

void EditorNode::request_instance_scene(const String &p_path) {
SceneTreeDock::get_singleton()->instantiate(p_path);
}
Expand Down Expand Up @@ -5837,6 +5897,7 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene);
ClassDB::bind_method("open_request", &EditorNode::open_request);
ClassDB::bind_method("edit_foreign_resource", &EditorNode::edit_foreign_resource);
ClassDB::bind_method("is_resource_read_only", &EditorNode::is_resource_read_only);
ClassDB::bind_method("_close_messages", &EditorNode::_close_messages);
ClassDB::bind_method("_show_messages", &EditorNode::_show_messages);

Expand Down
2 changes: 2 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,8 @@ class EditorNode : public Node {
void open_request(const String &p_path);
void edit_foreign_resource(Ref<Resource> p_resource);

bool is_resource_read_only(Ref<Resource> p_resource);

bool is_changing_scene() const;

Control *get_main_control();
Expand Down
16 changes: 10 additions & 6 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3653,20 +3653,24 @@ void EditorPropertyResource::_set_read_only(bool p_read_only) {
resource_picker->set_editable(!p_read_only);
};

void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_edit) {
void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {
if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) {
String parent = p_resource->get_path().get_slice("::", 0);
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);

if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) {
// If the resource belongs to another scene, edit it in that scene instead.
EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource);
return;
if (p_inspect) {
if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) {
// If the resource belongs to another (non-imported) scene, edit it in that scene instead.
if (!FileAccess::exists(parent + ".import")) {
EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource);
return;
}
}
}
}

if (!p_edit && use_sub_inspector) {
if (!p_inspect && use_sub_inspector) {
bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property());
get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold);
update_property();
Expand Down
2 changes: 1 addition & 1 deletion editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ class EditorPropertyResource : public EditorProperty {
bool updating_theme = false;
bool opened_editor = false;

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

void _viewport_selected(const NodePath &p_path);
Expand Down
Loading

0 comments on commit 792f7cc

Please sign in to comment.