diff --git a/core/object/object.h b/core/object/object.h index da8e8d1b86e..7467a86732c 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -89,6 +89,7 @@ enum PropertyHint { PROPERTY_HINT_DICTIONARY_TYPE, PROPERTY_HINT_TOOL_BUTTON, PROPERTY_HINT_ONESHOT, ///< the property will be changed by self after setting, such as AudioStreamPlayer.playing, Particles.emitting. + PROPERTY_HINT_NO_NODEPATH, /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock. PROPERTY_HINT_MAX, }; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index cc5c1e1e682..e7f043fff8b 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2943,7 +2943,7 @@ Hints that a property will be changed on its own after setting, such as [member AudioStreamPlayer.playing] or [member GPUParticles3D.emitting]. - + Represents the size of the [enum PropertyHint] enum. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 1e12d619e22..cbf6d048ebb 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1068,6 +1068,11 @@ Emitted when the node's editor description field changed. + + + Emitted when an attribute of the node that is relevant to the editor is changed. Only emitted in the editor. + + Emitted when the node is considered ready, after [method _ready] is called. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index d04a6f7316d..cce31b4bb1d 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -36,6 +36,12 @@ Calls the [param method] on the actual TreeItem and its children recursively. Pass parameters as a comma separated list. + + + + Removes all buttons from all columns of this item. + + diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 53b1f2a759a..f06765e982a 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -735,6 +735,7 @@ ConnectDialog::ConnectDialog() { from_signal->set_editable(false); tree = memnew(SceneTreeEditor(false)); + tree->set_update_when_invisible(false); tree->set_connecting_signal(true); tree->set_show_enabled_subscene(true); tree->set_v_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 2d51716c88d..154168f7716 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -61,7 +61,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } if (connect_to_script_mode) { - return; //don't do anything in this mode + return; // Don't do anything in this mode. } TreeItem *item = Object::cast_to(p_item); @@ -152,7 +152,8 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i const String line = all_warnings.substr(start, end - start); lines.append(line); } - all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); // We don't want the first two newlines. + // We don't want the first two newlines. + all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); warning->set_text(all_warnings); warning->popup_centered(); @@ -217,12 +218,35 @@ void SceneTreeEditor::_toggle_visible(Node *p_node) { } } -void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { +void SceneTreeEditor::_update_node_path(Node *p_node, bool p_recursive) { if (!p_node) { return; } - // only owned nodes are editable, since nodes can create their own (manually owned) child nodes, + HashMap::Iterator I = node_cache.get(p_node); + if (!I) { + return; + } + + I->value.item->set_metadata(0, p_node->get_path()); + + if (!p_recursive) { + return; + } + + int cc = p_node->get_child_count(false); + for (int i = 0; i < cc; i++) { + Node *c = p_node->get_child(i, false); + _update_node_path(c, p_recursive); + } +} + +void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force) { + if (!p_node) { + return; + } + + // Only owned nodes are editable, since nodes can create their own (manually owned) child nodes, // which the editor needs not to know about. bool part_of_subscene = false; @@ -230,42 +254,152 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (!display_foreign && p_node->get_owner() != get_scene_node() && p_node != get_scene_node()) { if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) { part_of_subscene = true; - //allow + // Allow. } else { + // Stale node, remove recursively. + node_cache.remove(p_node, true); return; } } else { part_of_subscene = p_node != get_scene_node() && get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node)) >= 0; } - TreeItem *item = tree->create_item(p_parent); + HashMap::Iterator I = node_cache.get(p_node); + TreeItem *item = nullptr; - item->set_text(0, p_node->get_name()); - item->set_text_overrun_behavior(0, TextServer::OVERRUN_NO_TRIMMING); - if (can_rename && !part_of_subscene) { - item->set_editable(0, true); + bool is_new = false; + + if (I) { + item = I->value.item; + TreeItem *current_parent = item->get_parent(); + + // Our parent might be re-created because of a changed type. + if (p_parent && p_parent != current_parent) { + if (current_parent) { + current_parent->remove_child(item); + } + p_parent->add_child(item); + I->value.removed = false; + _move_node_item(p_parent, I); + } + + if (I->value.has_moved_children) { + _move_node_children(I); + } + } else { + int index = -1; + // Check to see if there is a root node for us to reuse. + if (!p_parent) { + item = tree->get_root(); + if (!item) { + item = tree->create_item(nullptr); + index = 0; + } + } else { + index = p_node->get_index(false); + item = tree->create_item(p_parent, index); + } + + I = node_cache.add(p_node, item); + I->value.index = index; + is_new = true; + } + + if (!(p_force || I->value.dirty)) { + // Nothing to do. + return; + } + + _update_node(p_node, item, part_of_subscene); + I->value.dirty = false; + I->value.can_process = p_node->can_process(); + + // Force update all our children if we are new or if we were forced to update. + bool force_update_children = p_force || is_new; + // Update all our children. + for (int i = 0; i < p_node->get_child_count(false); i++) { + _update_node_subtree(p_node->get_child(i, false), item, force_update_children); + } + + if (valid_types.size()) { + bool valid = false; + for (const StringName &E : valid_types) { + if (p_node->is_class(E) || + EditorNode::get_singleton()->is_object_of_custom_type(p_node, E)) { + valid = true; + break; + } else { + Ref