mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
Merge pull request #99700 from hpvb/scene_tree_editor_performance
Improve Scene Tree editor performance
This commit is contained in:
commit
08508d2e01
15 changed files with 818 additions and 181 deletions
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -2943,7 +2943,7 @@
|
|||
<constant name="PROPERTY_HINT_ONESHOT" value="40" enum="PropertyHint">
|
||||
Hints that a property will be changed on its own after setting, such as [member AudioStreamPlayer.playing] or [member GPUParticles3D.emitting].
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint">
|
||||
Represents the size of the [enum PropertyHint] enum.
|
||||
</constant>
|
||||
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
|
||||
|
|
|
@ -1072,6 +1072,11 @@
|
|||
Emitted when the node's editor description field changed.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="editor_state_changed">
|
||||
<description>
|
||||
Emitted when an attribute of the node that is relevant to the editor is changed. Only emitted in the editor.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="ready">
|
||||
<description>
|
||||
Emitted when the node is considered ready, after [method _ready] is called.
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
Calls the [param method] on the actual TreeItem and its children recursively. Pass parameters as a comma separated list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear_buttons">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Removes all buttons from all columns of this item.
|
||||
</description>
|
||||
</method>
|
||||
<method name="clear_custom_bg_color">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -58,6 +58,57 @@ class SceneTreeEditor : public Control {
|
|||
BUTTON_UNIQUE = 9,
|
||||
};
|
||||
|
||||
struct CachedNode {
|
||||
Node *node = nullptr;
|
||||
TreeItem *item = nullptr;
|
||||
int index = -1;
|
||||
bool dirty = true;
|
||||
bool has_moved_children = false;
|
||||
bool removed = false;
|
||||
|
||||
// Store the iterator for faster removal. This is safe as
|
||||
// HashMap never moves elements.
|
||||
HashMap<Node *, CachedNode>::Iterator cache_iterator;
|
||||
// This is safe because it gets compared to a uint8_t.
|
||||
uint16_t delete_serial = UINT16_MAX;
|
||||
|
||||
// To know whether to update children or not.
|
||||
bool can_process = false;
|
||||
|
||||
CachedNode() = delete; // Always an error.
|
||||
CachedNode(Node *p_node, TreeItem *p_item) :
|
||||
node(p_node), item(p_item) {}
|
||||
};
|
||||
|
||||
struct NodeCache {
|
||||
~NodeCache() {
|
||||
clear();
|
||||
}
|
||||
|
||||
NodeCache(SceneTreeEditor *p_editor) :
|
||||
editor(p_editor) {}
|
||||
|
||||
HashMap<Node *, CachedNode>::Iterator add(Node *p_node, TreeItem *p_item);
|
||||
HashMap<Node *, CachedNode>::Iterator get(Node *p_node, bool p_deleted_ok = true);
|
||||
void remove(Node *p_node, bool p_recursive = false);
|
||||
void mark_dirty(Node *p_node, bool p_parents = true);
|
||||
void mark_children_dirty(Node *p_node, bool p_recursive = false);
|
||||
|
||||
void delete_pending();
|
||||
void clear();
|
||||
|
||||
SceneTreeEditor *editor;
|
||||
HashMap<Node *, CachedNode> cache;
|
||||
HashSet<CachedNode *> to_delete;
|
||||
Node *current_scene_node = nullptr;
|
||||
Node *current_pinned_node = nullptr;
|
||||
bool current_has_pin = false;
|
||||
bool force_update = false;
|
||||
uint8_t delete_serial = 0;
|
||||
};
|
||||
|
||||
NodeCache node_cache;
|
||||
|
||||
Tree *tree = nullptr;
|
||||
Node *selected = nullptr;
|
||||
ObjectID instance_node;
|
||||
|
@ -77,17 +128,30 @@ class SceneTreeEditor : public Control {
|
|||
bool auto_expand_selected = true;
|
||||
bool connect_to_script_mode = false;
|
||||
bool connecting_signal = false;
|
||||
bool update_when_invisible = true;
|
||||
|
||||
int blocked;
|
||||
|
||||
void _compute_hash(Node *p_node, uint64_t &hash);
|
||||
void _reset();
|
||||
|
||||
void _update_node_path(Node *p_node, bool p_recursive = true);
|
||||
void _update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force = false);
|
||||
void _update_node(Node *p_node, TreeItem *p_item, bool p_part_of_subscene);
|
||||
void _update_if_clean();
|
||||
|
||||
void _add_nodes(Node *p_node, TreeItem *p_parent);
|
||||
void _test_update_tree();
|
||||
bool _update_filter(TreeItem *p_parent = nullptr, bool p_scroll_to_selected = false);
|
||||
bool _item_matches_all_terms(TreeItem *p_item, const PackedStringArray &p_terms);
|
||||
void _tree_changed();
|
||||
void _tree_process_mode_changed();
|
||||
|
||||
void _move_node_children(HashMap<Node *, CachedNode>::Iterator &p_I);
|
||||
void _move_node_item(TreeItem *p_parent, HashMap<Node *, CachedNode>::Iterator &p_I);
|
||||
|
||||
void _node_child_order_changed(Node *p_node);
|
||||
void _node_editor_state_changed(Node *p_node);
|
||||
void _node_added(Node *p_node);
|
||||
void _node_removed(Node *p_node);
|
||||
void _node_renamed(Node *p_node);
|
||||
|
||||
|
@ -142,6 +206,7 @@ class SceneTreeEditor : public Control {
|
|||
void _rmb_select(const Vector2 &p_pos, MouseButton p_button = MouseButton::RIGHT);
|
||||
|
||||
void _warning_changed(Node *p_for_node);
|
||||
void _update_marking_list(const HashSet<Node *> &p_marked);
|
||||
|
||||
Timer *update_timer = nullptr;
|
||||
|
||||
|
@ -182,6 +247,7 @@ public:
|
|||
void set_auto_expand_selected(bool p_auto, bool p_update_settings);
|
||||
void set_connect_to_script_mode(bool p_enable);
|
||||
void set_connecting_signal(bool p_enable);
|
||||
void set_update_when_invisible(bool p_enable);
|
||||
|
||||
Tree *get_scene_tree() { return tree; }
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ ReparentDialog::ReparentDialog() {
|
|||
add_child(vbc);
|
||||
|
||||
tree = memnew(SceneTreeEditor(false));
|
||||
tree->set_update_when_invisible(false);
|
||||
tree->set_show_enabled_subscene(true);
|
||||
tree->get_scene_tree()->connect("item_activated", callable_mp(this, &ReparentDialog::_reparent));
|
||||
vbc->add_margin_child(TTR("Select new parent:"), tree, true);
|
||||
|
|
|
@ -78,17 +78,31 @@ void SceneTreeDock::_quick_open(const String &p_file_path) {
|
|||
instantiate_scenes({ p_file_path }, scene_tree->get_selected());
|
||||
}
|
||||
|
||||
static void _restore_treeitem_custom_color(TreeItem *p_item) {
|
||||
if (!p_item) {
|
||||
return;
|
||||
}
|
||||
Color custom_color = p_item->get_meta(SNAME("custom_color"), Color(0, 0, 0, 0));
|
||||
if (custom_color != Color(0, 0, 0, 0)) {
|
||||
p_item->set_custom_color(0, custom_color);
|
||||
} else {
|
||||
p_item->clear_custom_color(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeDock::_inspect_hovered_node() {
|
||||
select_node_hovered_at_end_of_drag = true;
|
||||
Tree *tree = scene_tree->get_scene_tree();
|
||||
TreeItem *item = tree->get_item_with_metadata(node_hovered_now->get_path());
|
||||
|
||||
_restore_treeitem_custom_color(tree_item_inspected);
|
||||
tree_item_inspected = item;
|
||||
|
||||
if (item) {
|
||||
if (tree_item_inspected) {
|
||||
tree_item_inspected->clear_custom_color(0);
|
||||
}
|
||||
tree_item_inspected = item;
|
||||
tree_item_inspected->set_custom_color(0, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
|
||||
Color accent_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
tree_item_inspected->set_custom_color(0, accent_color);
|
||||
}
|
||||
|
||||
EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
|
||||
editor_history->add_object(node_hovered_now->get_instance_id());
|
||||
InspectorDock::get_inspector_singleton()->edit(node_hovered_now);
|
||||
|
@ -1716,7 +1730,7 @@ void SceneTreeDock::_notification(int p_what) {
|
|||
case NOTIFICATION_DRAG_END: {
|
||||
_reset_hovering_timer();
|
||||
if (tree_item_inspected) {
|
||||
tree_item_inspected->clear_custom_color(0);
|
||||
_restore_treeitem_custom_color(tree_item_inspected);
|
||||
tree_item_inspected = nullptr;
|
||||
} else {
|
||||
return;
|
||||
|
@ -1963,6 +1977,49 @@ bool SceneTreeDock::_update_node_path(Node *p_root_node, NodePath &r_node_path,
|
|||
return false;
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ static bool _recurse_into_property(const PropertyInfo &p_property) {
|
||||
// Only check these types for NodePaths.
|
||||
static const Variant::Type property_type_check[] = { Variant::OBJECT, Variant::NODE_PATH, Variant::ARRAY, Variant::DICTIONARY };
|
||||
|
||||
if (!(p_property.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoid otherwise acceptable types if we marked them as irrelevant.
|
||||
if (p_property.hint == PROPERTY_HINT_NO_NODEPATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Variant::Type type : property_type_check) {
|
||||
if (p_property.type == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneTreeDock::_check_object_properties_recursive(Node *p_root_node, Object *p_obj, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource) const {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
p_obj->get_property_list(&properties);
|
||||
|
||||
for (const PropertyInfo &E : properties) {
|
||||
if (!_recurse_into_property(E)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringName propertyname = E.name;
|
||||
|
||||
Variant old_variant = p_obj->get(propertyname);
|
||||
Variant updated_variant = old_variant;
|
||||
if (_check_node_path_recursive(p_root_node, updated_variant, p_renames, p_inside_resource)) {
|
||||
undo_redo->add_do_property(p_obj, propertyname, updated_variant);
|
||||
undo_redo->add_undo_property(p_obj, propertyname, old_variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SceneTreeDock::_check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource) const {
|
||||
switch (r_variant.get_type()) {
|
||||
case Variant::NODE_PATH: {
|
||||
|
@ -2027,27 +2084,18 @@ bool SceneTreeDock::_check_node_path_recursive(Node *p_root_node, Variant &r_var
|
|||
break;
|
||||
}
|
||||
|
||||
if (Object::cast_to<Material>(resource)) {
|
||||
// For performance reasons, assume that Materials don't have NodePaths in them.
|
||||
// TODO This check could be removed when String performance has improved.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!resource->is_built_in()) {
|
||||
// For performance reasons, assume that scene paths are no concern for external resources.
|
||||
break;
|
||||
}
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
resource->get_property_list(&properties);
|
||||
|
||||
for (const PropertyInfo &E : properties) {
|
||||
if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
|
||||
continue;
|
||||
}
|
||||
String propertyname = E.name;
|
||||
Variant old_variant = resource->get(propertyname);
|
||||
Variant updated_variant = old_variant;
|
||||
if (_check_node_path_recursive(p_root_node, updated_variant, p_renames, true)) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->add_do_property(resource, propertyname, updated_variant);
|
||||
undo_redo->add_undo_property(resource, propertyname, old_variant);
|
||||
}
|
||||
}
|
||||
_check_object_properties_recursive(p_root_node, resource, p_renames, true);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
|
@ -2173,22 +2221,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
|
|||
}
|
||||
|
||||
// Renaming node paths used in node properties.
|
||||
List<PropertyInfo> properties;
|
||||
p_base->get_property_list(&properties);
|
||||
|
||||
for (const PropertyInfo &E : properties) {
|
||||
if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
|
||||
continue;
|
||||
}
|
||||
String propertyname = E.name;
|
||||
Variant old_variant = p_base->get(propertyname);
|
||||
Variant updated_variant = old_variant;
|
||||
if (_check_node_path_recursive(p_base, updated_variant, p_renames)) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->add_do_property(p_base, propertyname, updated_variant);
|
||||
undo_redo->add_undo_property(p_base, propertyname, old_variant);
|
||||
}
|
||||
}
|
||||
_check_object_properties_recursive(p_base, p_base, p_renames);
|
||||
|
||||
for (int i = 0; i < p_base->get_child_count(); i++) {
|
||||
perform_node_renames(p_base->get_child(i), p_renames, r_rem_anims);
|
||||
|
|
|
@ -302,6 +302,7 @@ class SceneTreeDock : public VBoxContainer {
|
|||
static void _update_configuration_warning();
|
||||
|
||||
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const;
|
||||
void _check_object_properties_recursive(Node *p_root_node, Object *p_obj, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource = false) const;
|
||||
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource = false) const;
|
||||
bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message);
|
||||
void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
|
||||
|
|
|
@ -1258,6 +1258,18 @@ void TreeItem::deselect(int p_column) {
|
|||
_cell_deselected(p_column);
|
||||
}
|
||||
|
||||
void TreeItem::clear_buttons() {
|
||||
int i = 0;
|
||||
for (Cell &cell : cells) {
|
||||
if (!cell.buttons.is_empty()) {
|
||||
cell.buttons.clear();
|
||||
cell.cached_minimum_size_dirty = true;
|
||||
_changed_notify(i);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id, bool p_disabled, const String &p_tooltip) {
|
||||
ERR_FAIL_INDEX(p_column, cells.size());
|
||||
ERR_FAIL_COND(!p_button.is_valid());
|
||||
|
@ -1768,6 +1780,7 @@ void TreeItem::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button);
|
||||
ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("clear_buttons"), &TreeItem::clear_buttons);
|
||||
ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
|
||||
ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_index"), &TreeItem::get_button_tooltip_text);
|
||||
|
@ -3726,7 +3739,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
|
|||
_determine_hovered_item();
|
||||
|
||||
bool rtl = is_layout_rtl();
|
||||
if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) {
|
||||
if (pressing_for_editor && popup_pressing_edited_item && !popup_pressing_edited_item->cells.is_empty() && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) {
|
||||
/* This needs to happen now, because the popup can be closed when pressing another item, and must remain the popup edited item until it actually closes */
|
||||
popup_edited_item = popup_pressing_edited_item;
|
||||
popup_edited_item_col = popup_pressing_edited_item_column;
|
||||
|
|
|
@ -277,6 +277,7 @@ public:
|
|||
void set_icon_max_width(int p_column, int p_max);
|
||||
int get_icon_max_width(int p_column) const;
|
||||
|
||||
void clear_buttons();
|
||||
void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = "");
|
||||
int get_button_count(int p_column) const;
|
||||
String get_button_tooltip_text(int p_column, int p_index) const;
|
||||
|
|
|
@ -678,6 +678,8 @@ void Node::set_process_mode(ProcessMode p_mode) {
|
|||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
get_tree()->emit_signal(SNAME("tree_process_mode_changed"));
|
||||
}
|
||||
|
||||
_emit_editor_state_changed();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2163,6 +2165,7 @@ void Node::set_unique_name_in_owner(bool p_enabled) {
|
|||
}
|
||||
|
||||
update_configuration_warnings();
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
bool Node::is_unique_name_in_owner() const {
|
||||
|
@ -2200,6 +2203,8 @@ void Node::set_owner(Node *p_owner) {
|
|||
if (data.unique_name_in_owner) {
|
||||
_acquire_unique_name_in_owner();
|
||||
}
|
||||
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
Node *Node::get_owner() const {
|
||||
|
@ -2383,6 +2388,9 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) {
|
|||
gd.persistent = p_persistent;
|
||||
|
||||
data.grouped[p_identifier] = gd;
|
||||
if (p_persistent) {
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void Node::remove_from_group(const StringName &p_identifier) {
|
||||
|
@ -2393,11 +2401,21 @@ void Node::remove_from_group(const StringName &p_identifier) {
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool persistent = E->value.persistent;
|
||||
#endif
|
||||
|
||||
if (data.tree) {
|
||||
data.tree->remove_from_group(E->key, this);
|
||||
}
|
||||
|
||||
data.grouped.remove(E);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (persistent) {
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TypedArray<StringName> Node::_get_groups() const {
|
||||
|
@ -2560,6 +2578,7 @@ Ref<Tween> Node::create_tween() {
|
|||
void Node::set_scene_file_path(const String &p_scene_file_path) {
|
||||
ERR_THREAD_GUARD
|
||||
data.scene_file_path = p_scene_file_path;
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
String Node::get_scene_file_path() const {
|
||||
|
@ -2592,6 +2611,8 @@ void Node::set_editable_instance(Node *p_node, bool p_editable) {
|
|||
} else {
|
||||
p_node->data.editable_instance = true;
|
||||
}
|
||||
|
||||
p_node->_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
bool Node::is_editable_instance(const Node *p_node) const {
|
||||
|
@ -2702,6 +2723,7 @@ Ref<SceneState> Node::get_scene_instance_state() const {
|
|||
void Node::set_scene_inherited_state(const Ref<SceneState> &p_state) {
|
||||
ERR_THREAD_GUARD
|
||||
data.inherited_state = p_state;
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
Ref<SceneState> Node::get_scene_inherited_state() const {
|
||||
|
@ -2950,6 +2972,14 @@ void Node::remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Re
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::_emit_editor_state_changed() {
|
||||
// This is required for the SceneTreeEditor to properly keep track of when an update is needed.
|
||||
// This signal might be expensive and not needed for anything outside of the editor.
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
emit_signal(SNAME("editor_state_changed"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Duplicate node's properties.
|
||||
|
@ -3843,6 +3873,7 @@ void Node::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("child_order_changed"));
|
||||
ADD_SIGNAL(MethodInfo("replacing_by", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("editor_description_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("editor_state_changed"));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner");
|
||||
|
@ -3966,11 +3997,13 @@ bool Node::has_meta(const StringName &p_name) const {
|
|||
void Node::set_meta(const StringName &p_name, const Variant &p_value) {
|
||||
ERR_THREAD_GUARD;
|
||||
Object::set_meta(p_name, p_value);
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
void Node::remove_meta(const StringName &p_name) {
|
||||
ERR_THREAD_GUARD;
|
||||
Object::remove_meta(p_name);
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
|
||||
Variant Node::get_meta(const StringName &p_name, const Variant &p_default) const {
|
||||
|
@ -4020,12 +4053,33 @@ void Node::get_signals_connected_to_this(List<Connection> *p_connections) const
|
|||
|
||||
Error Node::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
|
||||
ERR_THREAD_GUARD_V(ERR_INVALID_PARAMETER);
|
||||
return Object::connect(p_signal, p_callable, p_flags);
|
||||
|
||||
Error retval = Object::connect(p_signal, p_callable, p_flags);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_flags & CONNECT_PERSIST) {
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void Node::disconnect(const StringName &p_signal, const Callable &p_callable) {
|
||||
ERR_THREAD_GUARD;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Already under thread guard, don't check again.
|
||||
int old_connection_count = Object::get_persistent_signal_connection_count();
|
||||
#endif
|
||||
|
||||
Object::disconnect(p_signal, p_callable);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
int new_connection_count = Object::get_persistent_signal_connection_count();
|
||||
if (old_connection_count != new_connection_count) {
|
||||
_emit_editor_state_changed();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Node::is_connected(const StringName &p_signal, const Callable &p_callable) const {
|
||||
|
|
|
@ -331,6 +331,13 @@ private:
|
|||
Variant _call_deferred_thread_group_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
Variant _call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
// Editor only signal to keep the SceneTreeEditor in sync.
|
||||
#ifdef TOOLS_ENABLED
|
||||
void _emit_editor_state_changed();
|
||||
#else
|
||||
void _emit_editor_state_changed() {}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void _block() { data.blocked++; }
|
||||
void _unblock() { data.blocked--; }
|
||||
|
|
|
@ -1740,7 +1740,7 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
}
|
||||
|
||||
for (int i = 0; i < surfaces.size(); i++) {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_EDITOR));
|
||||
if (surfaces[i].is_2d) {
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR));
|
||||
} else {
|
||||
|
@ -2308,10 +2308,10 @@ void ArrayMesh::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
|
||||
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NO_NODEPATH, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NO_NODEPATH, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh");
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue