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