瀏覽代碼

Show detach script button when added via inspector

Fixes #107059.

The SceneTreeDock was not tracking script changes in selected nodes in any capacity as far as I could assess. To do
that, my solution essentially connects the "script_changed" signal from selected nodes to
"SceneTreeDock::_update_script_button()" whenever the selection changes. It actually queues the update to make sure it
happens only once no matter how many nodes are selected.

However, only connecting that signal would leave previously selected nodes with a signal connection that should no
longer exist. To properly disconnect previously selected nodes, we have to store the list of currently selected nodes so
we can disconnect them when the selection changes.

The commit also includes some improvements to the SceneTreeDock class:

1. Remove unnecessary initialization in SceneTreeDock

This field is already initialized in the line that declares it. As such, initializing it on the constructor as well as
is redundant.

2. Queue script button updates in scene tree dock

Since we now have the option to defer the script button update and make sure it only runs once per frame, it's always
best to use the queued version of the update from a performance perspective. I'm not entirely sure if there could be any
unexpected side effects but it is a minor self-contained UI update, so it's likely a relatively safe change.

The replacement includes the bindings since it is a requirement for the other replacements in the class to work
(UndoRedo needs their method names registered in the class DB). It should be OK to remove the old non-queued bindings
too even though they are accessible in the public API because it is a "unofficial" method starting with an underscore.
Wilson Kazuo Mizutani 2 月之前
父節點
當前提交
b400633dc2
共有 2 個文件被更改,包括 36 次插入11 次删除
  1. 33 11
      editor/docks/scene_tree_dock.cpp
  2. 3 0
      editor/docks/scene_tree_dock.h

+ 33 - 11
editor/docks/scene_tree_dock.cpp

@@ -789,8 +789,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 				}
 			}
 
-			undo_redo->add_do_method(this, "_update_script_button");
-			undo_redo->add_undo_method(this, "_update_script_button");
+			undo_redo->add_do_method(this, "_queue_update_script_button");
+			undo_redo->add_undo_method(this, "_queue_update_script_button");
 
 			undo_redo->commit_action();
 		} break;
@@ -2578,8 +2578,8 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
 		undo_redo->add_undo_method(E, "set_script", existing);
 		undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E);
 		undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E);
-		undo_redo->add_do_method(this, "_update_script_button");
-		undo_redo->add_undo_method(this, "_update_script_button");
+		undo_redo->add_do_method(this, "_queue_update_script_button");
+		undo_redo->add_undo_method(this, "_queue_update_script_button");
 	}
 	undo_redo->commit_action();
 
@@ -2587,7 +2587,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
 	Object *edited_object = InspectorDock::get_inspector_singleton()->get_edited_object();
 
 	_push_item(p_script.ptr());
-	_update_script_button();
+	_queue_update_script_button();
 
 	InspectorDock::get_inspector_singleton()->edit(edited_object);
 }
@@ -2901,6 +2901,16 @@ void SceneTreeDock::_update_script_button() {
 	button_create_script->set_visible(can_create_script);
 	button_detach_script->set_visible(can_detach_script);
 	button_extend_script->set_visible(can_extend_script);
+
+	update_script_button_queued = false;
+}
+
+void SceneTreeDock::_queue_update_script_button() {
+	if (update_script_button_queued) {
+		return;
+	}
+	update_script_button_queued = true;
+	callable_mp(this, &SceneTreeDock::_update_script_button).call_deferred();
 }
 
 void SceneTreeDock::_selection_changed() {
@@ -2914,7 +2924,20 @@ void SceneTreeDock::_selection_changed() {
 		_push_item(nullptr);
 	}
 
-	_update_script_button();
+	// Untrack script changes in previously selected nodes.
+	for (Node *node : node_previous_selection) {
+		node->disconnect(CoreStringName(script_changed), callable_mp(this, &SceneTreeDock::_queue_update_script_button));
+	}
+
+	// Track script changes in newly selected nodes.
+	node_previous_selection.clear();
+	node_previous_selection.reserve(editor_selection->get_selection().size());
+	for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
+		Node *node = E.key;
+		node_previous_selection.push_back(node);
+		node->connect(CoreStringName(script_changed), callable_mp(this, &SceneTreeDock::_queue_update_script_button));
+	}
+	_queue_update_script_button();
 }
 
 Node *SceneTreeDock::_do_create(Node *p_parent) {
@@ -3682,8 +3705,8 @@ void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) {
 		undo_redo->add_undo_method(n, "set_script", n->get_script());
 		undo_redo->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", n);
 		undo_redo->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", n);
-		undo_redo->add_do_method(this, "_update_script_button");
-		undo_redo->add_undo_method(this, "_update_script_button");
+		undo_redo->add_do_method(this, "_queue_update_script_button");
+		undo_redo->add_undo_method(this, "_queue_update_script_button");
 		undo_redo->commit_action();
 	}
 }
@@ -4456,7 +4479,7 @@ void SceneTreeDock::_feature_profile_changed() {
 		profile_allow_script_editing = true;
 	}
 
-	_update_script_button();
+	_queue_update_script_button();
 }
 
 void SceneTreeDock::_clear_clipboard() {
@@ -4631,7 +4654,7 @@ void SceneTreeDock::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_reparent_nodes_to_root"), &SceneTreeDock::_reparent_nodes_to_root);
 	ClassDB::bind_method(D_METHOD("_reparent_nodes_to_paths_with_transform_and_name"), &SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name);
 
-	ClassDB::bind_method(D_METHOD("_update_script_button"), &SceneTreeDock::_update_script_button);
+	ClassDB::bind_method(D_METHOD("_queue_update_script_button"), &SceneTreeDock::_queue_update_script_button);
 
 	ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate);
 	ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor);
@@ -4653,7 +4676,6 @@ void SceneTreeDock::_update_configuration_warning() {
 SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) {
 	singleton = this;
 	set_name("Scene");
-	edited_scene = nullptr;
 	editor_data = &p_editor_data;
 	editor_selection = p_editor_selection;
 	scene_root = p_scene_root;

+ 3 - 0
editor/docks/scene_tree_dock.h

@@ -135,6 +135,8 @@ class SceneTreeDock : public VBoxContainer {
 
 	EditorData *editor_data = nullptr;
 	EditorSelection *editor_selection = nullptr;
+	LocalVector<Node *> node_previous_selection;
+	bool update_script_button_queued = false;
 
 	List<Node *> node_clipboard;
 	HashSet<Node *> node_clipboard_edited_scene_owned;
@@ -248,6 +250,7 @@ class SceneTreeDock : public VBoxContainer {
 	bool _validate_no_instance();
 	void _selection_changed();
 	void _update_script_button();
+	void _queue_update_script_button();
 
 	void _fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames);
 	bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;