Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/scene_tree_editor.cpp
20829 views
1
/**************************************************************************/
2
/* scene_tree_editor.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "scene_tree_editor.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/object/script_language.h"
35
#include "editor/animation/animation_player_editor_plugin.h"
36
#include "editor/docks/editor_dock_manager.h"
37
#include "editor/docks/groups_dock.h"
38
#include "editor/docks/signals_dock.h"
39
#include "editor/editor_node.h"
40
#include "editor/editor_string_names.h"
41
#include "editor/editor_undo_redo_manager.h"
42
#include "editor/file_system/editor_file_system.h"
43
#include "editor/gui/filter_line_edit.h"
44
#include "editor/scene/canvas_item_editor_plugin.h"
45
#include "editor/script/script_editor_plugin.h"
46
#include "editor/settings/editor_settings.h"
47
#include "editor/themes/editor_scale.h"
48
#include "scene/2d/node_2d.h"
49
#include "scene/gui/check_box.h"
50
#include "scene/gui/check_button.h"
51
#include "scene/gui/flow_container.h"
52
#include "scene/gui/label.h"
53
#include "scene/gui/texture_rect.h"
54
#include "scene/main/window.h"
55
#include "scene/resources/packed_scene.h"
56
57
Node *SceneTreeEditor::get_scene_node() const {
58
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
59
60
return get_tree()->get_edited_scene_root();
61
}
62
63
PackedStringArray SceneTreeEditor::_get_node_configuration_warnings(Node *p_node) {
64
PackedStringArray warnings = p_node->get_configuration_warnings();
65
if (p_node == get_scene_node()) {
66
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
67
if (node_2d) {
68
// Note: Warn for Node2D but not all CanvasItems, don't warn for Control nodes.
69
// Control nodes may have reasons to use a transformed root node like anchors.
70
if (!node_2d->get_position().is_zero_approx()) {
71
warnings.append(TTR("The root node of a scene is recommended to not be transformed, since instances of the scene will usually override this. Reset the transform and reload the scene to remove this warning."));
72
}
73
}
74
Node3D *node_3d = Object::cast_to<Node3D>(p_node);
75
if (node_3d) {
76
if (!node_3d->get_position().is_zero_approx()) {
77
warnings.append(TTR("The root node of a scene is recommended to not be transformed, since instances of the scene will usually override this. Reset the transform and reload the scene to remove this warning."));
78
}
79
}
80
}
81
return warnings;
82
}
83
84
PackedStringArray SceneTreeEditor::_get_node_accessibility_configuration_warnings(Node *p_node) {
85
PackedStringArray warnings = p_node->get_accessibility_configuration_warnings();
86
87
return warnings;
88
}
89
90
void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
91
if (p_button != MouseButton::LEFT) {
92
return;
93
}
94
95
if (connect_to_script_mode) {
96
return; // Don't do anything in this mode.
97
}
98
99
TreeItem *item = Object::cast_to<TreeItem>(p_item);
100
ERR_FAIL_NULL(item);
101
102
NodePath np = item->get_metadata(0);
103
104
Node *n = get_node(np);
105
ERR_FAIL_NULL(n);
106
107
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
108
if (p_id == BUTTON_SUBSCENE) {
109
if (n == get_scene_node()) {
110
if (n && n->get_scene_inherited_state().is_valid()) {
111
emit_signal(SNAME("open"), n->get_scene_inherited_state()->get_path());
112
}
113
} else {
114
emit_signal(SNAME("open"), n->get_scene_file_path());
115
}
116
} else if (p_id == BUTTON_SCRIPT) {
117
Ref<Script> script_typed = n->get_script();
118
if (script_typed.is_valid()) {
119
emit_signal(SNAME("open_script"), script_typed);
120
}
121
122
} else if (p_id == BUTTON_VISIBILITY) {
123
undo_redo->create_action(TTR("Toggle Visible"));
124
_toggle_visible(n);
125
List<Node *> selection = editor_selection->get_top_selected_node_list();
126
if (selection.size() > 1 && selection.find(n) != nullptr) {
127
for (Node *nv : selection) {
128
ERR_FAIL_NULL(nv);
129
if (nv == n) {
130
continue;
131
}
132
_toggle_visible(nv);
133
}
134
}
135
undo_redo->commit_action();
136
} else if (p_id == BUTTON_LOCK) {
137
undo_redo->create_action(TTR("Unlock Node"));
138
undo_redo->add_do_method(n, "remove_meta", "_edit_lock_");
139
undo_redo->add_undo_method(n, "set_meta", "_edit_lock_", true);
140
undo_redo->add_do_method(this, "emit_signal", "node_changed");
141
undo_redo->add_undo_method(this, "emit_signal", "node_changed");
142
undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "emit_signal", "item_lock_status_changed");
143
undo_redo->add_undo_method(CanvasItemEditor::get_singleton(), "emit_signal", "item_lock_status_changed");
144
undo_redo->commit_action();
145
} else if (p_id == BUTTON_PIN) {
146
if (n->is_class("AnimationMixer")) {
147
AnimationPlayerEditor::get_singleton()->unpin();
148
_update_tree();
149
}
150
151
} else if (p_id == BUTTON_GROUP) {
152
undo_redo->create_action(TTR("Ungroup Children"));
153
154
if (n->is_class("CanvasItem") || n->is_class("Node3D")) {
155
undo_redo->add_do_method(n, "remove_meta", "_edit_group_");
156
undo_redo->add_undo_method(n, "set_meta", "_edit_group_", true);
157
undo_redo->add_do_method(this, "emit_signal", "node_changed");
158
undo_redo->add_undo_method(this, "emit_signal", "node_changed");
159
undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "emit_signal", "item_lock_status_changed");
160
undo_redo->add_undo_method(CanvasItemEditor::get_singleton(), "emit_signal", "item_lock_status_changed");
161
}
162
undo_redo->commit_action();
163
} else if (p_id == BUTTON_WARNING) {
164
PackedStringArray warnings = _get_node_configuration_warnings(n);
165
if (accessibility_warnings) {
166
warnings.append_array(_get_node_accessibility_configuration_warnings(n));
167
}
168
if (warnings.is_empty()) {
169
return;
170
}
171
172
// Improve looks on tooltip, extra spacing on non-bullet point newlines.
173
const String bullet_point = U"• ";
174
String all_warnings;
175
for (const String &w : warnings) {
176
all_warnings += "\n" + bullet_point + w;
177
}
178
179
// Limit the line width while keeping some padding.
180
// It is not efficient, but it does not have to be.
181
const PackedInt32Array boundaries = TS->string_get_word_breaks(all_warnings, "", 80);
182
PackedStringArray lines;
183
for (int i = 0; i < boundaries.size(); i += 2) {
184
const int start = boundaries[i];
185
const int end = boundaries[i + 1];
186
const String line = all_warnings.substr(start, end - start);
187
lines.append(line);
188
}
189
// We don't want the first two newlines.
190
all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2);
191
192
warning->set_text(all_warnings);
193
warning->popup_centered();
194
195
} else if (p_id == BUTTON_SIGNALS) {
196
editor_selection->clear();
197
editor_selection->add_node(n);
198
199
set_selected(n);
200
201
EditorDockManager::get_singleton()->focus_dock(SignalsDock::get_singleton());
202
} else if (p_id == BUTTON_GROUPS) {
203
editor_selection->clear();
204
editor_selection->add_node(n);
205
206
set_selected(n);
207
208
EditorDockManager::get_singleton()->focus_dock(GroupsDock::get_singleton());
209
} else if (p_id == BUTTON_UNIQUE) {
210
bool ask_before_revoking_unique_name = EDITOR_GET("docks/scene_tree/ask_before_revoking_unique_name");
211
revoke_node = n;
212
if (ask_before_revoking_unique_name) {
213
String msg = vformat(TTR("Revoke unique name for node \"%s\"?"), n->get_name());
214
ask_before_revoke_checkbox->set_pressed(false);
215
revoke_dialog_label->set_text(msg);
216
revoke_dialog->reset_size();
217
revoke_dialog->popup_centered();
218
} else {
219
_revoke_unique_name();
220
}
221
}
222
}
223
224
void SceneTreeEditor::_update_ask_before_revoking_unique_name() {
225
if (ask_before_revoke_checkbox->is_pressed()) {
226
EditorSettings::get_singleton()->set("docks/scene_tree/ask_before_revoking_unique_name", false);
227
ask_before_revoke_checkbox->set_pressed(false);
228
}
229
230
_revoke_unique_name();
231
}
232
233
void SceneTreeEditor::_revoke_unique_name() {
234
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
235
236
undo_redo->create_action(TTR("Disable Scene Unique Name"));
237
undo_redo->add_do_method(revoke_node, "set_unique_name_in_owner", false);
238
undo_redo->add_undo_method(revoke_node, "set_unique_name_in_owner", true);
239
undo_redo->add_do_method(this, "_update_tree");
240
undo_redo->add_undo_method(this, "_update_tree");
241
undo_redo->commit_action();
242
}
243
244
void SceneTreeEditor::_toggle_visible(Node *p_node) {
245
if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) {
246
bool v = bool(p_node->call("is_visible"));
247
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
248
undo_redo->add_do_method(p_node, "set_visible", !v);
249
undo_redo->add_undo_method(p_node, "set_visible", v);
250
}
251
}
252
253
void SceneTreeEditor::_update_node_path(Node *p_node, bool p_recursive) {
254
if (!p_node) {
255
return;
256
}
257
258
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node);
259
if (!I) {
260
return;
261
}
262
263
I->value.item->set_metadata(0, p_node->get_path());
264
265
if (!p_recursive) {
266
return;
267
}
268
269
int cc = p_node->get_child_count(false);
270
for (int i = 0; i < cc; i++) {
271
Node *c = p_node->get_child(i, false);
272
_update_node_path(c, p_recursive);
273
}
274
}
275
276
void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, bool p_force) {
277
if (!p_node) {
278
return;
279
}
280
281
// Only owned nodes are editable, since nodes can create their own (manually owned) child nodes,
282
// which the editor needs not to know about.
283
284
bool part_of_subscene = false;
285
286
if (!display_foreign && p_node->get_owner() != get_scene_node() && p_node != get_scene_node()) {
287
if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
288
part_of_subscene = true;
289
// Allow.
290
} else {
291
// Stale node, remove recursively.
292
node_cache.remove(p_node, true);
293
return;
294
}
295
} else {
296
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;
297
}
298
299
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node);
300
TreeItem *item = nullptr;
301
302
bool is_new = false;
303
304
if (I) {
305
item = I->value.item;
306
TreeItem *current_parent = item->get_parent();
307
308
// Our parent might be re-created because of a changed type.
309
if (p_parent && p_parent != current_parent) {
310
if (current_parent) {
311
current_parent->remove_child(item);
312
}
313
p_parent->add_child(item);
314
I->value.removed = false;
315
_move_node_item(p_parent, I);
316
}
317
318
if (I->value.has_moved_children) {
319
_move_node_children(I);
320
}
321
} else {
322
int index = -1;
323
// Check to see if there is a root node for us to reuse.
324
if (!p_parent) {
325
item = tree->get_root();
326
if (!item) {
327
item = tree->create_item(nullptr);
328
}
329
index = 0;
330
} else {
331
index = p_node->get_index(false);
332
item = tree->create_item(p_parent, index);
333
}
334
335
I = node_cache.add(p_node, item);
336
I->value.index = index;
337
is_new = true;
338
}
339
340
EditorNode::get_singleton()->update_resource_count(p_node);
341
342
if (!(p_force || I->value.dirty)) {
343
// Nothing to do.
344
return;
345
}
346
347
_update_node(p_node, item, part_of_subscene);
348
I->value.dirty = false;
349
I->value.can_process = p_node->can_process();
350
351
// Force update all our children if we are new or if we were forced to update.
352
bool force_update_children = p_force || is_new;
353
// Update all our children.
354
for (int i = 0; i < p_node->get_child_count(false); i++) {
355
_update_node_subtree(p_node->get_child(i, false), item, force_update_children);
356
}
357
358
if (valid_types.size()) {
359
bool valid = false;
360
for (const StringName &E : valid_types) {
361
if (p_node->is_class(E) ||
362
EditorNode::get_singleton()->is_object_of_custom_type(p_node, E)) {
363
valid = true;
364
break;
365
} else {
366
Ref<Script> node_script = p_node->get_script();
367
while (node_script.is_valid()) {
368
if (node_script->get_path() == E) {
369
valid = true;
370
break;
371
}
372
node_script = node_script->get_base_script();
373
}
374
if (valid) {
375
break;
376
}
377
}
378
}
379
380
if (!valid) {
381
_set_item_custom_color(item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
382
item->set_selectable(0, false);
383
item->deselect(0);
384
if (selected == p_node) {
385
selected = nullptr;
386
}
387
}
388
}
389
}
390
391
void SceneTreeEditor::_update_node(Node *p_node, TreeItem *p_item, bool p_part_of_subscene) {
392
// Reset item properties that are not explicitly set in the default case.
393
p_item->clear_buttons();
394
p_item->remove_meta(SNAME("custom_color"));
395
p_item->clear_custom_color(0);
396
p_item->set_selectable(0, true);
397
398
p_item->set_text(0, p_node->get_name());
399
p_item->set_text_overrun_behavior(0, TextServer::OVERRUN_NO_TRIMMING);
400
if (can_rename && !p_part_of_subscene) {
401
p_item->set_editable(0, true);
402
}
403
404
if (can_rename) {
405
bool collapsed = p_node->is_displayed_folded();
406
if (collapsed) {
407
p_item->set_collapsed(true);
408
}
409
}
410
411
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(p_node);
412
p_item->set_icon(0, icon);
413
p_item->set_metadata(0, p_node->get_path());
414
415
if (!p_node->is_connected("child_order_changed", callable_mp(this, &SceneTreeEditor::_node_child_order_changed))) {
416
p_node->connect("child_order_changed", callable_mp(this, &SceneTreeEditor::_node_child_order_changed).bind(p_node));
417
}
418
419
if (!p_node->is_connected("editor_state_changed", callable_mp(this, &SceneTreeEditor::_node_editor_state_changed))) {
420
p_node->connect("editor_state_changed", callable_mp(this, &SceneTreeEditor::_node_editor_state_changed).bind(p_node));
421
}
422
423
if (connecting_signal || (can_open_instance && is_scene_tree_dock)) {
424
if (!p_node->is_connected(CoreStringName(script_changed), callable_mp(this, &SceneTreeEditor::_node_script_changed))) {
425
p_node->connect(CoreStringName(script_changed), callable_mp(this, &SceneTreeEditor::_node_script_changed).bind(p_node));
426
}
427
}
428
429
if (connecting_signal) {
430
// Add script icons for all scripted nodes.
431
Ref<Script> scr = p_node->get_script();
432
if (scr.is_valid()) {
433
p_item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT);
434
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr) {
435
// Disable button on custom scripts (pure visual cue).
436
p_item->set_button_disabled(0, p_item->get_button_count(0) - 1, true);
437
}
438
}
439
}
440
441
if (connect_to_script_mode) {
442
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
443
444
Ref<Script> scr = p_node->get_script();
445
bool has_custom_script = scr.is_valid() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr;
446
if (scr.is_null() || has_custom_script) {
447
_set_item_custom_color(p_item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
448
p_item->set_selectable(0, false);
449
450
accent.a *= 0.7;
451
}
452
453
if (marked.has(p_node)) {
454
String node_name = p_node->get_name();
455
if (connecting_signal) {
456
node_name += " " + TTR("(Connecting From)");
457
}
458
p_item->set_text(0, node_name);
459
_set_item_custom_color(p_item, accent);
460
}
461
} else if (p_part_of_subscene) {
462
if (valid_types.is_empty()) {
463
_set_item_custom_color(p_item, get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
464
}
465
} else if (marked.has(p_node)) {
466
String node_name = p_node->get_name();
467
if (connecting_signal) {
468
node_name += " " + TTR("(Connecting From)");
469
}
470
p_item->set_text(0, node_name);
471
p_item->set_selectable(0, marked_selectable);
472
_set_item_custom_color(p_item, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
473
} else if (!p_node->can_process()) {
474
_set_item_custom_color(p_item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
475
} else if (!marked_selectable && !marked_children_selectable) {
476
Node *node = p_node;
477
while (node) {
478
if (marked.has(node)) {
479
p_item->set_selectable(0, false);
480
_set_item_custom_color(p_item, get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
481
break;
482
}
483
node = node->get_parent();
484
}
485
}
486
487
if (can_rename) { // TODO Should be can edit..
488
PackedStringArray warnings = _get_node_configuration_warnings(p_node);
489
if (accessibility_warnings) {
490
warnings.append_array(_get_node_accessibility_configuration_warnings(p_node));
491
}
492
493
const int num_warnings = warnings.size();
494
if (num_warnings > 0) {
495
StringName warning_icon;
496
if (num_warnings == 1) {
497
warning_icon = SNAME("NodeWarning");
498
} else if (num_warnings <= 3) {
499
warning_icon = vformat("NodeWarnings%d", num_warnings);
500
} else {
501
warning_icon = SNAME("NodeWarnings4Plus");
502
}
503
504
// Improve looks on tooltip, extra spacing on non-bullet point newlines.
505
const String bullet_point = U"• ";
506
String all_warnings;
507
for (const String &w : warnings) {
508
all_warnings += "\n\n" + bullet_point + w.replace("\n", "\n ");
509
}
510
if (num_warnings == 1) {
511
all_warnings.remove_at(0); // With only one warning, two newlines do not look great.
512
}
513
514
p_item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + all_warnings);
515
}
516
517
if (p_node->is_unique_name_in_owner()) {
518
const bool disabled = p_node->get_owner() != EditorNode::get_singleton()->get_edited_scene();
519
String button_text = vformat(TTR("This node can be accessed from anywhere within the scene it belongs to by using the '%s' prefix in the node path."), UNIQUE_NODE_PREFIX);
520
if (!disabled) {
521
button_text += "\n" + TTR("Click to disable this.");
522
}
523
p_item->add_button(0, get_editor_theme_icon(SNAME("SceneUniqueName")), BUTTON_UNIQUE, disabled, button_text);
524
}
525
526
int num_connections = p_node->get_persistent_signal_connection_count();
527
int num_groups = p_node->get_persistent_group_count();
528
529
String msg_temp;
530
if (num_connections >= 1) {
531
Array arr = { num_connections };
532
msg_temp += TTRN("Node has one connection.", "Node has {num} connections.", num_connections).format(arr, "{num}");
533
if (num_groups >= 1) {
534
msg_temp += "\n";
535
}
536
}
537
if (num_groups >= 1) {
538
msg_temp += TTRN("Node is in this group:", "Node is in the following groups:", num_groups) + "\n";
539
540
List<GroupInfo> groups;
541
p_node->get_groups(&groups);
542
for (const GroupInfo &E : groups) {
543
if (E.persistent) {
544
msg_temp += String::utf8("• ") + String(E.name) + "\n";
545
}
546
}
547
} else {
548
msg_temp += "\n";
549
}
550
551
Ref<Texture2D> icon_temp;
552
SceneTreeEditorButton signal_temp = BUTTON_SIGNALS;
553
String msg_temp_end = TTR("Click to show signals dock.");
554
555
if (num_connections >= 1 && num_groups >= 1) {
556
icon_temp = get_editor_theme_icon(SNAME("SignalsAndGroups"));
557
} else if (num_connections >= 1) {
558
icon_temp = get_editor_theme_icon(SNAME("Signals"));
559
} else if (num_groups >= 1) {
560
icon_temp = get_editor_theme_icon(SNAME("Groups"));
561
signal_temp = BUTTON_GROUPS;
562
msg_temp_end = TTR("Click to show groups dock.");
563
}
564
565
if (num_connections >= 1 || num_groups >= 1) {
566
msg_temp += msg_temp_end;
567
p_item->add_button(0, icon_temp, signal_temp, false, msg_temp);
568
}
569
}
570
571
{
572
_update_node_tooltip(p_node, p_item);
573
Callable delay_update_tooltip = callable_mp(this, &SceneTreeEditor::_queue_update_node_tooltip);
574
if (p_node->is_connected("editor_description_changed", delay_update_tooltip)) {
575
p_node->disconnect("editor_description_changed", delay_update_tooltip);
576
}
577
p_node->connect("editor_description_changed", delay_update_tooltip.bind(p_item));
578
}
579
580
// Show buttons only when necessary (SceneTreeDock) to avoid crashes.
581
if (can_open_instance && is_scene_tree_dock) {
582
Ref<Script> scr = p_node->get_script();
583
if (scr.is_valid()) {
584
String additional_notes;
585
Color button_color = Color(1, 1, 1);
586
// Can't set tooltip after adding button, need to do it before.
587
if (scr->is_tool()) {
588
if (Engine::get_singleton()->is_recovery_mode_hint()) {
589
additional_notes += "\n" + TTR("This script can run in the editor.\nIt is currently disabled due to recovery mode.");
590
button_color = get_theme_color(SNAME("warning_color"), EditorStringName(Editor));
591
} else {
592
additional_notes += "\n" + TTR("This script is currently running in the editor.");
593
button_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
594
}
595
}
596
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == scr) {
597
additional_notes += "\n" + TTR("This script is a custom type.");
598
button_color.a = 0.5;
599
}
600
p_item->add_button(0, get_editor_theme_icon(SNAME("Script")), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + scr->get_path() + additional_notes);
601
p_item->set_button_color(0, p_item->get_button_count(0) - 1, button_color);
602
}
603
604
if (p_node->has_meta("_edit_lock_")) {
605
p_item->add_button(0, get_editor_theme_icon(SNAME("Lock")), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock it."));
606
}
607
if (p_node->has_meta("_edit_group_")) {
608
p_item->add_button(0, get_editor_theme_icon(SNAME("Group")), BUTTON_GROUP, false, TTR("Children are not selectable.\nClick to make them selectable."));
609
}
610
611
if (p_node->has_method("is_visible") && p_node->has_method("set_visible") && p_node->has_signal(SceneStringName(visibility_changed))) {
612
bool is_visible = p_node->call("is_visible");
613
if (is_visible) {
614
p_item->add_button(0, get_editor_theme_icon(SNAME("GuiVisibilityVisible")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
615
} else {
616
p_item->add_button(0, get_editor_theme_icon(SNAME("GuiVisibilityHidden")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
617
}
618
const Callable vis_changed = callable_mp(this, &SceneTreeEditor::_node_visibility_changed);
619
if (!p_node->is_connected(SceneStringName(visibility_changed), vis_changed)) {
620
p_node->connect(SceneStringName(visibility_changed), vis_changed.bind(p_node));
621
}
622
_update_visibility_color(p_node, p_item);
623
}
624
625
if (p_node->is_class("AnimationMixer")) {
626
bool is_pinned = AnimationPlayerEditor::get_singleton()->get_editing_node() == p_node && AnimationPlayerEditor::get_singleton()->is_pinned();
627
628
if (is_pinned) {
629
p_item->add_button(0, get_editor_theme_icon(SNAME("Pin")), BUTTON_PIN, false, TTR("AnimationPlayer is pinned.\nClick to unpin."));
630
}
631
}
632
}
633
634
if (editor_selection) {
635
if (editor_selection->is_selected(p_node)) {
636
p_item->select(0);
637
}
638
}
639
640
if (selected == p_node) {
641
if (!editor_selection) {
642
p_item->select(0);
643
}
644
p_item->set_as_cursor(0);
645
}
646
}
647
648
void SceneTreeEditor::_update_if_clean() {
649
if (tree_dirty) {
650
return;
651
}
652
653
callable_mp(this, &SceneTreeEditor::_update_tree).call_deferred(false);
654
tree_dirty = true;
655
}
656
657
void SceneTreeEditor::_queue_update_node_tooltip(Node *p_node, TreeItem *p_item) {
658
Callable update_tooltip = callable_mp(this, &SceneTreeEditor::_update_node_tooltip);
659
if (update_node_tooltip_delay->is_connected("timeout", update_tooltip)) {
660
update_node_tooltip_delay->disconnect("timeout", update_tooltip);
661
}
662
663
update_node_tooltip_delay->connect("timeout", update_tooltip.bind(p_node, p_item));
664
update_node_tooltip_delay->start();
665
}
666
667
void SceneTreeEditor::_update_node_tooltip(Node *p_node, TreeItem *p_item) {
668
// Display the node name in all tooltips so that long node names can be previewed
669
// without having to rename them.
670
String tooltip = p_node->get_name();
671
672
if (p_node == get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
673
if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) {
674
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
675
}
676
tooltip += String("\n" + TTR("Inherits:") + " " + p_node->get_scene_inherited_state()->get_path());
677
} else if (p_node != get_scene_node() && p_node->is_instance() && can_open_instance) {
678
if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) {
679
p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
680
}
681
tooltip += String("\n" + TTR("Instance:") + " " + p_node->get_scene_file_path());
682
}
683
684
StringName custom_type = EditorNode::get_singleton()->get_object_custom_type_name(p_node);
685
tooltip += "\n" + TTR("Type:") + " " + (custom_type != StringName() ? String(custom_type) : p_node->get_class());
686
687
if (!p_node->get_editor_description().is_empty()) {
688
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_node->get_editor_description(), "", 80);
689
tooltip += "\n";
690
691
for (int i = 0; i < boundaries.size(); i += 2) {
692
const int start = boundaries[i];
693
const int end = boundaries[i + 1];
694
tooltip += "\n" + p_node->get_editor_description().substr(start, end - start + 1).rstrip("\n");
695
}
696
}
697
698
p_item->set_tooltip_text(0, tooltip);
699
}
700
701
void SceneTreeEditor::_node_visibility_changed(Node *p_node) {
702
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node, false);
703
if (!I) {
704
// We leave these signals connected when switching tabs.
705
// If the node is not in cache it was for a different tab.
706
return;
707
}
708
709
if (!p_node || (p_node != get_scene_node() && !p_node->get_owner())) {
710
return;
711
}
712
713
TreeItem *item;
714
if (I->value.item && I->value.item->get_metadata(0) == p_node->get_path()) {
715
item = I->value.item;
716
} else {
717
item = _find(tree->get_root(), p_node->get_path());
718
}
719
720
if (!item) {
721
return;
722
}
723
724
int idx = item->get_button_by_id(0, BUTTON_VISIBILITY);
725
ERR_FAIL_COND(idx == -1);
726
727
bool node_visible = false;
728
729
if (p_node->has_method("is_visible")) {
730
node_visible = p_node->call("is_visible");
731
if (p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) {
732
CanvasItemEditor::get_singleton()->get_viewport_control()->queue_redraw();
733
}
734
}
735
736
if (node_visible) {
737
item->set_button(0, idx, get_editor_theme_icon(SNAME("GuiVisibilityVisible")));
738
} else {
739
item->set_button(0, idx, get_editor_theme_icon(SNAME("GuiVisibilityHidden")));
740
}
741
742
_update_visibility_color(p_node, item);
743
}
744
745
void SceneTreeEditor::_update_visibility_color(Node *p_node, TreeItem *p_item) {
746
if (p_node->has_method("is_visible_in_tree")) {
747
Color color(1, 1, 1, 1);
748
bool visible_on_screen = p_node->call("is_visible_in_tree");
749
if (!visible_on_screen) {
750
color.a = 0.6;
751
}
752
int idx = p_item->get_button_by_id(0, BUTTON_VISIBILITY);
753
p_item->set_button_color(0, idx, color);
754
}
755
}
756
757
void SceneTreeEditor::_set_item_custom_color(TreeItem *p_item, Color p_color) {
758
p_item->set_custom_color(0, p_color);
759
p_item->set_meta(SNAME("custom_color"), p_color);
760
}
761
762
void SceneTreeEditor::_node_script_changed(Node *p_node) {
763
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node, false);
764
if (!I) {
765
// We leave these signals connected when switching tabs.
766
// If the node is not in cache it was for a different tab.
767
return;
768
}
769
770
node_cache.mark_dirty(p_node);
771
772
_update_if_clean();
773
}
774
775
void SceneTreeEditor::_move_node_children(HashMap<Node *, CachedNode>::Iterator &p_I) {
776
TreeItem *item = p_I->value.item;
777
TreeItem *previous_item = nullptr;
778
Node *node = p_I->key;
779
int cc = node->get_child_count(false);
780
781
for (int i = 0; i < cc; i++) {
782
HashMap<Node *, CachedNode>::Iterator CI = node_cache.get(node->get_child(i, false));
783
if (CI) {
784
_move_node_item(item, CI, previous_item);
785
previous_item = CI->value.item;
786
} else {
787
previous_item = nullptr;
788
}
789
}
790
791
p_I->value.has_moved_children = false;
792
}
793
794
void SceneTreeEditor::_move_node_item(TreeItem *p_parent, HashMap<Node *, CachedNode>::Iterator &p_I, TreeItem *p_correct_prev) {
795
if (!p_parent) {
796
return;
797
}
798
799
Node *node = p_I->key;
800
801
int current_node_index = node->get_index(false);
802
int current_item_index = -1;
803
TreeItem *item = p_I->value.item;
804
805
if (item->get_parent() != p_parent) {
806
TreeItem *p = item->get_parent();
807
if (p) {
808
item->get_parent()->remove_child(item);
809
}
810
p_parent->add_child(item);
811
p_I->value.removed = false;
812
current_item_index = p_parent->get_child_count() - 1;
813
p_I->value.index = current_item_index;
814
}
815
816
if (p_I->value.index != current_node_index) {
817
bool already_in_correct_location;
818
if (current_item_index >= 0) {
819
// If we just re-parented we know our index.
820
already_in_correct_location = current_item_index == current_node_index;
821
} else if (p_correct_prev) {
822
// It's cheaper to check if we're set up correctly by checking via correct_prev if we can
823
already_in_correct_location = item->get_prev() == p_correct_prev;
824
} else {
825
already_in_correct_location = item->get_index() == current_node_index;
826
}
827
828
// Are we already in the right place?
829
if (already_in_correct_location) {
830
p_I->value.index = current_node_index;
831
return;
832
}
833
834
// Are we the first node?
835
if (current_node_index == 0) {
836
// There has to be at least 1 other node, otherwise we would not have gotten here.
837
TreeItem *neighbor_item = p_parent->get_first_child();
838
item->move_before(neighbor_item);
839
} else {
840
TreeItem *prev_item = p_correct_prev;
841
if (!prev_item) {
842
prev_item = p_parent->get_child(CLAMP(current_node_index - 1, 0, p_parent->get_child_count() - 1));
843
}
844
item->move_after(prev_item);
845
}
846
847
p_I->value.index = current_node_index;
848
}
849
}
850
851
void SceneTreeEditor::_node_child_order_changed(Node *p_node) {
852
// Do not try to change children on nodes currently marked for removal.
853
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node, false);
854
if (I) {
855
node_cache.mark_dirty(I->key);
856
I->value.has_moved_children = true;
857
}
858
859
_update_if_clean();
860
}
861
862
void SceneTreeEditor::_node_editor_state_changed(Node *p_node) {
863
node_cache.mark_dirty(p_node);
864
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(p_node, false);
865
if (I) {
866
if (p_node->is_inside_tree() && p_node->can_process() != I->value.can_process) {
867
// All our children also change process mode.
868
node_cache.mark_children_dirty(p_node, true);
869
}
870
}
871
872
_update_if_clean();
873
}
874
875
void SceneTreeEditor::_node_added(Node *p_node) {
876
if (!get_scene_node()) {
877
return;
878
}
879
880
if (p_node != get_scene_node() && !get_scene_node()->is_ancestor_of(p_node)) {
881
return;
882
}
883
884
node_cache.mark_dirty(p_node);
885
_update_if_clean();
886
}
887
888
void SceneTreeEditor::_node_removed(Node *p_node) {
889
if (EditorNode::get_singleton()->is_exiting()) {
890
return; // Speed up exit.
891
}
892
893
if (EditorNode::get_singleton()->is_changing_scene()) {
894
return; // Switching tabs we will be destroying node cache anyway.
895
}
896
897
if (!get_scene_node()) {
898
return;
899
}
900
901
if (p_node != get_scene_node() && !get_scene_node()->is_ancestor_of(p_node)) {
902
return;
903
}
904
node_cache.remove(p_node);
905
_update_if_clean();
906
}
907
908
void SceneTreeEditor::_node_renamed(Node *p_node) {
909
if (!get_scene_node()) {
910
return;
911
}
912
913
if (p_node != get_scene_node() && !get_scene_node()->is_ancestor_of(p_node)) {
914
return;
915
}
916
917
node_cache.mark_dirty(p_node);
918
// Recursively update child node paths.
919
_update_node_path(p_node, true);
920
921
emit_signal(SNAME("node_renamed"));
922
923
_update_if_clean();
924
}
925
926
void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) {
927
if (!is_inside_tree()) {
928
tree_dirty = false;
929
return;
930
}
931
932
if (!update_when_invisible && !is_visible_in_tree()) {
933
return;
934
}
935
936
Node *scene_node = get_scene_node();
937
const ObjectID scene_id = scene_node ? scene_node->get_instance_id() : ObjectID();
938
if (node_cache.current_scene_id != scene_id) {
939
_reset();
940
marked.clear();
941
node_cache.current_scene_id = scene_id;
942
node_cache.force_update = true;
943
}
944
945
if (tree->is_editing()) {
946
return;
947
}
948
949
updating_tree = true;
950
951
last_hash = hash_djb2_one_64(0);
952
953
if (node_cache.current_scene_id.is_valid()) {
954
// Handle pinning/unpinning the animation player only do this once per iteration.
955
Node *pinned_node = AnimationPlayerEditor::get_singleton()->get_editing_node();
956
// If pinned state changed, update the currently pinned node.
957
if (AnimationPlayerEditor::get_singleton()->is_pinned() != node_cache.current_has_pin) {
958
node_cache.current_has_pin = AnimationPlayerEditor::get_singleton()->is_pinned();
959
if (node_cache.has(pinned_node)) {
960
node_cache.mark_dirty(pinned_node);
961
}
962
}
963
// If the current pinned node changed update both the old and new node.
964
if (node_cache.current_pinned_node != pinned_node) {
965
// get_editing_node() will return deleted nodes. If the nodes are not in cache don't try to mark them.
966
if (node_cache.has(pinned_node)) {
967
node_cache.mark_dirty(pinned_node);
968
}
969
if (node_cache.has(node_cache.current_pinned_node)) {
970
node_cache.mark_dirty(node_cache.current_pinned_node);
971
}
972
node_cache.current_pinned_node = pinned_node;
973
}
974
_update_node_subtree(get_scene_node(), nullptr, node_cache.force_update);
975
_compute_hash(get_scene_node(), last_hash);
976
977
node_cache.delete_pending();
978
}
979
980
updating_tree = false;
981
tree_dirty = false;
982
node_cache.force_update = false;
983
984
if (!filter.strip_edges().is_empty() || !show_all_nodes) {
985
_update_filter(nullptr, p_scroll_to_selected);
986
}
987
}
988
989
bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_selected) {
990
TreeItem *last_selected = nullptr;
991
bool result = _update_filter_helper(p_parent, p_scroll_to_selected, last_selected);
992
if (p_scroll_to_selected && last_selected) {
993
// Scrolling to the first selected in the _update_filter call above followed by the last
994
// selected here is enough to frame all selected items as well as possible.
995
callable_mp(tree, &Tree::scroll_to_item).call_deferred(last_selected, false);
996
}
997
return result;
998
}
999
1000
bool SceneTreeEditor::_update_filter_helper(TreeItem *p_parent, bool p_scroll_to_selected, TreeItem *&r_last_selected) {
1001
if (!p_parent) {
1002
p_parent = tree->get_root();
1003
filter_term_warning.clear();
1004
}
1005
1006
if (!p_parent) {
1007
// Tree is empty, nothing to do here.
1008
return false;
1009
}
1010
1011
// Now find other reasons to keep this Node, too.
1012
PackedStringArray terms = filter.to_lower().split_spaces();
1013
bool keep = _item_matches_all_terms(p_parent, terms);
1014
1015
bool selectable = keep;
1016
bool is_root = p_parent == tree->get_root();
1017
1018
if (keep) {
1019
Node *n = get_node(p_parent->get_metadata(0));
1020
if (!p_parent->is_visible() || (is_root && tree->is_root_hidden())) {
1021
// Place back moved out children from when this item has hidden.
1022
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(n, false);
1023
if (I && I->value.has_moved_children) {
1024
_update_node_subtree(I->value.node, nullptr, true);
1025
}
1026
}
1027
1028
if (!valid_types.is_empty()) {
1029
selectable = false;
1030
for (const StringName &E : valid_types) {
1031
if (n->is_class(E) ||
1032
EditorNode::get_singleton()->is_object_of_custom_type(n, E)) {
1033
selectable = true;
1034
break;
1035
} else {
1036
Ref<Script> node_script = n->get_script();
1037
while (node_script.is_valid()) {
1038
if (node_script->get_path() == E) {
1039
selectable = true;
1040
break;
1041
}
1042
node_script = node_script->get_base_script();
1043
}
1044
if (selectable) {
1045
break;
1046
}
1047
}
1048
}
1049
}
1050
}
1051
1052
bool keep_for_children = false;
1053
for (TreeItem *child = p_parent->get_first_child(); child; child = child->get_next()) {
1054
// Always keep if at least one of the children are kept.
1055
// Only scroll if we haven't already found a child to scroll to.
1056
keep_for_children = _update_filter_helper(child, p_scroll_to_selected && !keep_for_children, r_last_selected) || keep_for_children;
1057
}
1058
1059
if (!is_root) {
1060
if (show_all_nodes) {
1061
p_parent->set_visible(keep_for_children || keep);
1062
} else {
1063
// Show only selectable nodes, or parents of selectable.
1064
p_parent->set_visible(keep_for_children || selectable);
1065
}
1066
}
1067
1068
if (selectable) {
1069
Color custom_color = p_parent->get_meta(SNAME("custom_color"), Color(0, 0, 0, 0));
1070
if (custom_color == Color(0, 0, 0, 0)) {
1071
p_parent->clear_custom_color(0);
1072
} else {
1073
p_parent->set_custom_color(0, custom_color);
1074
}
1075
1076
p_parent->set_selectable(0, true);
1077
} else if (keep_for_children) {
1078
p_parent->set_visible(!hide_filtered_out_parents || is_root);
1079
1080
if (!p_parent->is_visible()) {
1081
TreeItem *filtered_parent = p_parent->get_parent();
1082
while (filtered_parent) {
1083
if (filtered_parent == tree->get_root() || (filtered_parent->is_selectable(0) && filtered_parent->is_visible())) {
1084
break;
1085
}
1086
filtered_parent = filtered_parent->get_parent();
1087
}
1088
1089
if (filtered_parent) {
1090
for (Variant &item : p_parent->get_children()) {
1091
TreeItem *ti = Object::cast_to<TreeItem>(item);
1092
bool is_selected = ti->is_selected(0);
1093
1094
p_parent->remove_child(ti);
1095
filtered_parent->add_child(ti);
1096
TreeItem *prev = p_parent->get_prev();
1097
if (prev) {
1098
ti->move_after(prev);
1099
}
1100
1101
if (is_selected) {
1102
ti->select(0);
1103
}
1104
1105
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(get_node(p_parent->get_metadata(0)), false);
1106
if (I) {
1107
I->value.has_moved_children = true;
1108
}
1109
}
1110
1111
return false;
1112
}
1113
} else {
1114
p_parent->set_custom_color(0, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
1115
p_parent->set_selectable(0, false);
1116
}
1117
}
1118
if (is_root) {
1119
tree->set_hide_root(hide_filtered_out_parents && !selectable);
1120
if (tree->is_root_hidden()) {
1121
p_parent->set_collapsed(false);
1122
}
1123
}
1124
1125
if (editor_selection) {
1126
Node *n = get_node(p_parent->get_metadata(0));
1127
if (selectable) {
1128
if (n && editor_selection->is_selected(n)) {
1129
if (p_scroll_to_selected) {
1130
// Needs to be deferred to account for possible root visibility change.
1131
callable_mp(tree, &Tree::scroll_to_item).call_deferred(p_parent, false);
1132
} else {
1133
r_last_selected = p_parent;
1134
}
1135
}
1136
}
1137
}
1138
1139
return p_parent->is_visible();
1140
}
1141
1142
bool SceneTreeEditor::_node_matches_class_term(const Node *p_item_node, const String &p_term) {
1143
if (p_term.is_empty()) {
1144
// Defend against https://github.com/godotengine/godot/issues/82473
1145
return true;
1146
}
1147
Ref<Script> item_script = p_item_node->get_script();
1148
while (item_script.is_valid()) {
1149
String global_name = item_script->get_global_name();
1150
if (global_name.to_lower().contains(p_term)) {
1151
return true;
1152
}
1153
item_script = item_script->get_base_script();
1154
}
1155
1156
String type = p_item_node->get_class();
1157
// Every Node is a Node, duh!
1158
while (type != "Node") {
1159
if (type.to_lower().contains(p_term)) {
1160
return true;
1161
}
1162
1163
type = ClassDB::get_parent_class(type);
1164
}
1165
1166
return false;
1167
}
1168
1169
bool SceneTreeEditor::_item_matches_all_terms(TreeItem *p_item, const PackedStringArray &p_terms) {
1170
if (p_terms.is_empty()) {
1171
return true;
1172
}
1173
1174
for (int i = 0; i < p_terms.size(); i++) {
1175
const String &term = p_terms[i];
1176
1177
// Recognize special filter.
1178
if (term.contains_char(':') && !term.get_slicec(':', 0).is_empty()) {
1179
String parameter = term.get_slicec(':', 0);
1180
String argument = term.get_slicec(':', 1);
1181
1182
if (parameter == "type" || parameter == "t") {
1183
// Filter by Type.
1184
Node *item_node = get_node(p_item->get_metadata(0));
1185
if (!_node_matches_class_term(item_node, argument)) {
1186
return false;
1187
}
1188
} else if (parameter == "group" || parameter == "g") {
1189
// Filter by Group.
1190
Node *node = get_node(p_item->get_metadata(0));
1191
1192
if (argument.is_empty()) {
1193
// When argument is empty, match all Nodes belonging to any exposed group.
1194
if (node->get_persistent_group_count() == 0) {
1195
return false;
1196
}
1197
} else {
1198
List<Node::GroupInfo> group_info_list;
1199
node->get_groups(&group_info_list);
1200
1201
bool term_in_groups = false;
1202
for (const Node::GroupInfo &group_info : group_info_list) {
1203
if (!group_info.persistent) {
1204
continue; // Ignore internal groups.
1205
}
1206
if (String(group_info.name).to_lower().contains(argument)) {
1207
term_in_groups = true;
1208
break;
1209
}
1210
}
1211
if (!term_in_groups) {
1212
return false;
1213
}
1214
}
1215
} else if (filter_term_warning.is_empty()) {
1216
filter_term_warning = vformat(TTR("\"%s\" is not a known filter."), parameter);
1217
continue;
1218
}
1219
} else {
1220
// Default.
1221
if (!p_item->get_text(0).to_lower().contains(term)) {
1222
return false;
1223
}
1224
}
1225
}
1226
1227
return true;
1228
}
1229
1230
void SceneTreeEditor::_compute_hash(Node *p_node, uint64_t &hash) {
1231
// Nodes are added and removed by Node* pointers.
1232
hash = hash_djb2_one_64((ptrdiff_t)p_node, hash);
1233
// This hash is non-commutative: if the node order changes so will the hash.
1234
for (int i = 0; i < p_node->get_child_count(); i++) {
1235
_compute_hash(p_node->get_child(i), hash);
1236
}
1237
}
1238
1239
void SceneTreeEditor::_reset() {
1240
// Stop any waiting change to tooltip.
1241
update_node_tooltip_delay->stop();
1242
tree->clear();
1243
node_cache.clear();
1244
}
1245
1246
void SceneTreeEditor::_test_update_tree() {
1247
pending_test_update = false;
1248
1249
if (!is_inside_tree()) {
1250
return;
1251
}
1252
1253
if (tree_dirty) {
1254
return; // Don't even bother.
1255
}
1256
1257
uint64_t hash = hash_djb2_one_64(0);
1258
if (get_scene_node()) {
1259
_compute_hash(get_scene_node(), hash);
1260
}
1261
1262
// Test hash.
1263
if (hash == last_hash) {
1264
return; // Did not change.
1265
}
1266
1267
_update_if_clean();
1268
}
1269
1270
void SceneTreeEditor::_tree_process_mode_changed() {
1271
callable_mp(this, &SceneTreeEditor::_update_tree).call_deferred(false);
1272
tree_dirty = true;
1273
}
1274
1275
void SceneTreeEditor::_tree_changed() {
1276
if (EditorNode::get_singleton()->is_exiting()) {
1277
return; // Speed up exit.
1278
}
1279
1280
if (pending_test_update) {
1281
return;
1282
}
1283
1284
if (tree_dirty) {
1285
return;
1286
}
1287
1288
callable_mp(this, &SceneTreeEditor::_test_update_tree).call_deferred();
1289
pending_test_update = true;
1290
}
1291
1292
void SceneTreeEditor::_selected_changed() {
1293
TreeItem *s = tree->get_selected();
1294
ERR_FAIL_NULL(s);
1295
NodePath np = s->get_metadata(0);
1296
1297
Node *n = get_node(np);
1298
1299
if (n == selected) {
1300
return;
1301
}
1302
1303
selected = n;
1304
1305
blocked++;
1306
emit_signal(SNAME("node_selected"));
1307
blocked--;
1308
}
1309
1310
void SceneTreeEditor::_deselect_items() {
1311
// Clear currently selected items in scene tree dock.
1312
if (editor_selection) {
1313
editor_selection->clear();
1314
emit_signal(SNAME("node_changed"));
1315
}
1316
}
1317
1318
void SceneTreeEditor::_cell_multi_selected(Object *p_object, int p_cell, bool p_selected) {
1319
TreeItem *item = Object::cast_to<TreeItem>(p_object);
1320
ERR_FAIL_NULL(item);
1321
1322
if (!item->is_visible()) {
1323
return;
1324
}
1325
1326
NodePath np = item->get_metadata(0);
1327
1328
Node *n = get_node(np);
1329
1330
if (!n) {
1331
return;
1332
}
1333
1334
if (!editor_selection) {
1335
return;
1336
}
1337
1338
if (p_selected) {
1339
editor_selection->add_node(n);
1340
1341
} else {
1342
editor_selection->remove_node(n);
1343
}
1344
1345
// Emitted "selected" in _selected_changed() when select single node, so select multiple node emit "changed".
1346
if (editor_selection->get_selection().size() > 1 && !pending_selection_update) {
1347
pending_selection_update = true;
1348
callable_mp(this, &SceneTreeEditor::_process_selection_update).call_deferred();
1349
}
1350
}
1351
1352
void SceneTreeEditor::_process_selection_update() {
1353
pending_selection_update = false;
1354
emit_signal(SNAME("node_changed"));
1355
}
1356
1357
void SceneTreeEditor::_tree_scroll_to_item(ObjectID p_item_id) {
1358
ERR_FAIL_NULL(tree);
1359
TreeItem *item = ObjectDB::get_instance<TreeItem>(p_item_id);
1360
if (item) {
1361
tree->scroll_to_item(item, true);
1362
}
1363
}
1364
1365
void SceneTreeEditor::_notification(int p_what) {
1366
switch (p_what) {
1367
case NOTIFICATION_ENTER_TREE: {
1368
get_tree()->connect("tree_changed", callable_mp(this, &SceneTreeEditor::_tree_changed));
1369
get_tree()->connect("tree_process_mode_changed", callable_mp(this, &SceneTreeEditor::_tree_process_mode_changed));
1370
get_tree()->connect("node_added", callable_mp(this, &SceneTreeEditor::_node_added));
1371
get_tree()->connect("node_removed", callable_mp(this, &SceneTreeEditor::_node_removed));
1372
get_tree()->connect("node_renamed", callable_mp(this, &SceneTreeEditor::_node_renamed));
1373
get_tree()->connect(SceneStringName(node_configuration_warning_changed), callable_mp(this, &SceneTreeEditor::_warning_changed));
1374
1375
tree->connect("item_collapsed", callable_mp(this, &SceneTreeEditor::_cell_collapsed));
1376
1377
_update_tree();
1378
} break;
1379
1380
case NOTIFICATION_EXIT_TREE: {
1381
get_tree()->disconnect("tree_changed", callable_mp(this, &SceneTreeEditor::_tree_changed));
1382
get_tree()->disconnect("tree_process_mode_changed", callable_mp(this, &SceneTreeEditor::_tree_process_mode_changed));
1383
get_tree()->disconnect("node_added", callable_mp(this, &SceneTreeEditor::_node_added));
1384
get_tree()->disconnect("node_removed", callable_mp(this, &SceneTreeEditor::_node_removed));
1385
get_tree()->disconnect("node_renamed", callable_mp(this, &SceneTreeEditor::_node_renamed));
1386
tree->disconnect("item_collapsed", callable_mp(this, &SceneTreeEditor::_cell_collapsed));
1387
get_tree()->disconnect(SceneStringName(node_configuration_warning_changed), callable_mp(this, &SceneTreeEditor::_warning_changed));
1388
} break;
1389
1390
case NOTIFICATION_THEME_CHANGED: {
1391
// Wait for the node to be inspected before triggering the unfolding.
1392
tree->add_theme_constant_override("dragging_unfold_wait_msec", (float)EDITOR_GET("interface/editor/dragging_hover_wait_seconds") * 1000 * 2);
1393
tree->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
1394
[[fallthrough]];
1395
}
1396
case NOTIFICATION_TRANSLATION_CHANGED: {
1397
// When we change theme or translation we need to re-do everything.
1398
_reset();
1399
_update_tree();
1400
} break;
1401
1402
case NOTIFICATION_VISIBILITY_CHANGED: {
1403
if (is_visible()) {
1404
TreeItem *item = nullptr;
1405
if (selected) {
1406
// Scroll to selected node.
1407
item = _find(tree->get_root(), selected->get_path());
1408
} else if (marked.size() == 1) {
1409
// Scroll to a single marked node.
1410
Node *marked_node = *marked.begin();
1411
if (marked_node) {
1412
item = _find(tree->get_root(), marked_node->get_path());
1413
}
1414
}
1415
1416
bool has_item = item;
1417
1418
if (update_when_invisible) {
1419
if (has_item) {
1420
ObjectID item_id = item->get_instance_id();
1421
callable_mp(this, &SceneTreeEditor::_tree_scroll_to_item).call_deferred(item_id);
1422
}
1423
} else {
1424
callable_mp(this, &SceneTreeEditor::_update_tree).call_deferred(has_item);
1425
}
1426
}
1427
} break;
1428
}
1429
}
1430
1431
TreeItem *SceneTreeEditor::_find(TreeItem *p_node, const NodePath &p_path) {
1432
if (!p_node) {
1433
return nullptr;
1434
}
1435
1436
NodePath np = p_node->get_metadata(0);
1437
if (np == p_path) {
1438
return p_node;
1439
}
1440
1441
TreeItem *children = p_node->get_first_child();
1442
while (children) {
1443
TreeItem *n = _find(children, p_path);
1444
if (n) {
1445
return n;
1446
}
1447
children = children->get_next();
1448
}
1449
1450
return nullptr;
1451
}
1452
1453
void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
1454
ERR_FAIL_COND(blocked > 0);
1455
1456
if (pending_test_update) {
1457
_test_update_tree();
1458
}
1459
1460
if (tree_dirty) {
1461
_update_tree();
1462
}
1463
1464
if (selected == p_node) {
1465
return;
1466
}
1467
1468
TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : nullptr;
1469
1470
if (item) {
1471
selected = p_node;
1472
if (auto_expand_selected) {
1473
// Make visible when it's collapsed.
1474
TreeItem *node = item->get_parent();
1475
while (node) {
1476
node->set_collapsed(false);
1477
node = node->get_parent();
1478
}
1479
item->select(0);
1480
item->set_as_cursor(0);
1481
tree->ensure_cursor_is_visible();
1482
} else {
1483
// Ensure the node is selected and visible for the user if the node
1484
// is not collapsed.
1485
bool collapsed = false;
1486
TreeItem *node = item;
1487
while (node && node != tree->get_root()) {
1488
if (node->is_collapsed()) {
1489
collapsed = true;
1490
break;
1491
}
1492
node = node->get_parent();
1493
}
1494
if (!collapsed) {
1495
item->select(0);
1496
item->set_as_cursor(0);
1497
tree->ensure_cursor_is_visible();
1498
}
1499
}
1500
} else {
1501
if (!p_node) {
1502
selected = nullptr;
1503
}
1504
selected = p_node;
1505
}
1506
1507
if (p_emit_selected) {
1508
emit_signal(SNAME("node_selected"));
1509
}
1510
}
1511
1512
void SceneTreeEditor::rename_node(Node *p_node, const String &p_name, TreeItem *p_item) {
1513
TreeItem *item;
1514
if (p_item) {
1515
item = p_item; // During batch rename the paths may change, so using _find() is unreliable.
1516
} else {
1517
item = _find(tree->get_root(), p_node->get_path());
1518
}
1519
ERR_FAIL_NULL(item);
1520
bool check_for_unique_name_token = !p_name.is_empty() && p_name[0] == '%';
1521
String substr_name = p_name;
1522
1523
if (check_for_unique_name_token) {
1524
substr_name = p_name.substr(1);
1525
1526
// No need to do anything else with this if already unique.
1527
if (p_node->is_unique_name_in_owner()) {
1528
check_for_unique_name_token = false;
1529
// Do not set scene root as unique.
1530
} else if (get_tree()->get_edited_scene_root() == p_node) {
1531
check_for_unique_name_token = false;
1532
String text = TTR("Root nodes cannot be accessed as unique names in their own scene. Instantiate in another scene and set as unique name there.");
1533
if (error->is_visible()) {
1534
error->set_text(error->get_text() + "\n\n" + text);
1535
} else {
1536
error->set_text(text);
1537
error->popup_centered();
1538
}
1539
}
1540
}
1541
1542
String new_name = substr_name.validate_node_name();
1543
1544
// If p_name only has "%" at the beginning and no other invalid characters, do not error.
1545
if (new_name != substr_name) {
1546
String text = TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters();
1547
if (error->is_visible()) {
1548
if (!error->get_meta("invalid_character", false)) {
1549
error->set_text(error->get_text() + "\n\n" + text);
1550
error->set_meta("invalid_character", true);
1551
}
1552
} else {
1553
error->set_text(text);
1554
error->set_meta("invalid_character", true);
1555
error->set_meta("same_unique_name", false);
1556
error->popup_centered();
1557
}
1558
}
1559
1560
// Trim leading/trailing whitespace to prevent node names from containing accidental whitespace,
1561
// which would make it more difficult to get the node via `get_node()`.
1562
new_name = new_name.strip_edges();
1563
if (new_name.is_empty() && p_node->get_owner() != nullptr && p_node->is_instance()) {
1564
// If name is empty and node is root of an instance, revert to the original name.
1565
const Ref<PackedScene> node_scene = ResourceLoader::load(p_node->get_scene_file_path());
1566
if (node_scene.is_valid()) {
1567
const Ref<SceneState> &state = node_scene->get_state();
1568
if (state->get_node_count() > 0) {
1569
new_name = state->get_node_name(0); // Root's name.
1570
}
1571
}
1572
}
1573
1574
if (new_name.is_empty()) {
1575
// If name is still empty, fallback to class name.
1576
if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
1577
new_name = Node::adjust_name_casing(p_node->get_class());
1578
} else {
1579
new_name = p_node->get_class();
1580
}
1581
}
1582
1583
new_name = p_node->get_parent()->prevalidate_child_name(p_node, new_name);
1584
if (new_name == p_node->get_name()) {
1585
item->set_text(0, new_name);
1586
// If setting name as unique, check for existing unique node below first.
1587
if (!check_for_unique_name_token) {
1588
return;
1589
}
1590
}
1591
1592
// We previously made sure name is not the same as current name
1593
// so that it won't complain about already used unique name when not changing name.
1594
if ((check_for_unique_name_token || p_node->is_unique_name_in_owner()) && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name)) {
1595
check_for_unique_name_token = false;
1596
String text = vformat(TTR("A node with the unique name %s already exists in this scene."), new_name);
1597
if (error->is_visible()) {
1598
if (!error->get_meta("same_unique_name", false)) {
1599
error->set_text(error->get_text() + "\n\n" + text);
1600
error->set_meta("same_unique_name", true);
1601
}
1602
} else {
1603
error->set_text(text);
1604
error->set_meta("same_unique_name", true);
1605
error->set_meta("invalid_character", false);
1606
error->popup_centered();
1607
}
1608
item->set_text(0, p_node->get_name());
1609
if (p_node->is_unique_name_in_owner()) {
1610
return;
1611
}
1612
}
1613
1614
// If same name and check_for_unique_name_token is still true, now set as unique.
1615
// This is separate from final action so "Rename Node" is not added to undo history.
1616
if (new_name == p_node->get_name()) {
1617
if (check_for_unique_name_token) {
1618
if (!is_scene_tree_dock) {
1619
p_node->set_unique_name_in_owner(true);
1620
} else {
1621
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1622
undo_redo->create_action(TTR("Enable Scene Unique Name(s)"));
1623
undo_redo->add_undo_method(p_node, "set_unique_name_in_owner", false);
1624
undo_redo->add_do_method(p_node, "set_unique_name_in_owner", true);
1625
undo_redo->commit_action();
1626
}
1627
}
1628
return;
1629
}
1630
1631
if (!is_scene_tree_dock) {
1632
p_node->set_name(new_name);
1633
if (check_for_unique_name_token) {
1634
p_node->set_unique_name_in_owner(true);
1635
}
1636
item->set_metadata(0, p_node->get_path());
1637
emit_signal(SNAME("node_renamed"));
1638
} else {
1639
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1640
undo_redo->create_action(TTR("Rename Node"), UndoRedo::MERGE_DISABLE, p_node);
1641
1642
if (check_for_unique_name_token) {
1643
undo_redo->add_undo_method(p_node, "set_unique_name_in_owner", false);
1644
}
1645
1646
emit_signal(SNAME("node_prerename"), p_node, new_name);
1647
1648
undo_redo->add_undo_method(p_node, "set_name", p_node->get_name());
1649
undo_redo->add_undo_method(item, "set_metadata", 0, p_node->get_path());
1650
undo_redo->add_undo_method(item, "set_text", 0, p_node->get_name());
1651
1652
undo_redo->add_do_method(p_node, "set_name", new_name);
1653
undo_redo->add_do_method(item, "set_metadata", 0, p_node->get_path());
1654
undo_redo->add_do_method(item, "set_text", 0, new_name);
1655
1656
if (check_for_unique_name_token) {
1657
undo_redo->add_do_method(p_node, "set_unique_name_in_owner", true);
1658
}
1659
1660
undo_redo->commit_action();
1661
}
1662
}
1663
1664
void SceneTreeEditor::_edited() {
1665
TreeItem *which = tree->get_next_selected(nullptr);
1666
ERR_FAIL_NULL(which);
1667
TreeItem *edited = tree->get_edited();
1668
ERR_FAIL_NULL(edited);
1669
1670
if (is_scene_tree_dock && tree->get_next_selected(which)) {
1671
List<Node *> nodes_to_rename;
1672
for (TreeItem *item = which; item; item = tree->get_next_selected(item)) {
1673
Node *n = get_node(item->get_metadata(0));
1674
ERR_FAIL_NULL(n);
1675
nodes_to_rename.push_back(n);
1676
}
1677
ERR_FAIL_COND(nodes_to_rename.is_empty());
1678
1679
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1680
undo_redo->create_action(TTR("Rename Nodes"), UndoRedo::MERGE_DISABLE, nodes_to_rename.front()->get(), true);
1681
1682
TreeItem *item = which;
1683
String new_name = edited->get_text(0);
1684
for (Node *n : nodes_to_rename) {
1685
rename_node(n, new_name, item);
1686
item = tree->get_next_selected(item);
1687
}
1688
1689
undo_redo->commit_action();
1690
} else {
1691
Node *n = get_node(which->get_metadata(0));
1692
ERR_FAIL_NULL(n);
1693
rename_node(n, which->get_text(0));
1694
}
1695
}
1696
1697
Node *SceneTreeEditor::get_selected() {
1698
return selected;
1699
}
1700
1701
void SceneTreeEditor::_update_marking_list(const HashSet<Node *> &p_marked) {
1702
for (Node *N : p_marked) {
1703
HashMap<Node *, CachedNode>::Iterator I = node_cache.get(N);
1704
if (I) {
1705
node_cache.mark_dirty(N);
1706
node_cache.mark_children_dirty(N, true);
1707
}
1708
}
1709
}
1710
1711
void SceneTreeEditor::set_marked(const HashSet<Node *> &p_marked, bool p_selectable, bool p_children_selectable) {
1712
_update_if_clean();
1713
1714
_update_marking_list(marked);
1715
_update_marking_list(p_marked);
1716
1717
marked = p_marked;
1718
1719
marked_selectable = p_selectable;
1720
marked_children_selectable = p_children_selectable;
1721
_update_tree();
1722
}
1723
1724
void SceneTreeEditor::set_marked(Node *p_marked, bool p_selectable, bool p_children_selectable) {
1725
HashSet<Node *> s;
1726
if (p_marked) {
1727
s.insert(p_marked);
1728
}
1729
set_marked(s, p_selectable, p_children_selectable);
1730
}
1731
1732
void SceneTreeEditor::set_filter(const String &p_filter) {
1733
filter = p_filter;
1734
_update_filter(nullptr, true);
1735
}
1736
1737
String SceneTreeEditor::get_filter() const {
1738
return filter;
1739
}
1740
1741
String SceneTreeEditor::get_filter_term_warning() {
1742
return filter_term_warning;
1743
}
1744
1745
void SceneTreeEditor::set_show_all_nodes(bool p_show_all_nodes) {
1746
show_all_nodes = p_show_all_nodes;
1747
_update_filter(nullptr, true);
1748
}
1749
1750
void SceneTreeEditor::set_as_scene_tree_dock() {
1751
is_scene_tree_dock = true;
1752
}
1753
1754
void SceneTreeEditor::set_display_foreign_nodes(bool p_display) {
1755
display_foreign = p_display;
1756
_update_tree();
1757
}
1758
1759
void SceneTreeEditor::clear_cache() {
1760
node_cache.force_update = true;
1761
callable_mp(this, &SceneTreeEditor::_update_tree).call_deferred(false);
1762
tree_dirty = true;
1763
}
1764
1765
void SceneTreeEditor::set_valid_types(const Vector<StringName> &p_valid) {
1766
valid_types = p_valid;
1767
clear_cache();
1768
}
1769
1770
void SceneTreeEditor::set_editor_selection(EditorSelection *p_selection) {
1771
editor_selection = p_selection;
1772
tree->set_select_mode(Tree::SELECT_MULTI);
1773
tree->set_cursor_can_exit_tree(false);
1774
editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeEditor::_selection_changed));
1775
}
1776
1777
void SceneTreeEditor::_update_selection(TreeItem *item) {
1778
ERR_FAIL_NULL(item);
1779
1780
NodePath np = item->get_metadata(0);
1781
1782
if (!has_node(np)) {
1783
return;
1784
}
1785
1786
Node *n = get_node(np);
1787
1788
if (!n) {
1789
return;
1790
}
1791
1792
if (editor_selection->is_selected(n)) {
1793
if (!item->is_selected(0)) {
1794
item->select(0);
1795
}
1796
} else {
1797
if (item->is_selected(0)) {
1798
TreeItem *previous_cursor_item = tree->get_selected();
1799
item->deselect(0);
1800
if (previous_cursor_item) {
1801
previous_cursor_item->set_as_cursor(0);
1802
}
1803
}
1804
}
1805
1806
TreeItem *c = item->get_first_child();
1807
1808
while (c) {
1809
_update_selection(c);
1810
c = c->get_next();
1811
}
1812
}
1813
1814
void SceneTreeEditor::_selection_changed() {
1815
if (!editor_selection) {
1816
return;
1817
}
1818
1819
TreeItem *root = tree->get_root();
1820
1821
if (!root) {
1822
return;
1823
}
1824
_update_selection(root);
1825
}
1826
1827
void SceneTreeEditor::_cell_collapsed(Object *p_obj) {
1828
if (updating_tree) {
1829
return;
1830
}
1831
if (!can_rename) {
1832
return;
1833
}
1834
1835
TreeItem *ti = Object::cast_to<TreeItem>(p_obj);
1836
if (!ti) {
1837
return;
1838
}
1839
1840
bool collapsed = ti->is_collapsed();
1841
1842
NodePath np = ti->get_metadata(0);
1843
1844
Node *n = get_node(np);
1845
ERR_FAIL_NULL(n);
1846
1847
n->set_display_folded(collapsed);
1848
}
1849
1850
Variant SceneTreeEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
1851
if (!can_rename) {
1852
return Variant(); // Not editable tree.
1853
}
1854
1855
if (tree->get_button_id_at_position(p_point) != -1) {
1856
return Variant(); // Dragging from button.
1857
}
1858
1859
Vector<Node *> selected_nodes;
1860
Vector<Ref<Texture2D>> icons;
1861
TreeItem *next = tree->get_next_selected(nullptr);
1862
while (next) {
1863
NodePath np = next->get_metadata(0);
1864
1865
Node *n = get_node(np);
1866
if (n) {
1867
selected_nodes.push_back(n);
1868
icons.push_back(next->get_icon(0));
1869
}
1870
next = tree->get_next_selected(next);
1871
}
1872
1873
if (selected_nodes.is_empty()) {
1874
return Variant();
1875
}
1876
1877
VBoxContainer *vb = memnew(VBoxContainer);
1878
Array objs;
1879
int list_max = 10;
1880
float opacity_step = 1.0f / list_max;
1881
float opacity_item = 1.0f;
1882
for (int i = 0; i < selected_nodes.size(); i++) {
1883
if (i < list_max) {
1884
HBoxContainer *hb = memnew(HBoxContainer);
1885
TextureRect *tf = memnew(TextureRect);
1886
int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
1887
tf->set_custom_minimum_size(Size2(icon_size, icon_size));
1888
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
1889
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
1890
tf->set_texture(icons[i]);
1891
hb->add_child(tf);
1892
Label *label = memnew(Label(selected_nodes[i]->get_name()));
1893
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1894
hb->add_child(label);
1895
vb->add_child(hb);
1896
hb->set_modulate(Color(1, 1, 1, opacity_item));
1897
opacity_item -= opacity_step;
1898
}
1899
NodePath p = selected_nodes[i]->get_path();
1900
objs.push_back(p);
1901
}
1902
1903
set_drag_preview(vb);
1904
Dictionary drag_data;
1905
drag_data["type"] = "nodes";
1906
drag_data["nodes"] = objs;
1907
drag_data["scene_root"] = get_tree()->get_edited_scene_root();
1908
1909
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
1910
emit_signal(SNAME("nodes_dragged"));
1911
1912
return drag_data;
1913
}
1914
1915
bool SceneTreeEditor::_is_script_type(const StringName &p_type) const {
1916
return (script_types->has(p_type));
1917
}
1918
1919
bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
1920
if (!can_rename) {
1921
return false; // Not editable tree.
1922
}
1923
1924
Dictionary d = p_data;
1925
if (!d.has("type")) {
1926
return false;
1927
}
1928
1929
Object *data_root = d.get("scene_root", (Object *)nullptr);
1930
if (data_root && get_tree()->get_edited_scene_root() != data_root) {
1931
return false;
1932
}
1933
1934
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_selected() : tree->get_item_at_position(p_point);
1935
if (!item) {
1936
return false;
1937
}
1938
1939
int section = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_drop_section_at_position(tree->get_item_rect(item).position) : tree->get_drop_section_at_position(p_point);
1940
if (section < -1 || (section == -1 && !item->get_parent())) {
1941
return false;
1942
}
1943
1944
if (String(d["type"]) == "files") {
1945
Vector<String> files = d["files"];
1946
1947
if (files.is_empty()) {
1948
return false; // TODO Weird?
1949
}
1950
1951
if (_is_script_type(EditorFileSystem::get_singleton()->get_file_type(files[0]))) {
1952
tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
1953
return true;
1954
}
1955
1956
bool scene_drop = true;
1957
bool audio_drop = true;
1958
for (int i = 0; i < files.size(); i++) {
1959
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]);
1960
if (ftype != "PackedScene") {
1961
scene_drop = false;
1962
}
1963
if (audio_drop && !ClassDB::is_parent_class(ftype, "AudioStream")) {
1964
audio_drop = false;
1965
}
1966
}
1967
1968
if (scene_drop) {
1969
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
1970
return true;
1971
}
1972
1973
if (audio_drop) {
1974
if (files.size() > 1) {
1975
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
1976
} else {
1977
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
1978
}
1979
return true;
1980
}
1981
1982
if (files.size() > 1) {
1983
return false;
1984
}
1985
tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
1986
1987
return true;
1988
}
1989
1990
if (String(d["type"]) == "script_list_element") {
1991
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(d["script_list_element"]);
1992
if (se) {
1993
String sp = se->get_edited_resource()->get_path();
1994
if (_is_script_type(EditorFileSystem::get_singleton()->get_file_type(sp))) {
1995
tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM);
1996
return true;
1997
}
1998
}
1999
}
2000
2001
if (filter.is_empty() && String(d["type"]) == "nodes") {
2002
Array nodes = d["nodes"];
2003
2004
for (int i = 0; i < nodes.size(); i++) {
2005
Node *n = get_node(nodes[i]);
2006
// Nodes from an instantiated scene can't be rearranged.
2007
if (n && n->get_owner() && n->get_owner() != get_scene_node() && n->get_owner()->is_instance()) {
2008
return false;
2009
}
2010
}
2011
2012
return true;
2013
}
2014
2015
return false;
2016
}
2017
2018
void SceneTreeEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
2019
if (!can_drop_data_fw(p_point, p_data, p_from)) {
2020
return;
2021
}
2022
2023
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_selected() : tree->get_item_at_position(p_point);
2024
if (!item) {
2025
return;
2026
}
2027
int section = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_drop_section_at_position(tree->get_item_rect(item).position) : tree->get_drop_section_at_position(p_point);
2028
if (section < -1) {
2029
return;
2030
}
2031
2032
NodePath np = item->get_metadata(0);
2033
Node *n = get_node(np);
2034
if (!n) {
2035
return;
2036
}
2037
2038
Dictionary d = p_data;
2039
2040
if (String(d["type"]) == "nodes") {
2041
Array nodes = d["nodes"];
2042
emit_signal(SNAME("nodes_rearranged"), nodes, np, section);
2043
}
2044
2045
if (String(d["type"]) == "files") {
2046
Vector<String> files = d["files"];
2047
2048
String ftype = EditorFileSystem::get_singleton()->get_file_type(files[0]);
2049
if (_is_script_type(ftype)) {
2050
emit_signal(SNAME("script_dropped"), files[0], np);
2051
} else {
2052
emit_signal(SNAME("files_dropped"), files, np, section);
2053
}
2054
}
2055
2056
if (String(d["type"]) == "script_list_element") {
2057
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(d["script_list_element"]);
2058
if (se) {
2059
String sp = se->get_edited_resource()->get_path();
2060
if (_is_script_type(EditorFileSystem::get_singleton()->get_file_type(sp))) {
2061
emit_signal(SNAME("script_dropped"), sp, np);
2062
}
2063
}
2064
}
2065
}
2066
2067
void SceneTreeEditor::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) {
2068
if (p_button != MouseButton::RIGHT) {
2069
return;
2070
}
2071
_rmb_select(p_pos);
2072
}
2073
2074
void SceneTreeEditor::_rmb_select(const Vector2 &p_pos, MouseButton p_button) {
2075
if (p_button != MouseButton::RIGHT) {
2076
return;
2077
}
2078
emit_signal(SNAME("rmb_pressed"), tree->get_screen_position() + p_pos);
2079
}
2080
2081
void SceneTreeEditor::update_warning() {
2082
_warning_changed(nullptr);
2083
}
2084
2085
void SceneTreeEditor::_warning_changed(Node *p_for_node) {
2086
node_cache.mark_dirty(p_for_node);
2087
2088
// Should use a timer.
2089
update_timer->start();
2090
}
2091
2092
void SceneTreeEditor::set_auto_expand_selected(bool p_auto, bool p_update_settings) {
2093
if (p_update_settings) {
2094
EditorSettings::get_singleton()->set("docks/scene_tree/auto_expand_to_selected", p_auto);
2095
}
2096
auto_expand_selected = p_auto;
2097
}
2098
2099
void SceneTreeEditor::set_hide_filtered_out_parents(bool p_hide, bool p_update_settings) {
2100
if (p_hide == hide_filtered_out_parents) {
2101
return;
2102
}
2103
2104
if (p_update_settings) {
2105
EditorSettings::get_singleton()->set("docks/scene_tree/hide_filtered_out_parents", p_hide);
2106
}
2107
hide_filtered_out_parents = p_hide;
2108
2109
if (hide_filtered_out_parents) {
2110
_update_filter();
2111
} else {
2112
node_cache.force_update = true;
2113
_update_tree();
2114
}
2115
}
2116
2117
void SceneTreeEditor::set_accessibility_warnings(bool p_enable, bool p_update_settings) {
2118
if (p_update_settings) {
2119
EditorSettings::get_singleton()->set("docks/scene_tree/accessibility_warnings", p_enable);
2120
}
2121
accessibility_warnings = p_enable;
2122
}
2123
2124
void SceneTreeEditor::set_connect_to_script_mode(bool p_enable) {
2125
connect_to_script_mode = p_enable;
2126
_update_tree();
2127
}
2128
2129
void SceneTreeEditor::set_connecting_signal(bool p_enable) {
2130
connecting_signal = p_enable;
2131
_update_tree();
2132
}
2133
2134
void SceneTreeEditor::set_update_when_invisible(bool p_enable) {
2135
update_when_invisible = p_enable;
2136
_update_tree();
2137
}
2138
2139
void SceneTreeEditor::_bind_methods() {
2140
ClassDB::bind_method(D_METHOD("_update_tree"), &SceneTreeEditor::_update_tree, DEFVAL(false)); // Still used by UndoRedo.
2141
2142
ClassDB::bind_method(D_METHOD("update_tree"), &SceneTreeEditor::update_tree);
2143
2144
ADD_SIGNAL(MethodInfo("node_selected"));
2145
ADD_SIGNAL(MethodInfo("node_renamed"));
2146
ADD_SIGNAL(MethodInfo("node_prerename"));
2147
ADD_SIGNAL(MethodInfo("node_changed"));
2148
ADD_SIGNAL(MethodInfo("nodes_dragged"));
2149
ADD_SIGNAL(MethodInfo("nodes_rearranged", PropertyInfo(Variant::ARRAY, "paths"), PropertyInfo(Variant::NODE_PATH, "to_path"), PropertyInfo(Variant::INT, "type")));
2150
ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::NODE_PATH, "to_path"), PropertyInfo(Variant::INT, "type")));
2151
ADD_SIGNAL(MethodInfo("script_dropped", PropertyInfo(Variant::STRING, "file"), PropertyInfo(Variant::NODE_PATH, "to_path")));
2152
ADD_SIGNAL(MethodInfo("rmb_pressed", PropertyInfo(Variant::VECTOR2, "position")));
2153
2154
ADD_SIGNAL(MethodInfo("open"));
2155
ADD_SIGNAL(MethodInfo("open_script"));
2156
}
2157
2158
SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) :
2159
node_cache(this) {
2160
selected = nullptr;
2161
2162
can_rename = p_can_rename;
2163
can_open_instance = p_can_open_instance;
2164
editor_selection = nullptr;
2165
2166
if (p_label) {
2167
Label *label = memnew(Label);
2168
label->set_theme_type_variation("HeaderSmall");
2169
label->set_position(Point2(10, 0));
2170
label->set_text(TTR("Scene Tree (Nodes):"));
2171
2172
add_child(label);
2173
}
2174
2175
tree = memnew(Tree);
2176
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
2177
tree->set_anchor(SIDE_RIGHT, ANCHOR_END);
2178
tree->set_anchor(SIDE_BOTTOM, ANCHOR_END);
2179
tree->set_begin(Point2(0, p_label ? 18 : 0));
2180
tree->set_end(Point2(0, 0));
2181
tree->set_allow_reselect(true);
2182
tree->add_theme_constant_override("button_margin", 0);
2183
2184
add_child(tree);
2185
2186
SET_DRAG_FORWARDING_GCD(tree, SceneTreeEditor);
2187
if (p_can_rename) {
2188
tree->set_allow_rmb_select(true);
2189
tree->connect("item_mouse_selected", callable_mp(this, &SceneTreeEditor::_rmb_select));
2190
tree->connect("empty_clicked", callable_mp(this, &SceneTreeEditor::_empty_clicked));
2191
}
2192
2193
tree->connect("cell_selected", callable_mp(this, &SceneTreeEditor::_selected_changed));
2194
tree->connect("item_edited", callable_mp(this, &SceneTreeEditor::_edited));
2195
tree->connect("multi_selected", callable_mp(this, &SceneTreeEditor::_cell_multi_selected));
2196
tree->connect("button_clicked", callable_mp(this, &SceneTreeEditor::_cell_button_pressed));
2197
tree->connect("nothing_selected", callable_mp(this, &SceneTreeEditor::_deselect_items));
2198
2199
error = memnew(AcceptDialog);
2200
add_child(error);
2201
2202
warning = memnew(AcceptDialog);
2203
add_child(warning);
2204
warning->set_title(TTR("Node Configuration Warning!"));
2205
warning->set_flag(Window::FLAG_POPUP, true);
2206
2207
last_hash = 0;
2208
blocked = 0;
2209
2210
update_timer = memnew(Timer);
2211
update_timer->connect("timeout", callable_mp(this, &SceneTreeEditor::_update_tree).bind(false));
2212
update_timer->set_one_shot(true);
2213
update_timer->set_wait_time(0.5);
2214
add_child(update_timer);
2215
2216
update_node_tooltip_delay = memnew(Timer);
2217
update_node_tooltip_delay->set_wait_time(0.5);
2218
update_node_tooltip_delay->set_one_shot(true);
2219
add_child(update_node_tooltip_delay);
2220
2221
revoke_dialog = memnew(ConfirmationDialog);
2222
revoke_dialog->set_ok_button_text(TTR("Revoke"));
2223
add_child(revoke_dialog);
2224
revoke_dialog->connect(SceneStringName(confirmed), callable_mp(this, &SceneTreeEditor::_update_ask_before_revoking_unique_name));
2225
VBoxContainer *vb = memnew(VBoxContainer);
2226
revoke_dialog->add_child(vb);
2227
revoke_dialog_label = memnew(Label);
2228
revoke_dialog_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
2229
vb->add_child(revoke_dialog_label);
2230
ask_before_revoke_checkbox = memnew(CheckBox(TTR("Don't Ask Again")));
2231
ask_before_revoke_checkbox->set_tooltip_text(TTR("This dialog can also be enabled/disabled in the Editor Settings: Docks > Scene Tree > Ask Before Revoking Unique Name."));
2232
vb->add_child(ask_before_revoke_checkbox);
2233
2234
script_types = memnew(LocalVector<StringName>);
2235
ClassDB::get_inheriters_from_class("Script", *script_types);
2236
}
2237
2238
SceneTreeEditor::~SceneTreeEditor() {
2239
memdelete(script_types);
2240
}
2241
2242
/******** DIALOG *********/
2243
2244
void SceneTreeDialog::popup_scenetree_dialog(Node *p_selected_node, Node *p_marked_node, bool p_marked_node_selectable, bool p_marked_node_children_selectable) {
2245
get_scene_tree()->set_marked(p_marked_node, p_marked_node_selectable, p_marked_node_children_selectable);
2246
get_scene_tree()->set_selected(p_selected_node);
2247
popup_centered_clamped(Size2(350, 700) * EDSCALE);
2248
}
2249
2250
void SceneTreeDialog::_show_all_nodes_changed(bool p_button_pressed) {
2251
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_all_nodes_for_node_selection", p_button_pressed);
2252
tree->set_show_all_nodes(p_button_pressed);
2253
}
2254
2255
void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) {
2256
if (allowed_types_hbox) {
2257
allowed_types_hbox->queue_free();
2258
allowed_types_hbox = nullptr;
2259
valid_type_icons.clear();
2260
}
2261
2262
tree->set_valid_types(p_valid);
2263
2264
if (p_valid.is_empty()) {
2265
return;
2266
}
2267
2268
allowed_types_hbox = memnew(HBoxContainer);
2269
content->add_child(allowed_types_hbox);
2270
content->move_child(allowed_types_hbox, 0);
2271
2272
{
2273
Label *label = memnew(Label(TTRC("Allowed:")));
2274
allowed_types_hbox->add_child(label);
2275
}
2276
2277
HFlowContainer *hflow = memnew(HFlowContainer);
2278
allowed_types_hbox->add_child(hflow);
2279
hflow->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2280
2281
for (const StringName &type : p_valid) {
2282
HBoxContainer *hb = memnew(HBoxContainer);
2283
hflow->add_child(hb);
2284
2285
// Attempt to get the correct name and icon for script path types.
2286
String name = type;
2287
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type);
2288
2289
// If we can't find a global class icon, try to find one for the script.
2290
if (icon.is_null() && ResourceLoader::exists(type, "Script")) {
2291
Ref<Script> node_script = ResourceLoader::load(type);
2292
if (node_script.is_valid()) {
2293
name = name.get_file();
2294
icon = EditorNode::get_singleton()->get_object_icon(node_script.ptr());
2295
}
2296
}
2297
2298
TextureRect *trect = memnew(TextureRect);
2299
hb->add_child(trect);
2300
trect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
2301
trect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
2302
trect->set_meta("icon", icon);
2303
valid_type_icons.push_back(trect);
2304
2305
Label *label = memnew(Label);
2306
hb->add_child(label);
2307
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
2308
label->set_text(name);
2309
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
2310
}
2311
2312
show_all_nodes->show();
2313
if (is_inside_tree()) {
2314
_update_valid_type_icons();
2315
}
2316
}
2317
2318
void SceneTreeDialog::_notification(int p_what) {
2319
switch (p_what) {
2320
case NOTIFICATION_VISIBILITY_CHANGED: {
2321
if (is_visible()) {
2322
tree->update_tree();
2323
2324
// Select the search bar by default.
2325
callable_mp((Control *)filter, &Control::grab_focus).call_deferred(false);
2326
}
2327
} break;
2328
2329
case NOTIFICATION_ENTER_TREE: {
2330
connect(SceneStringName(confirmed), callable_mp(this, &SceneTreeDialog::_select));
2331
} break;
2332
2333
case NOTIFICATION_THEME_CHANGED: {
2334
_update_valid_type_icons();
2335
} break;
2336
2337
case NOTIFICATION_EXIT_TREE: {
2338
disconnect(SceneStringName(confirmed), callable_mp(this, &SceneTreeDialog::_select));
2339
} break;
2340
}
2341
}
2342
2343
void SceneTreeDialog::_update_valid_type_icons() {
2344
for (TextureRect *trect : valid_type_icons) {
2345
trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0));
2346
trect->set_texture(trect->get_meta("icon"));
2347
}
2348
}
2349
2350
void SceneTreeDialog::_cancel() {
2351
hide();
2352
}
2353
2354
void SceneTreeDialog::_select() {
2355
if (tree->get_selected()) {
2356
// The signal may cause another dialog to be displayed, so be sure to hide this one first.
2357
hide();
2358
emit_signal(SNAME("selected"), tree->get_selected()->get_path());
2359
}
2360
}
2361
2362
void SceneTreeDialog::_selected_changed() {
2363
get_ok_button()->set_disabled(!tree->get_selected());
2364
}
2365
2366
void SceneTreeDialog::_filter_changed(const String &p_filter) {
2367
tree->set_filter(p_filter);
2368
}
2369
2370
void SceneTreeDialog::_bind_methods() {
2371
ClassDB::bind_method("_cancel", &SceneTreeDialog::_cancel);
2372
2373
ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::NODE_PATH, "path")));
2374
}
2375
2376
LineEdit *SceneTreeDialog::get_filter_line_edit() {
2377
return filter;
2378
}
2379
2380
SceneTreeDialog::SceneTreeDialog() {
2381
set_title(TTRC("Select a Node"));
2382
content = memnew(VBoxContainer);
2383
add_child(content);
2384
2385
HBoxContainer *filter_hbc = memnew(HBoxContainer);
2386
content->add_child(filter_hbc);
2387
2388
filter = memnew(FilterLineEdit);
2389
filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2390
filter->set_placeholder(TTRC("Filter Nodes"));
2391
filter->add_theme_constant_override("minimum_character_width", 0);
2392
filter->connect(SceneStringName(text_changed), callable_mp(this, &SceneTreeDialog::_filter_changed));
2393
2394
register_text_enter(filter);
2395
2396
filter_hbc->add_child(filter);
2397
2398
// Add 'Show All' button to HBoxContainer next to the filter, visible only when valid_types is defined.
2399
show_all_nodes = memnew(CheckButton);
2400
show_all_nodes->set_text(TTRC("Show All"));
2401
show_all_nodes->connect(SceneStringName(toggled), callable_mp(this, &SceneTreeDialog::_show_all_nodes_changed));
2402
show_all_nodes->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
2403
show_all_nodes->hide();
2404
filter_hbc->add_child(show_all_nodes);
2405
2406
MarginContainer *mc = memnew(MarginContainer);
2407
mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2408
mc->set_theme_type_variation("NoBorderHorizontalWindow");
2409
content->add_child(mc);
2410
2411
tree = memnew(SceneTreeEditor(false, false, true));
2412
filter->set_forward_control(tree->get_scene_tree());
2413
tree->set_update_when_invisible(false);
2414
tree->get_scene_tree()->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTH);
2415
tree->get_scene_tree()->connect("item_activated", callable_mp(this, &SceneTreeDialog::_select));
2416
// Initialize button state, must be done after the tree has been created to update its 'show_all_nodes' flag.
2417
// This is also done before adding the tree to the content to avoid triggering unnecessary tree filtering.
2418
show_all_nodes->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_all_nodes_for_node_selection", false));
2419
mc->add_child(tree);
2420
2421
// Disable the OK button when no node is selected.
2422
get_ok_button()->set_disabled(!tree->get_selected());
2423
tree->connect("node_selected", callable_mp(this, &SceneTreeDialog::_selected_changed));
2424
}
2425
2426
/******** CACHE *********/
2427
2428
HashMap<Node *, SceneTreeEditor::CachedNode>::Iterator SceneTreeEditor::NodeCache::add(Node *p_node, TreeItem *p_item) {
2429
if (!p_node) {
2430
return HashMap<Node *, CachedNode>::Iterator();
2431
}
2432
2433
return cache.insert(p_node, CachedNode(p_node, p_item));
2434
}
2435
2436
HashMap<Node *, SceneTreeEditor::CachedNode>::Iterator SceneTreeEditor::NodeCache::get(Node *p_node, bool p_deleted_ok) {
2437
if (!p_node) {
2438
return HashMap<Node *, CachedNode>::Iterator();
2439
}
2440
2441
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
2442
if (I) {
2443
if (I->value.delete_serial != UINT16_MAX) {
2444
// Don't give us a node marked for deletion.
2445
if (!p_deleted_ok) {
2446
return HashMap<Node *, CachedNode>::Iterator();
2447
}
2448
2449
to_delete.erase(&I->value);
2450
I->value.delete_serial = UINT16_MAX;
2451
2452
// If we were resurrected from near-death we might have been renamed.
2453
// Make sure that we are updated properly.
2454
mark_dirty(p_node);
2455
mark_children_dirty(p_node, true);
2456
}
2457
}
2458
2459
return I;
2460
}
2461
2462
bool SceneTreeEditor::NodeCache::has(Node *p_node) {
2463
return get(p_node, false).operator bool();
2464
}
2465
2466
void SceneTreeEditor::NodeCache::remove(Node *p_node, bool p_recursive) {
2467
if (!p_node) {
2468
return;
2469
}
2470
2471
if (p_node == editor->selected) {
2472
editor->selected = nullptr;
2473
}
2474
2475
if (p_node == current_pinned_node) {
2476
current_pinned_node = nullptr;
2477
current_has_pin = false;
2478
}
2479
2480
editor->marked.erase(p_node);
2481
2482
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
2483
if (I) {
2484
if (editor->is_scene_tree_dock) {
2485
EditorNode::get_singleton()->update_resource_count(I->key, true);
2486
}
2487
if (p_recursive) {
2488
int cc = p_node->get_child_count(false);
2489
2490
for (int i = 0; i < cc; i++) {
2491
remove(p_node->get_child(i, false), p_recursive);
2492
}
2493
}
2494
2495
if (current_scene_id != p_node->get_instance_id()) {
2496
// Do not remove from the Tree control here. See delete_pending below.
2497
I->value.item->deselect(0);
2498
I->value.delete_serial = delete_serial;
2499
I->value.index = -1;
2500
I->value.cache_iterator = I;
2501
to_delete.insert(&I->value);
2502
} else {
2503
// If it is the root node, we leave the TreeItem and reuse it later.
2504
cache.remove(I);
2505
}
2506
}
2507
}
2508
2509
void SceneTreeEditor::NodeCache::mark_dirty(Node *p_node, bool p_parents) {
2510
Node *node = p_node;
2511
while (node) {
2512
HashMap<Node *, CachedNode>::Iterator I = cache.find(node);
2513
if (I) {
2514
I->value.dirty = true;
2515
}
2516
2517
if (!p_parents) {
2518
break;
2519
}
2520
2521
node = node->get_parent();
2522
}
2523
}
2524
2525
void SceneTreeEditor::NodeCache::mark_children_dirty(Node *p_node, bool p_recursive) {
2526
if (!p_node) {
2527
return;
2528
}
2529
2530
int cc = p_node->get_child_count(false);
2531
for (int i = 0; i < cc; i++) {
2532
Node *c = p_node->get_child(i, false);
2533
HashMap<Node *, CachedNode>::Iterator IC = cache.find(c);
2534
2535
if (IC) {
2536
IC->value.dirty = true;
2537
2538
if (p_recursive) {
2539
mark_children_dirty(c, p_recursive);
2540
}
2541
}
2542
}
2543
}
2544
2545
void SceneTreeEditor::NodeCache::delete_pending() {
2546
HashSet<CachedNode *>::Iterator I = to_delete.begin();
2547
while (I) {
2548
// We want to keep TreeItems around just long enough for a Node removal,
2549
// and immediate reinsertion. This is what happens with moves and
2550
// type changes.
2551
if (Math::abs((*I)->delete_serial - delete_serial) >= 2) {
2552
memdelete((*I)->item);
2553
cache.remove((*I)->cache_iterator);
2554
to_delete.remove(I);
2555
} else if (!(*I)->removed) {
2556
// We don't remove from the tree until now because if the node got
2557
// deleted from a @tool script the SceneTreeEditor might have had it
2558
// marked or selected before the node was removed. If we immediately
2559
// remove from the Tree control then we end up trying to scroll to an
2560
// Item without a parent.
2561
//
2562
// We might already be removed (and thus not have a parent) by rapid
2563
// undo/redo.
2564
if (!(*I)->removed) {
2565
TreeItem *parent = (*I)->item->get_parent();
2566
parent->remove_child((*I)->item);
2567
}
2568
(*I)->removed = true;
2569
}
2570
++I;
2571
}
2572
2573
++delete_serial;
2574
}
2575
2576
void SceneTreeEditor::NodeCache::clear() {
2577
for (CachedNode *E : to_delete) {
2578
// Only removed entries won't be automatically cleaned up by Tree::clear().
2579
if (E->removed) {
2580
memdelete(E->item);
2581
}
2582
}
2583
cache.clear();
2584
to_delete.clear();
2585
current_pinned_node = nullptr;
2586
current_has_pin = false;
2587
}
2588
2589