2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* scene_tree_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "scene_tree_editor.h"
2017-01-16 07:04:19 +00:00
2022-09-21 22:43:38 +00:00
# include "core/config/project_settings.h"
2023-09-06 19:02:52 +00:00
# include "core/object/script_language.h"
2024-02-05 22:31:19 +00:00
# include "editor/editor_dock_manager.h"
2022-02-12 01:46:22 +00:00
# include "editor/editor_file_system.h"
2019-12-24 07:17:23 +00:00
# include "editor/editor_node.h"
2022-11-19 11:45:49 +00:00
# include "editor/editor_settings.h"
2023-08-13 00:33:39 +00:00
# include "editor/editor_string_names.h"
2022-03-25 17:06:46 +00:00
# include "editor/editor_undo_redo_manager.h"
2019-12-24 07:17:23 +00:00
# include "editor/node_dock.h"
2018-06-07 15:46:14 +00:00
# include "editor/plugins/animation_player_editor_plugin.h"
2014-02-10 01:10:30 +00:00
# include "editor/plugins/canvas_item_editor_plugin.h"
2022-02-14 13:00:03 +00:00
# include "editor/plugins/script_editor_plugin.h"
2024-01-15 12:14:55 +00:00
# include "editor/themes/editor_scale.h"
2023-07-17 21:40:27 +00:00
# include "scene/gui/flow_container.h"
2014-02-10 01:10:30 +00:00
# include "scene/gui/label.h"
2023-04-07 16:59:49 +00:00
# include "scene/gui/texture_rect.h"
2020-03-04 01:51:12 +00:00
# include "scene/main/window.h"
2015-10-10 12:09:09 +00:00
# include "scene/resources/packed_scene.h"
2023-11-01 13:56:14 +00:00
Node * SceneTreeEditor : : get_scene_node ( ) const {
2020-04-01 23:20:12 +00:00
ERR_FAIL_COND_V ( ! is_inside_tree ( ) , nullptr ) ;
2014-02-10 01:10:30 +00:00
2016-01-17 01:41:10 +00:00
return get_tree ( ) - > get_edited_scene_root ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-09-18 07:33:18 +00:00
void SceneTreeEditor : : _cell_button_pressed ( Object * p_item , int p_column , int p_id , MouseButton p_button ) {
if ( p_button ! = MouseButton : : LEFT ) {
return ;
}
2019-04-12 02:21:48 +00:00
if ( connect_to_script_mode ) {
2019-05-19 10:34:40 +00:00
return ; //don't do anything in this mode
2019-04-12 02:21:48 +00:00
}
2019-06-06 02:13:57 +00:00
2017-08-24 20:58:51 +00:00
TreeItem * item = Object : : cast_to < TreeItem > ( p_item ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( item ) ;
2014-02-10 01:10:30 +00:00
NodePath np = item - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( n ) ;
2014-02-10 01:10:30 +00:00
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2014-02-10 01:10:30 +00:00
if ( p_id = = BUTTON_SUBSCENE ) {
2015-10-10 12:09:09 +00:00
if ( n = = get_scene_node ( ) ) {
2017-06-30 16:17:33 +00:00
if ( n & & n - > get_scene_inherited_state ( ) . is_valid ( ) ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " open " ) , n - > get_scene_inherited_state ( ) - > get_path ( ) ) ;
2015-10-16 22:11:23 +00:00
}
2017-06-30 16:17:33 +00:00
} else {
2021-09-30 14:30:55 +00:00
emit_signal ( SNAME ( " open " ) , n - > get_scene_file_path ( ) ) ;
2015-10-10 12:09:09 +00:00
}
2014-02-10 01:10:30 +00:00
} else if ( p_id = = BUTTON_SCRIPT ) {
2020-02-13 19:03:10 +00:00
Ref < Script > script_typed = n - > get_script ( ) ;
2020-05-14 14:41:43 +00:00
if ( ! script_typed . is_null ( ) ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " open_script " ) , script_typed ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
} else if ( p_id = = BUTTON_VISIBILITY ) {
2018-01-01 08:05:00 +00:00
undo_redo - > create_action ( TTR ( " Toggle Visible " ) ) ;
2018-05-15 14:13:48 +00:00
_toggle_visible ( n ) ;
List < Node * > selection = editor_selection - > get_selected_node_list ( ) ;
2020-04-01 23:20:12 +00:00
if ( selection . size ( ) > 1 & & selection . find ( n ) ! = nullptr ) {
2021-07-16 03:45:57 +00:00
for ( Node * nv : selection ) {
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( nv ) ;
2018-05-15 14:13:48 +00:00
if ( nv = = n ) {
continue ;
}
_toggle_visible ( nv ) ;
}
}
2018-01-01 08:05:00 +00:00
undo_redo - > commit_action ( ) ;
2014-05-08 05:43:19 +00:00
} else if ( p_id = = BUTTON_LOCK ) {
2019-04-26 17:23:50 +00:00
undo_redo - > create_action ( TTR ( " Unlock Node " ) ) ;
2023-12-17 17:26:41 +00:00
undo_redo - > add_do_method ( n , " remove_meta " , " _edit_lock_ " ) ;
undo_redo - > add_undo_method ( n , " set_meta " , " _edit_lock_ " , true ) ;
undo_redo - > add_do_method ( this , " _update_tree " ) ;
undo_redo - > add_undo_method ( this , " _update_tree " ) ;
undo_redo - > add_do_method ( this , " emit_signal " , " node_changed " ) ;
undo_redo - > add_undo_method ( this , " emit_signal " , " node_changed " ) ;
2019-04-26 17:23:50 +00:00
undo_redo - > commit_action ( ) ;
2018-06-07 15:46:14 +00:00
} else if ( p_id = = BUTTON_PIN ) {
2023-07-20 15:34:06 +00:00
if ( n - > is_class ( " AnimationMixer " ) ) {
2021-02-04 08:20:26 +00:00
AnimationPlayerEditor : : get_singleton ( ) - > unpin ( ) ;
2018-06-07 15:46:14 +00:00
_update_tree ( ) ;
}
2014-05-08 05:43:19 +00:00
} else if ( p_id = = BUTTON_GROUP ) {
2023-12-22 11:53:39 +00:00
undo_redo - > create_action ( TTR ( " Ungroup Children " ) ) ;
2019-04-26 17:23:50 +00:00
2020-03-26 21:49:16 +00:00
if ( n - > is_class ( " CanvasItem " ) | | n - > is_class ( " Node3D " ) ) {
2019-04-26 17:23:50 +00:00
undo_redo - > add_do_method ( n , " remove_meta " , " _edit_group_ " ) ;
undo_redo - > add_undo_method ( n , " set_meta " , " _edit_group_ " , true ) ;
2022-03-27 19:10:57 +00:00
undo_redo - > add_do_method ( this , " _update_tree " ) ;
undo_redo - > add_undo_method ( this , " _update_tree " ) ;
2019-04-26 17:23:50 +00:00
undo_redo - > add_do_method ( this , " emit_signal " , " node_changed " ) ;
undo_redo - > add_undo_method ( this , " emit_signal " , " node_changed " ) ;
2014-05-08 05:43:19 +00:00
}
2019-04-26 17:23:50 +00:00
undo_redo - > commit_action ( ) ;
2016-05-17 21:27:15 +00:00
} else if ( p_id = = BUTTON_WARNING ) {
2024-02-17 18:04:18 +00:00
const PackedStringArray warnings = n - > get_configuration_warnings ( ) ;
2024-01-23 17:47:26 +00:00
if ( warnings . is_empty ( ) ) {
2016-05-17 21:27:15 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2022-08-18 08:20:20 +00:00
2024-02-17 18:04:18 +00:00
// Improve looks on tooltip, extra spacing on non-bullet point newlines.
const String bullet_point = U " • " ;
String all_warnings ;
for ( const String & w : warnings ) {
all_warnings + = " \n " + bullet_point + w ;
}
// Limit the line width while keeping some padding.
// It is not efficient, but it does not have to be.
const PackedInt32Array boundaries = TS - > string_get_word_breaks ( all_warnings , " " , 80 ) ;
PackedStringArray lines ;
for ( int i = 0 ; i < boundaries . size ( ) ; i + = 2 ) {
const int start = boundaries [ i ] ;
const int end = boundaries [ i + 1 ] ;
const String line = all_warnings . substr ( start , end - start ) ;
lines . append ( line ) ;
}
all_warnings = String ( " \n " ) . join ( lines ) . indent ( " " ) . replace ( U " • " , U " \n • " ) . substr ( 2 ) ; // We don't want the first two newlines.
warning - > set_text ( all_warnings ) ;
2020-03-06 17:00:16 +00:00
warning - > popup_centered ( ) ;
2016-05-17 21:27:15 +00:00
2016-06-04 16:17:56 +00:00
} else if ( p_id = = BUTTON_SIGNALS ) {
2016-07-16 21:22:44 +00:00
editor_selection - > clear ( ) ;
editor_selection - > add_node ( n ) ;
set_selected ( n ) ;
2024-02-05 22:31:19 +00:00
EditorDockManager : : get_singleton ( ) - > focus_dock ( NodeDock : : get_singleton ( ) ) ;
2021-11-17 20:08:55 +00:00
NodeDock : : get_singleton ( ) - > show_connections ( ) ;
2016-06-04 16:17:56 +00:00
} else if ( p_id = = BUTTON_GROUPS ) {
2016-07-16 21:22:44 +00:00
editor_selection - > clear ( ) ;
editor_selection - > add_node ( n ) ;
set_selected ( n ) ;
2024-02-05 22:31:19 +00:00
EditorDockManager : : get_singleton ( ) - > focus_dock ( NodeDock : : get_singleton ( ) ) ;
2021-11-17 20:08:55 +00:00
NodeDock : : get_singleton ( ) - > show_groups ( ) ;
2022-04-16 10:23:32 +00:00
} else if ( p_id = = BUTTON_UNIQUE ) {
undo_redo - > create_action ( TTR ( " Disable Scene Unique Name " ) ) ;
undo_redo - > add_do_method ( n , " set_unique_name_in_owner " , false ) ;
undo_redo - > add_undo_method ( n , " set_unique_name_in_owner " , true ) ;
undo_redo - > add_do_method ( this , " _update_tree " ) ;
undo_redo - > add_undo_method ( this , " _update_tree " ) ;
undo_redo - > commit_action ( ) ;
2014-02-10 01:10:30 +00:00
}
}
2020-05-14 12:29:06 +00:00
2018-01-01 08:05:00 +00:00
void SceneTreeEditor : : _toggle_visible ( Node * p_node ) {
2018-05-15 14:13:48 +00:00
if ( p_node - > has_method ( " is_visible " ) & & p_node - > has_method ( " set_visible " ) ) {
2018-01-01 08:05:00 +00:00
bool v = bool ( p_node - > call ( " is_visible " ) ) ;
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2018-05-15 14:13:48 +00:00
undo_redo - > add_do_method ( p_node , " set_visible " , ! v ) ;
undo_redo - > add_undo_method ( p_node , " set_visible " , v ) ;
2018-01-01 08:05:00 +00:00
}
}
2014-02-10 01:10:30 +00:00
2022-05-24 15:24:25 +00:00
void SceneTreeEditor : : _add_nodes ( Node * p_node , TreeItem * p_parent ) {
2020-05-14 14:41:43 +00:00
if ( ! p_node ) {
2022-05-24 15:24:25 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
// only owned nodes are editable, since nodes can create their own (manually owned) child nodes,
// which the editor needs not to know about.
2014-09-03 02:13:40 +00:00
bool part_of_subscene = false ;
2015-10-10 12:09:09 +00:00
if ( ! display_foreign & & p_node - > get_owner ( ) ! = get_scene_node ( ) & & p_node ! = get_scene_node ( ) ) {
if ( ( show_enabled_subscene | | can_open_instance ) & & p_node - > get_owner ( ) & & ( get_scene_node ( ) - > is_editable_instance ( p_node - > get_owner ( ) ) ) ) {
2014-09-03 02:13:40 +00:00
part_of_subscene = true ;
//allow
} else {
2022-05-24 15:24:25 +00:00
return ;
2014-09-03 02:13:40 +00:00
}
2015-10-10 12:09:09 +00:00
} else {
2016-02-09 19:30:50 +00:00
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 ;
2014-09-03 02:13:40 +00:00
}
2014-02-10 01:10:30 +00:00
TreeItem * item = tree - > create_item ( p_parent ) ;
2018-06-27 23:50:25 +00:00
2014-02-10 01:10:30 +00:00
item - > set_text ( 0 , p_node - > get_name ( ) ) ;
2020-05-14 14:41:43 +00:00
if ( can_rename & & ! part_of_subscene ) {
2014-02-10 01:10:30 +00:00
item - > set_editable ( 0 , true ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
item - > set_selectable ( 0 , true ) ;
if ( can_rename ) {
2016-06-28 16:10:15 +00:00
bool collapsed = p_node - > is_displayed_folded ( ) ;
2020-05-14 14:41:43 +00:00
if ( collapsed ) {
2014-02-10 01:10:30 +00:00
item - > set_collapsed ( true ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2019-06-11 18:43:37 +00:00
Ref < Texture2D > icon = EditorNode : : get_singleton ( ) - > get_object_icon ( p_node , " Node " ) ;
2014-02-10 01:10:30 +00:00
item - > set_icon ( 0 , icon ) ;
2016-03-08 23:00:52 +00:00
item - > set_metadata ( 0 , p_node - > get_path ( ) ) ;
2015-10-10 12:09:09 +00:00
2024-06-01 14:27:01 +00:00
if ( connecting_signal ) {
// Add script icons for all scripted nodes.
Ref < Script > scr = p_node - > get_script ( ) ;
if ( scr . is_valid ( ) ) {
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Script " ) ) , BUTTON_SCRIPT ) ;
if ( EditorNode : : get_singleton ( ) - > get_object_custom_type_base ( p_node ) = = scr ) {
// Disable button on custom scripts (pure visual cue).
item - > set_button_disabled ( 0 , item - > get_button_count ( 0 ) - 1 , true ) ;
}
}
}
2019-04-12 02:21:48 +00:00
if ( connect_to_script_mode ) {
2023-08-13 00:33:39 +00:00
Color accent = get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ;
2019-04-12 02:21:48 +00:00
2022-09-29 09:53:28 +00:00
Ref < Script > scr = p_node - > get_script ( ) ;
2024-06-01 14:27:01 +00:00
bool has_custom_script = scr . is_valid ( ) & & EditorNode : : get_singleton ( ) - > get_object_custom_type_base ( p_node ) = = scr ;
if ( scr . is_null ( ) | | has_custom_script ) {
2024-01-15 12:14:55 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ) ;
2019-04-12 02:21:48 +00:00
item - > set_selectable ( 0 , false ) ;
2019-07-19 19:21:30 +00:00
2019-04-12 02:21:48 +00:00
accent . a * = 0.7 ;
}
if ( marked . has ( p_node ) ) {
2019-06-06 02:13:57 +00:00
String node_name = p_node - > get_name ( ) ;
if ( connecting_signal ) {
node_name + = " " + TTR ( " (Connecting From) " ) ;
}
item - > set_text ( 0 , node_name ) ;
2023-08-28 00:37:47 +00:00
_set_item_custom_color ( item , accent ) ;
2019-04-12 02:21:48 +00:00
}
} else if ( part_of_subscene ) {
2018-06-27 23:50:25 +00:00
if ( valid_types . size ( ) = = 0 ) {
2023-08-13 00:33:39 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ) ;
2018-06-27 23:50:25 +00:00
}
2014-09-03 02:13:40 +00:00
} else if ( marked . has ( p_node ) ) {
2019-06-06 02:13:57 +00:00
String node_name = p_node - > get_name ( ) ;
if ( connecting_signal ) {
node_name + = " " + TTR ( " (Connecting From) " ) ;
2019-04-12 02:21:48 +00:00
}
2019-06-06 02:13:57 +00:00
item - > set_text ( 0 , node_name ) ;
item - > set_selectable ( 0 , marked_selectable ) ;
2023-08-13 00:33:39 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ) ;
2021-02-18 18:52:29 +00:00
} else if ( ! p_node - > can_process ( ) ) {
2024-01-15 12:14:55 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ) ;
2014-02-10 01:10:30 +00:00
} else if ( ! marked_selectable & & ! marked_children_selectable ) {
Node * node = p_node ;
while ( node ) {
if ( marked . has ( node ) ) {
item - > set_selectable ( 0 , false ) ;
2023-08-13 00:33:39 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
2014-02-10 01:10:30 +00:00
break ;
}
node = node - > get_parent ( ) ;
}
}
2016-06-04 16:17:56 +00:00
if ( can_rename ) { //should be can edit..
2024-02-17 18:04:18 +00:00
const PackedStringArray warnings = p_node - > get_configuration_warnings ( ) ;
2024-01-23 17:47:26 +00:00
const int num_warnings = warnings . size ( ) ;
if ( num_warnings > 0 ) {
2024-06-03 11:42:31 +00:00
StringName warning_icon ;
2024-02-17 18:04:18 +00:00
if ( num_warnings = = 1 ) {
warning_icon = SNAME ( " NodeWarning " ) ;
} else if ( num_warnings < = 3 ) {
warning_icon = vformat ( " NodeWarnings%d " , num_warnings ) ;
} else {
warning_icon = SNAME ( " NodeWarnings4Plus " ) ;
}
// Improve looks on tooltip, extra spacing on non-bullet point newlines.
const String bullet_point = U " • " ;
String all_warnings ;
for ( const String & w : warnings ) {
all_warnings + = " \n \n " + bullet_point + w . replace ( " \n " , " \n " ) ;
}
if ( num_warnings = = 1 ) {
all_warnings . remove_at ( 0 ) ; // With only one warning, two newlines do not look great.
}
item - > add_button ( 0 , get_editor_theme_icon ( warning_icon ) , BUTTON_WARNING , false , TTR ( " Node configuration warning: " ) + all_warnings ) ;
2016-06-04 16:17:56 +00:00
}
2022-04-16 10:23:32 +00:00
if ( p_node - > is_unique_name_in_owner ( ) ) {
2023-10-15 04:58:19 +00:00
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " SceneUniqueName " ) ) , BUTTON_UNIQUE , p_node - > get_owner ( ) ! = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) , vformat ( TTR ( " This node can be accessed from within anywhere in the scene by preceding it with the '%s' prefix in a node path. \n Click to disable this. " ) , UNIQUE_NODE_PREFIX ) ) ;
2022-04-16 10:23:32 +00:00
}
2019-08-16 20:30:31 +00:00
int num_connections = p_node - > get_persistent_signal_connection_count ( ) ;
int num_groups = p_node - > get_persistent_group_count ( ) ;
2020-07-16 08:52:06 +00:00
String msg_temp ;
if ( num_connections > = 1 ) {
Array arr ;
arr . push_back ( num_connections ) ;
msg_temp + = TTRN ( " Node has one connection. " , " Node has {num} connections. " , num_connections ) . format ( arr , " {num} " ) ;
2022-05-12 13:18:27 +00:00
if ( num_groups > = 1 ) {
msg_temp + = " \n " ;
}
2020-07-16 08:52:06 +00:00
}
if ( num_groups > = 1 ) {
2022-05-12 13:18:27 +00:00
msg_temp + = TTRN ( " Node is in this group: " , " Node is in the following groups: " , num_groups ) + " \n " ;
List < GroupInfo > groups ;
p_node - > get_groups ( & groups ) ;
for ( const GroupInfo & E : groups ) {
if ( E . persistent ) {
msg_temp + = String : : utf8 ( " • " ) + String ( E . name ) + " \n " ;
}
}
2020-07-16 08:52:06 +00:00
}
if ( num_connections > = 1 | | num_groups > = 1 ) {
2022-05-12 13:18:27 +00:00
if ( num_groups < 1 ) {
msg_temp + = " \n " ;
}
msg_temp + = TTR ( " Click to show signals dock. " ) ;
2020-07-16 08:52:06 +00:00
}
Ref < Texture2D > icon_temp ;
2021-04-25 14:40:58 +00:00
SceneTreeEditorButton signal_temp = BUTTON_SIGNALS ;
2019-08-16 20:30:31 +00:00
if ( num_connections > = 1 & & num_groups > = 1 ) {
2023-08-13 00:33:39 +00:00
icon_temp = get_editor_theme_icon ( SNAME ( " SignalsAndGroups " ) ) ;
2019-08-16 20:30:31 +00:00
} else if ( num_connections > = 1 ) {
2023-08-13 00:33:39 +00:00
icon_temp = get_editor_theme_icon ( SNAME ( " Signals " ) ) ;
2019-08-16 20:30:31 +00:00
} else if ( num_groups > = 1 ) {
2023-08-13 00:33:39 +00:00
icon_temp = get_editor_theme_icon ( SNAME ( " Groups " ) ) ;
2020-07-16 08:52:06 +00:00
signal_temp = BUTTON_GROUPS ;
}
if ( num_connections > = 1 | | num_groups > = 1 ) {
item - > add_button ( 0 , icon_temp , signal_temp , false , msg_temp ) ;
2016-06-04 16:17:56 +00:00
}
2016-05-17 21:27:15 +00:00
}
2023-05-02 10:17:09 +00:00
{
2023-11-14 05:00:41 +00:00
_update_node_tooltip ( p_node , item ) ;
Callable delay_update_tooltip = callable_mp ( this , & SceneTreeEditor : : _queue_update_node_tooltip ) ;
if ( p_node - > is_connected ( " editor_description_changed " , delay_update_tooltip ) ) {
p_node - > disconnect ( " editor_description_changed " , delay_update_tooltip ) ;
2019-08-15 12:50:26 +00:00
}
2023-11-14 05:00:41 +00:00
p_node - > connect ( " editor_description_changed " , delay_update_tooltip . bind ( item ) ) ;
2014-02-10 01:10:30 +00:00
}
2022-08-29 10:10:32 +00:00
if ( can_open_instance & & is_scene_tree_dock ) { // Show buttons only when necessary (SceneTreeDock) to avoid crashes.
2024-05-13 14:56:03 +00:00
if ( ! p_node - > is_connected ( CoreStringName ( script_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_script_changed ) ) ) {
p_node - > connect ( CoreStringName ( script_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_script_changed ) . bind ( p_node ) ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2022-09-29 09:53:28 +00:00
Ref < Script > scr = p_node - > get_script ( ) ;
if ( ! scr . is_null ( ) ) {
2022-08-30 14:44:57 +00:00
String additional_notes ;
2022-09-06 08:14:28 +00:00
Color button_color = Color ( 1 , 1 , 1 ) ;
2022-08-30 14:44:57 +00:00
// Can't set tooltip after adding button, need to do it before.
2022-09-29 09:53:28 +00:00
if ( scr - > is_tool ( ) ) {
2022-08-30 14:44:57 +00:00
additional_notes + = " \n " + TTR ( " This script is currently running in the editor. " ) ;
2023-08-13 00:33:39 +00:00
button_color = get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ;
2022-08-30 14:44:57 +00:00
}
2022-09-29 09:53:28 +00:00
if ( EditorNode : : get_singleton ( ) - > get_object_custom_type_base ( p_node ) = = scr ) {
2022-09-06 08:14:28 +00:00
additional_notes + = " \n " + TTR ( " This script is a custom type. " ) ;
button_color . a = 0.5 ;
2022-08-30 14:44:57 +00:00
}
2023-08-13 00:33:39 +00:00
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Script " ) ) , BUTTON_SCRIPT , false , TTR ( " Open Script: " ) + " " + scr - > get_path ( ) + additional_notes ) ;
2022-09-06 08:14:28 +00:00
item - > set_button_color ( 0 , item - > get_button_count ( 0 ) - 1 , button_color ) ;
2014-02-10 01:10:30 +00:00
}
2024-05-13 14:56:03 +00:00
if ( p_node - > has_method ( " is_visible " ) & & p_node - > has_method ( " set_visible " ) & & p_node - > has_signal ( SceneStringName ( visibility_changed ) ) ) {
2023-12-17 17:26:41 +00:00
bool is_visible = p_node - > call ( " is_visible " ) ;
if ( is_visible ) {
2023-08-13 00:33:39 +00:00
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " GuiVisibilityVisible " ) ) , BUTTON_VISIBILITY , false , TTR ( " Toggle Visibility " ) ) ;
2020-05-14 14:41:43 +00:00
} else {
2023-08-13 00:33:39 +00:00
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " GuiVisibilityHidden " ) ) , BUTTON_VISIBILITY , false , TTR ( " Toggle Visibility " ) ) ;
2020-05-14 14:41:43 +00:00
}
2023-12-17 17:26:41 +00:00
const Callable vis_changed = callable_mp ( this , & SceneTreeEditor : : _node_visibility_changed ) ;
2024-05-13 14:56:03 +00:00
if ( ! p_node - > is_connected ( SceneStringName ( visibility_changed ) , vis_changed ) ) {
p_node - > connect ( SceneStringName ( visibility_changed ) , vis_changed . bind ( p_node ) ) ;
2020-05-14 14:41:43 +00:00
}
2016-12-28 13:12:08 +00:00
_update_visibility_color ( p_node , item ) ;
2023-12-17 17:26:41 +00:00
}
2014-02-10 01:10:30 +00:00
2023-12-17 17:26:41 +00:00
if ( p_node - > has_meta ( " _edit_lock_ " ) ) {
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Lock " ) ) , BUTTON_LOCK , false , TTR ( " Node is locked. \n Click to unlock it. " ) ) ;
}
if ( p_node - > has_meta ( " _edit_group_ " ) ) {
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Group " ) ) , BUTTON_GROUP , false , TTR ( " Children are not selectable. \n Click to make them selectable. " ) ) ;
}
2014-02-10 01:10:30 +00:00
2023-12-17 17:26:41 +00:00
if ( p_node - > is_class ( " AnimationMixer " ) ) {
2023-07-20 15:34:06 +00:00
bool is_pinned = AnimationPlayerEditor : : get_singleton ( ) - > get_editing_node ( ) = = p_node & & AnimationPlayerEditor : : get_singleton ( ) - > is_pinned ( ) ;
2018-06-07 15:46:14 +00:00
if ( is_pinned ) {
2023-08-13 00:33:39 +00:00
item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " Pin " ) ) , BUTTON_PIN , false , TTR ( " AnimationPlayer is pinned. \n Click to unpin. " ) ) ;
2018-06-07 15:46:14 +00:00
}
2014-02-10 01:10:30 +00:00
}
}
if ( editor_selection ) {
if ( editor_selection - > is_selected ( p_node ) ) {
item - > select ( 0 ) ;
}
}
if ( selected = = p_node ) {
2020-05-14 14:41:43 +00:00
if ( ! editor_selection ) {
2014-02-10 01:10:30 +00:00
item - > select ( 0 ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
item - > set_as_cursor ( 0 ) ;
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
2022-05-24 15:24:25 +00:00
_add_nodes ( p_node - > get_child ( i ) , item ) ;
2014-02-10 01:10:30 +00:00
}
2016-05-16 15:23:40 +00:00
2018-06-27 23:50:25 +00:00
if ( valid_types . size ( ) ) {
bool valid = false ;
2022-10-07 22:16:54 +00:00
for ( const StringName & E : valid_types ) {
if ( p_node - > is_class ( E ) | |
EditorNode : : get_singleton ( ) - > is_object_of_custom_type ( p_node , E ) ) {
2018-06-27 23:50:25 +00:00
valid = true ;
break ;
2023-09-29 13:58:13 +00:00
} else {
Ref < Script > node_script = p_node - > get_script ( ) ;
while ( node_script . is_valid ( ) ) {
if ( node_script - > get_path ( ) = = E ) {
valid = true ;
break ;
}
node_script = node_script - > get_base_script ( ) ;
}
if ( valid ) {
break ;
}
2018-06-27 23:50:25 +00:00
}
}
if ( ! valid ) {
2024-01-15 12:14:55 +00:00
_set_item_custom_color ( item , get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ) ;
2018-06-27 23:50:25 +00:00
item - > set_selectable ( 0 , false ) ;
}
}
2014-02-10 01:10:30 +00:00
}
2023-11-14 05:00:41 +00:00
void SceneTreeEditor : : _queue_update_node_tooltip ( Node * p_node , TreeItem * p_item ) {
Callable update_tooltip = callable_mp ( this , & SceneTreeEditor : : _update_node_tooltip ) ;
if ( update_node_tooltip_delay - > is_connected ( " timeout " , update_tooltip ) ) {
update_node_tooltip_delay - > disconnect ( " timeout " , update_tooltip ) ;
}
update_node_tooltip_delay - > connect ( " timeout " , update_tooltip . bind ( p_node , p_item ) ) ;
update_node_tooltip_delay - > start ( ) ;
}
void SceneTreeEditor : : _update_node_tooltip ( Node * p_node , TreeItem * p_item ) {
// Display the node name in all tooltips so that long node names can be previewed
// without having to rename them.
String tooltip = p_node - > get_name ( ) ;
if ( p_node = = get_scene_node ( ) & & p_node - > get_scene_inherited_state ( ) . is_valid ( ) ) {
p_item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " InstanceOptions " ) ) , BUTTON_SUBSCENE , false , TTR ( " Open in Editor " ) ) ;
tooltip + = String ( " \n " + TTR ( " Inherits: " ) + " " + p_node - > get_scene_inherited_state ( ) - > get_path ( ) ) ;
} else if ( p_node ! = get_scene_node ( ) & & ! p_node - > get_scene_file_path ( ) . is_empty ( ) & & can_open_instance ) {
p_item - > add_button ( 0 , get_editor_theme_icon ( SNAME ( " InstanceOptions " ) ) , BUTTON_SUBSCENE , false , TTR ( " Open in Editor " ) ) ;
tooltip + = String ( " \n " + TTR ( " Instance: " ) + " " + p_node - > get_scene_file_path ( ) ) ;
}
StringName custom_type = EditorNode : : get_singleton ( ) - > get_object_custom_type_name ( p_node ) ;
tooltip + = " \n " + TTR ( " Type: " ) + " " + ( custom_type ! = StringName ( ) ? String ( custom_type ) : p_node - > get_class ( ) ) ;
if ( ! p_node - > get_editor_description ( ) . is_empty ( ) ) {
const PackedInt32Array boundaries = TS - > string_get_word_breaks ( p_node - > get_editor_description ( ) , " " , 80 ) ;
tooltip + = " \n " ;
for ( int i = 0 ; i < boundaries . size ( ) ; i + = 2 ) {
const int start = boundaries [ i ] ;
const int end = boundaries [ i + 1 ] ;
tooltip + = " \n " + p_node - > get_editor_description ( ) . substr ( start , end - start + 1 ) . rstrip ( " \n " ) ;
}
}
p_item - > set_tooltip_text ( 0 , tooltip ) ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _node_visibility_changed ( Node * p_node ) {
2019-06-03 19:52:50 +00:00
if ( ! p_node | | ( p_node ! = get_scene_node ( ) & & ! p_node - > get_owner ( ) ) ) {
2014-08-14 13:31:38 +00:00
return ;
}
2019-06-03 19:52:50 +00:00
TreeItem * item = _find ( tree - > get_root ( ) , p_node - > get_path ( ) ) ;
if ( ! item ) {
2014-08-14 13:31:38 +00:00
return ;
}
2019-06-03 19:52:50 +00:00
2014-02-10 01:10:30 +00:00
int idx = item - > get_button_by_id ( 0 , BUTTON_VISIBILITY ) ;
ERR_FAIL_COND ( idx = = - 1 ) ;
2022-09-29 09:53:28 +00:00
bool node_visible = false ;
2014-02-10 01:10:30 +00:00
2023-12-17 17:26:41 +00:00
if ( p_node - > has_method ( " is_visible " ) ) {
2022-09-29 09:53:28 +00:00
node_visible = p_node - > call ( " is_visible " ) ;
2023-12-17 17:26:41 +00:00
if ( p_node - > is_class ( " CanvasItem " ) | | p_node - > is_class ( " CanvasLayer " ) | | p_node - > is_class ( " Window " ) ) {
CanvasItemEditor : : get_singleton ( ) - > get_viewport_control ( ) - > queue_redraw ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2022-09-29 09:53:28 +00:00
if ( node_visible ) {
2023-08-13 00:33:39 +00:00
item - > set_button ( 0 , idx , get_editor_theme_icon ( SNAME ( " GuiVisibilityVisible " ) ) ) ;
2020-05-14 14:41:43 +00:00
} else {
2023-08-13 00:33:39 +00:00
item - > set_button ( 0 , idx , get_editor_theme_icon ( SNAME ( " GuiVisibilityHidden " ) ) ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2016-12-28 13:12:08 +00:00
_update_visibility_color ( p_node , item ) ;
2014-02-10 01:10:30 +00:00
}
2016-12-28 13:12:08 +00:00
void SceneTreeEditor : : _update_visibility_color ( Node * p_node , TreeItem * p_item ) {
2023-12-17 17:26:41 +00:00
if ( p_node - > has_method ( " is_visible_in_tree " ) ) {
2016-12-28 13:12:08 +00:00
Color color ( 1 , 1 , 1 , 1 ) ;
2017-07-02 15:27:27 +00:00
bool visible_on_screen = p_node - > call ( " is_visible_in_tree " ) ;
2016-12-28 13:12:08 +00:00
if ( ! visible_on_screen ) {
2017-09-26 02:43:20 +00:00
color . a = 0.6 ;
2016-12-28 13:12:08 +00:00
}
int idx = p_item - > get_button_by_id ( 0 , BUTTON_VISIBILITY ) ;
p_item - > set_button_color ( 0 , idx , color ) ;
}
}
2014-02-10 01:10:30 +00:00
2023-08-28 00:37:47 +00:00
void SceneTreeEditor : : _set_item_custom_color ( TreeItem * p_item , Color p_color ) {
p_item - > set_custom_color ( 0 , p_color ) ;
p_item - > set_meta ( SNAME ( " custom_color " ) , p_color ) ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _node_script_changed ( Node * p_node ) {
2020-05-14 14:41:43 +00:00
if ( tree_dirty ) {
2017-11-15 13:41:31 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-11-15 13:41:31 +00:00
2023-12-18 14:46:56 +00:00
callable_mp ( this , & SceneTreeEditor : : _update_tree ) . call_deferred ( false ) ;
2017-11-15 13:41:31 +00:00
tree_dirty = true ;
2014-02-10 01:10:30 +00:00
}
void SceneTreeEditor : : _node_removed ( Node * p_node ) {
2020-05-14 14:41:43 +00:00
if ( EditorNode : : get_singleton ( ) - > is_exiting ( ) ) {
2016-06-08 01:08:12 +00:00
return ; //speed up exit
2020-05-14 14:41:43 +00:00
}
2016-06-08 01:08:12 +00:00
2024-05-13 14:56:03 +00:00
if ( p_node - > is_connected ( CoreStringName ( script_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_script_changed ) ) ) {
p_node - > disconnect ( CoreStringName ( script_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_script_changed ) ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2024-05-13 14:56:03 +00:00
if ( p_node - > has_signal ( SceneStringName ( visibility_changed ) ) ) {
if ( p_node - > is_connected ( SceneStringName ( visibility_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_visibility_changed ) ) ) {
p_node - > disconnect ( SceneStringName ( visibility_changed ) , callable_mp ( this , & SceneTreeEditor : : _node_visibility_changed ) ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
if ( p_node = = selected ) {
2020-04-01 23:20:12 +00:00
selected = nullptr ;
2014-02-10 01:10:30 +00:00
}
}
2019-05-24 13:27:22 +00:00
void SceneTreeEditor : : _node_renamed ( Node * p_node ) {
2021-09-23 07:19:21 +00:00
if ( p_node ! = get_scene_node ( ) & & ! get_scene_node ( ) - > is_ancestor_of ( p_node ) ) {
2021-04-12 11:26:56 +00:00
return ;
}
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " node_renamed " ) ) ;
2019-05-24 13:27:22 +00:00
if ( ! tree_dirty ) {
2023-12-18 14:46:56 +00:00
callable_mp ( this , & SceneTreeEditor : : _update_tree ) . call_deferred ( false ) ;
2019-05-24 13:27:22 +00:00
tree_dirty = true ;
}
}
2021-02-08 00:19:46 +00:00
void SceneTreeEditor : : _update_tree ( bool p_scroll_to_selected ) {
2014-11-06 00:20:42 +00:00
if ( ! is_inside_tree ( ) ) {
2014-02-10 01:10:30 +00:00
tree_dirty = false ;
return ;
}
2020-12-31 07:43:03 +00:00
if ( tree - > is_editing ( ) ) {
return ;
}
2014-02-10 01:10:30 +00:00
updating_tree = true ;
tree - > clear ( ) ;
2024-05-22 12:55:39 +00:00
last_hash = hash_djb2_one_64 ( 0 ) ;
2014-02-10 01:10:30 +00:00
if ( get_scene_node ( ) ) {
2022-05-24 15:24:25 +00:00
_add_nodes ( get_scene_node ( ) , nullptr ) ;
2014-02-10 01:10:30 +00:00
_compute_hash ( get_scene_node ( ) , last_hash ) ;
}
updating_tree = false ;
tree_dirty = false ;
2022-05-24 15:24:25 +00:00
2023-07-08 17:15:17 +00:00
if ( ! filter . strip_edges ( ) . is_empty ( ) | | ! show_all_nodes ) {
2022-05-24 15:24:25 +00:00
_update_filter ( nullptr , p_scroll_to_selected ) ;
}
}
bool SceneTreeEditor : : _update_filter ( TreeItem * p_parent , bool p_scroll_to_selected ) {
if ( ! p_parent ) {
p_parent = tree - > get_root ( ) ;
2022-09-16 13:10:28 +00:00
filter_term_warning . clear ( ) ;
2022-05-24 15:24:25 +00:00
}
2022-06-03 22:04:49 +00:00
if ( ! p_parent ) {
// Tree is empty, nothing to do here.
return false ;
}
2022-09-05 06:22:20 +00:00
bool keep_for_children = false ;
2022-05-24 15:24:25 +00:00
for ( TreeItem * child = p_parent - > get_first_child ( ) ; child ; child = child - > get_next ( ) ) {
2022-09-05 06:22:20 +00:00
// Always keep if at least one of the children are kept.
keep_for_children = _update_filter ( child , p_scroll_to_selected ) | | keep_for_children ;
2022-05-24 15:24:25 +00:00
}
2022-09-05 06:22:20 +00:00
// Now find other reasons to keep this Node, too.
PackedStringArray terms = filter . to_lower ( ) . split_spaces ( ) ;
bool keep = _item_matches_all_terms ( p_parent , terms ) ;
2023-07-08 17:15:17 +00:00
bool selectable = keep ;
2023-06-07 21:27:11 +00:00
if ( keep & & ! valid_types . is_empty ( ) ) {
2023-07-08 17:15:17 +00:00
selectable = false ;
2023-06-07 21:27:11 +00:00
Node * n = get_node ( p_parent - > get_metadata ( 0 ) ) ;
for ( const StringName & E : valid_types ) {
if ( n - > is_class ( E ) | |
EditorNode : : get_singleton ( ) - > is_object_of_custom_type ( n , E ) ) {
2023-07-08 17:15:17 +00:00
selectable = true ;
2023-06-07 21:27:11 +00:00
break ;
2023-09-29 13:58:13 +00:00
} else {
Ref < Script > node_script = n - > get_script ( ) ;
while ( node_script . is_valid ( ) ) {
if ( node_script - > get_path ( ) = = E ) {
selectable = true ;
break ;
}
node_script = node_script - > get_base_script ( ) ;
}
if ( selectable ) {
break ;
}
2023-06-07 21:27:11 +00:00
}
}
}
2023-07-08 17:15:17 +00:00
if ( show_all_nodes ) {
p_parent - > set_visible ( keep_for_children | | keep ) ;
} else {
// Show only selectable nodes, or parents of selectable.
p_parent - > set_visible ( keep_for_children | | selectable ) ;
}
if ( selectable ) {
2023-08-28 00:37:47 +00:00
Color custom_color = p_parent - > get_meta ( SNAME ( " custom_color " ) , Color ( 0 , 0 , 0 , 0 ) ) ;
if ( custom_color = = Color ( 0 , 0 , 0 , 0 ) ) {
p_parent - > clear_custom_color ( 0 ) ;
} else {
p_parent - > set_custom_color ( 0 , custom_color ) ;
}
2023-06-08 03:32:19 +00:00
p_parent - > set_selectable ( 0 , true ) ;
} else if ( keep_for_children ) {
2024-01-15 12:14:55 +00:00
p_parent - > set_custom_color ( 0 , get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ) ;
2023-06-08 03:32:19 +00:00
p_parent - > set_selectable ( 0 , false ) ;
p_parent - > deselect ( 0 ) ;
2022-05-24 15:24:25 +00:00
}
if ( editor_selection ) {
Node * n = get_node ( p_parent - > get_metadata ( 0 ) ) ;
2023-07-08 17:15:17 +00:00
if ( selectable ) {
2022-05-24 15:24:25 +00:00
if ( p_scroll_to_selected & & n & & editor_selection - > is_selected ( n ) ) {
tree - > scroll_to_item ( p_parent ) ;
}
} else {
if ( n & & p_parent - > is_selected ( 0 ) ) {
editor_selection - > remove_node ( n ) ;
p_parent - > deselect ( 0 ) ;
}
}
}
2023-07-08 17:15:17 +00:00
return p_parent - > is_visible ( ) ;
2022-09-05 06:22:20 +00:00
}
2024-02-15 16:25:58 +00:00
bool SceneTreeEditor : : _item_matches_all_terms ( TreeItem * p_item , const PackedStringArray & p_terms ) {
2022-09-05 06:22:20 +00:00
if ( p_terms . is_empty ( ) ) {
return true ;
}
for ( int i = 0 ; i < p_terms . size ( ) ; i + + ) {
2023-11-18 22:40:56 +00:00
const String & term = p_terms [ i ] ;
2022-09-05 06:22:20 +00:00
2023-01-21 11:25:29 +00:00
// Recognize special filter.
2022-09-05 06:22:20 +00:00
if ( term . contains ( " : " ) & & ! term . get_slicec ( ' : ' , 0 ) . is_empty ( ) ) {
String parameter = term . get_slicec ( ' : ' , 0 ) ;
String argument = term . get_slicec ( ' : ' , 1 ) ;
if ( parameter = = " type " | | parameter = = " t " ) {
// Filter by Type.
2022-09-16 21:01:33 +00:00
String type = get_node ( p_item - > get_metadata ( 0 ) ) - > get_class ( ) ;
bool term_in_inherited_class = false ;
2024-02-23 19:49:13 +00:00
// Every Node is a Node, duh!
2022-09-16 21:01:33 +00:00
while ( type ! = " Node " ) {
if ( type . to_lower ( ) . contains ( argument ) ) {
term_in_inherited_class = true ;
break ;
}
2022-09-05 06:22:20 +00:00
2022-09-16 21:01:33 +00:00
type = ClassDB : : get_parent_class ( type ) ;
}
if ( ! term_in_inherited_class ) {
2022-09-05 06:22:20 +00:00
return false ;
}
} else if ( parameter = = " group " | | parameter = = " g " ) {
// Filter by Group.
Node * node = get_node ( p_item - > get_metadata ( 0 ) ) ;
2022-09-16 21:01:33 +00:00
if ( argument . is_empty ( ) ) {
// When argument is empty, match all Nodes belonging to any exposed group.
if ( node - > get_persistent_group_count ( ) = = 0 ) {
return false ;
}
} else {
List < Node : : GroupInfo > group_info_list ;
node - > get_groups ( & group_info_list ) ;
2022-09-05 06:22:20 +00:00
bool term_in_groups = false ;
2024-04-15 13:18:34 +00:00
for ( const Node : : GroupInfo & group_info : group_info_list ) {
if ( ! group_info . persistent ) {
2022-09-16 21:01:33 +00:00
continue ; // Ignore internal groups.
2022-09-05 06:22:20 +00:00
}
2024-04-15 13:18:34 +00:00
if ( String ( group_info . name ) . to_lower ( ) . contains ( argument ) ) {
2022-09-05 06:22:20 +00:00
term_in_groups = true ;
break ;
}
}
if ( ! term_in_groups ) {
return false ;
}
}
2022-09-16 13:10:28 +00:00
} else if ( filter_term_warning . is_empty ( ) ) {
filter_term_warning = vformat ( TTR ( " \" %s \" is not a known filter. " ) , parameter ) ;
2022-09-05 06:22:20 +00:00
continue ;
}
} else {
// Default.
if ( ! p_item - > get_text ( 0 ) . to_lower ( ) . contains ( term ) ) {
return false ;
}
}
}
return true ;
2014-02-10 01:10:30 +00:00
}
void SceneTreeEditor : : _compute_hash ( Node * p_node , uint64_t & hash ) {
2017-08-07 10:17:31 +00:00
hash = hash_djb2_one_64 ( p_node - > get_instance_id ( ) , hash ) ;
2020-05-14 14:41:43 +00:00
if ( p_node - > get_parent ( ) ) {
2017-08-07 10:17:31 +00:00
hash = hash_djb2_one_64 ( p_node - > get_parent ( ) - > get_instance_id ( ) , hash ) ; //so a reparent still produces a different hash
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_compute_hash ( p_node - > get_child ( i ) , hash ) ;
}
}
void SceneTreeEditor : : _test_update_tree ( ) {
pending_test_update = false ;
2020-05-14 14:41:43 +00:00
if ( ! is_inside_tree ( ) ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( tree_dirty ) {
2014-02-10 01:10:30 +00:00
return ; // don't even bother
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
uint64_t hash = hash_djb2_one_64 ( 0 ) ;
2020-05-14 14:41:43 +00:00
if ( get_scene_node ( ) ) {
2014-02-10 01:10:30 +00:00
_compute_hash ( get_scene_node ( ) , hash ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
//test hash
2020-05-14 14:41:43 +00:00
if ( hash = = last_hash ) {
2014-02-10 01:10:30 +00:00
return ; // did not change
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-12-18 14:46:56 +00:00
callable_mp ( this , & SceneTreeEditor : : _update_tree ) . call_deferred ( false ) ;
2014-02-10 01:10:30 +00:00
tree_dirty = true ;
}
2021-02-18 18:52:29 +00:00
void SceneTreeEditor : : _tree_process_mode_changed ( ) {
2023-12-18 14:46:56 +00:00
callable_mp ( this , & SceneTreeEditor : : _update_tree ) . call_deferred ( false ) ;
2021-02-18 18:52:29 +00:00
tree_dirty = true ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _tree_changed ( ) {
2020-05-14 14:41:43 +00:00
if ( EditorNode : : get_singleton ( ) - > is_exiting ( ) ) {
2016-06-08 01:08:12 +00:00
return ; //speed up exit
2020-05-14 14:41:43 +00:00
}
if ( pending_test_update ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
if ( tree_dirty ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-12-18 14:46:56 +00:00
callable_mp ( this , & SceneTreeEditor : : _test_update_tree ) . call_deferred ( ) ;
2014-02-10 01:10:30 +00:00
pending_test_update = true ;
}
2021-07-23 08:34:21 +00:00
void SceneTreeEditor : : _selected_changed ( ) {
TreeItem * s = tree - > get_selected ( ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( s ) ;
2021-07-23 08:34:21 +00:00
NodePath np = s - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
if ( n = = selected ) {
return ;
}
selected = get_node ( np ) ;
blocked + + ;
2021-08-20 12:48:34 +00:00
emit_signal ( SNAME ( " node_selected " ) ) ;
2021-07-23 08:34:21 +00:00
blocked - - ;
}
2017-11-26 11:49:21 +00:00
void SceneTreeEditor : : _deselect_items ( ) {
2021-03-12 16:58:25 +00:00
// Clear currently selected items in scene tree dock.
2018-08-23 12:15:54 +00:00
if ( editor_selection ) {
2017-12-10 21:33:44 +00:00
editor_selection - > clear ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " node_changed " ) ) ;
2018-08-23 12:15:54 +00:00
}
2017-11-26 11:49:21 +00:00
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _cell_multi_selected ( Object * p_object , int p_cell , bool p_selected ) {
2017-08-24 20:58:51 +00:00
TreeItem * item = Object : : cast_to < TreeItem > ( p_object ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( item ) ;
2014-02-10 01:10:30 +00:00
2022-10-03 16:28:43 +00:00
if ( ! item - > is_visible ( ) ) {
return ;
}
2014-02-10 01:10:30 +00:00
NodePath np = item - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
2020-05-14 14:41:43 +00:00
if ( ! n ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( ! editor_selection ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
if ( p_selected ) {
editor_selection - > add_node ( n ) ;
} else {
editor_selection - > remove_node ( n ) ;
}
2021-06-19 02:25:24 +00:00
2021-07-23 08:37:35 +00:00
// Emitted "selected" in _selected_changed() when select single node, so select multiple node emit "changed"
if ( editor_selection - > get_selected_nodes ( ) . size ( ) > 1 ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " node_changed " ) ) ;
2021-06-19 02:25:24 +00:00
}
2014-02-10 01:10:30 +00:00
}
2024-04-05 14:51:54 +00:00
void SceneTreeEditor : : _tree_scroll_to_item ( ObjectID p_item_id ) {
ERR_FAIL_NULL ( tree ) ;
TreeItem * item = Object : : cast_to < TreeItem > ( ObjectDB : : get_instance ( p_item_id ) ) ;
if ( item ) {
tree - > scroll_to_item ( item , true ) ;
}
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _notification ( int p_what ) {
2019-03-06 17:33:26 +00:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
2020-02-21 17:28:45 +00:00
get_tree ( ) - > connect ( " tree_changed " , callable_mp ( this , & SceneTreeEditor : : _tree_changed ) ) ;
2021-02-18 18:52:29 +00:00
get_tree ( ) - > connect ( " tree_process_mode_changed " , callable_mp ( this , & SceneTreeEditor : : _tree_process_mode_changed ) ) ;
2020-02-21 17:28:45 +00:00
get_tree ( ) - > connect ( " node_removed " , callable_mp ( this , & SceneTreeEditor : : _node_removed ) ) ;
get_tree ( ) - > connect ( " node_renamed " , callable_mp ( this , & SceneTreeEditor : : _node_renamed ) ) ;
2024-05-13 14:56:03 +00:00
get_tree ( ) - > connect ( SceneStringName ( node_configuration_warning_changed ) , callable_mp ( this , & SceneTreeEditor : : _warning_changed ) ) ;
2015-10-10 12:09:09 +00:00
2020-02-21 17:28:45 +00:00
tree - > connect ( " item_collapsed " , callable_mp ( this , & SceneTreeEditor : : _cell_collapsed ) ) ;
2016-06-15 17:10:19 +00:00
2019-03-06 17:33:26 +00:00
_update_tree ( ) ;
} break ;
2022-02-16 14:17:55 +00:00
2019-03-06 17:33:26 +00:00
case NOTIFICATION_EXIT_TREE : {
2020-02-21 17:28:45 +00:00
get_tree ( ) - > disconnect ( " tree_changed " , callable_mp ( this , & SceneTreeEditor : : _tree_changed ) ) ;
2021-02-18 18:52:29 +00:00
get_tree ( ) - > disconnect ( " tree_process_mode_changed " , callable_mp ( this , & SceneTreeEditor : : _tree_process_mode_changed ) ) ;
2020-02-21 17:28:45 +00:00
get_tree ( ) - > disconnect ( " node_removed " , callable_mp ( this , & SceneTreeEditor : : _node_removed ) ) ;
get_tree ( ) - > disconnect ( " node_renamed " , callable_mp ( this , & SceneTreeEditor : : _node_renamed ) ) ;
tree - > disconnect ( " item_collapsed " , callable_mp ( this , & SceneTreeEditor : : _cell_collapsed ) ) ;
2024-05-13 14:56:03 +00:00
get_tree ( ) - > disconnect ( SceneStringName ( node_configuration_warning_changed ) , callable_mp ( this , & SceneTreeEditor : : _warning_changed ) ) ;
2019-03-06 17:33:26 +00:00
} break ;
2022-02-16 14:17:55 +00:00
2019-03-06 17:33:26 +00:00
case NOTIFICATION_THEME_CHANGED : {
2023-08-13 00:33:39 +00:00
tree - > add_theme_constant_override ( " icon_max_width " , get_theme_constant ( SNAME ( " class_icon_size " ) , EditorStringName ( Editor ) ) ) ;
2023-03-31 19:17:59 +00:00
2019-03-06 17:33:26 +00:00
_update_tree ( ) ;
} break ;
2023-11-26 15:44:18 +00:00
case NOTIFICATION_VISIBILITY_CHANGED : {
if ( is_visible ( ) ) {
TreeItem * item = nullptr ;
if ( selected ) {
// Scroll to selected node.
item = _find ( tree - > get_root ( ) , selected - > get_path ( ) ) ;
} else if ( marked . size ( ) = = 1 ) {
// Scroll to a single marked node.
Node * marked_node = * marked . begin ( ) ;
if ( marked_node ) {
item = _find ( tree - > get_root ( ) , marked_node - > get_path ( ) ) ;
}
}
if ( item ) {
// Must wait until tree is properly sized before scrolling.
2024-04-05 14:51:54 +00:00
ObjectID item_id = item - > get_instance_id ( ) ;
callable_mp ( this , & SceneTreeEditor : : _tree_scroll_to_item ) . call_deferred ( item_id ) ;
2023-11-26 15:44:18 +00:00
}
}
} break ;
2018-02-25 16:04:16 +00:00
}
2014-02-10 01:10:30 +00:00
}
TreeItem * SceneTreeEditor : : _find ( TreeItem * p_node , const NodePath & p_path ) {
2020-05-14 14:41:43 +00:00
if ( ! p_node ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
NodePath np = p_node - > get_metadata ( 0 ) ;
2020-05-14 14:41:43 +00:00
if ( np = = p_path ) {
2014-02-10 01:10:30 +00:00
return p_node ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2021-03-07 20:07:30 +00:00
TreeItem * children = p_node - > get_first_child ( ) ;
2014-02-10 01:10:30 +00:00
while ( children ) {
TreeItem * n = _find ( children , p_path ) ;
2020-05-14 14:41:43 +00:00
if ( n ) {
2014-02-10 01:10:30 +00:00
return n ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
children = children - > get_next ( ) ;
}
2016-03-08 23:00:52 +00:00
2020-04-01 23:20:12 +00:00
return nullptr ;
2014-02-10 01:10:30 +00:00
}
void SceneTreeEditor : : set_selected ( Node * p_node , bool p_emit_selected ) {
ERR_FAIL_COND ( blocked > 0 ) ;
2020-05-14 14:41:43 +00:00
if ( pending_test_update ) {
2014-02-10 01:10:30 +00:00
_test_update_tree ( ) ;
2020-05-14 14:41:43 +00:00
}
if ( tree_dirty ) {
2014-02-10 01:10:30 +00:00
_update_tree ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( selected = = p_node ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2020-04-01 23:20:12 +00:00
TreeItem * item = p_node ? _find ( tree - > get_root ( ) , p_node - > get_path ( ) ) : nullptr ;
2014-02-10 01:10:30 +00:00
if ( item ) {
2021-08-09 17:14:06 +00:00
if ( auto_expand_selected ) {
// Make visible when it's collapsed.
TreeItem * node = item - > get_parent ( ) ;
while ( node & & node ! = tree - > get_root ( ) ) {
node - > set_collapsed ( false ) ;
node = node - > get_parent ( ) ;
}
item - > select ( 0 ) ;
item - > set_as_cursor ( 0 ) ;
selected = p_node ;
tree - > ensure_cursor_is_visible ( ) ;
2016-12-02 02:27:17 +00:00
}
2014-02-10 01:10:30 +00:00
} else {
2020-05-14 14:41:43 +00:00
if ( ! p_node ) {
2020-04-01 23:20:12 +00:00
selected = nullptr ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
selected = p_node ;
2017-08-19 22:13:10 +00:00
}
if ( p_emit_selected ) {
2021-08-20 12:48:34 +00:00
emit_signal ( SNAME ( " node_selected " ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2023-06-15 11:20:49 +00:00
void SceneTreeEditor : : rename_node ( Node * p_node , const String & p_name , TreeItem * p_item ) {
TreeItem * item ;
if ( p_item ) {
item = p_item ; // During batch rename the paths may change, so using _find() is unreliable.
} else {
item = _find ( tree - > get_root ( ) , p_node - > get_path ( ) ) ;
}
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( item ) ;
2023-04-23 16:32:59 +00:00
String new_name = p_name . validate_node_name ( ) ;
2014-02-10 01:10:30 +00:00
2023-04-23 16:32:59 +00:00
if ( new_name ! = p_name ) {
2023-07-07 22:06:29 +00:00
String text = TTR ( " Invalid node name, the following characters are not allowed: " ) + " \n " + String : : get_invalid_node_name_characters ( ) ;
if ( error - > is_visible ( ) ) {
if ( ! error - > get_meta ( " invalid_character " , false ) ) {
error - > set_text ( error - > get_text ( ) + " \n \n " + text ) ;
error - > set_meta ( " invalid_character " , true ) ;
}
} else {
error - > set_text ( text ) ;
error - > set_meta ( " invalid_character " , true ) ;
error - > set_meta ( " same_unique_name " , false ) ;
error - > popup_centered ( ) ;
2019-01-29 16:51:50 +00:00
}
2023-07-07 22:06:29 +00:00
}
2019-01-29 16:51:50 +00:00
2023-07-07 22:06:29 +00:00
// Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`.
new_name = new_name . strip_edges ( ) ;
if ( new_name . is_empty ( ) ) {
// If name is empty, fallback to class name.
if ( GLOBAL_GET ( " editor/naming/node_name_casing " ) . operator int ( ) ! = NAME_CASING_PASCAL_CASE ) {
new_name = Node : : adjust_name_casing ( p_node - > get_class ( ) ) ;
} else {
new_name = p_node - > get_class ( ) ;
}
2014-06-11 06:45:26 +00:00
}
2024-01-16 14:16:58 +00:00
new_name = p_node - > get_parent ( ) - > prevalidate_child_name ( p_node , new_name ) ;
2023-04-23 16:32:59 +00:00
if ( new_name = = p_node - > get_name ( ) ) {
2024-01-16 14:16:58 +00:00
item - > set_text ( 0 , new_name ) ;
2023-07-07 22:06:29 +00:00
return ;
}
2023-01-10 09:18:38 +00:00
2023-07-07 22:06:29 +00:00
// We previously made sure name is not the same as current name so that it won't complain about already used unique name when not changing name.
if ( p_node - > is_unique_name_in_owner ( ) & & get_tree ( ) - > get_edited_scene_root ( ) - > get_node_or_null ( " % " + new_name ) ) {
2023-06-15 11:20:49 +00:00
String text = vformat ( TTR ( " A node with the unique name %s already exists in this scene. " ) , new_name ) ;
2023-07-07 22:06:29 +00:00
if ( error - > is_visible ( ) ) {
if ( ! error - > get_meta ( " same_unique_name " , false ) ) {
error - > set_text ( error - > get_text ( ) + " \n \n " + text ) ;
error - > set_meta ( " same_unique_name " , true ) ;
}
} else {
error - > set_text ( text ) ;
error - > set_meta ( " same_unique_name " , true ) ;
error - > set_meta ( " invalid_character " , false ) ;
error - > popup_centered ( ) ;
}
item - > set_text ( 0 , p_node - > get_name ( ) ) ;
2016-01-26 16:25:04 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2020-09-12 21:59:46 +00:00
2023-04-23 16:32:59 +00:00
if ( ! is_scene_tree_dock ) {
p_node - > set_name ( new_name ) ;
item - > set_metadata ( 0 , p_node - > get_path ( ) ) ;
emit_signal ( SNAME ( " node_renamed " ) ) ;
} else {
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2023-06-15 11:20:49 +00:00
undo_redo - > create_action ( TTR ( " Rename Node " ) , UndoRedo : : MERGE_DISABLE , p_node ) ;
2023-04-23 16:32:59 +00:00
emit_signal ( SNAME ( " node_prerename " ) , p_node , new_name ) ;
undo_redo - > add_undo_method ( p_node , " set_name " , p_node - > get_name ( ) ) ;
undo_redo - > add_undo_method ( item , " set_metadata " , 0 , p_node - > get_path ( ) ) ;
undo_redo - > add_undo_method ( item , " set_text " , 0 , p_node - > get_name ( ) ) ;
2023-07-07 22:06:29 +00:00
p_node - > set_name ( new_name ) ;
2023-04-23 16:32:59 +00:00
undo_redo - > add_do_method ( p_node , " set_name " , new_name ) ;
undo_redo - > add_do_method ( item , " set_metadata " , 0 , p_node - > get_path ( ) ) ;
undo_redo - > add_do_method ( item , " set_text " , 0 , new_name ) ;
undo_redo - > commit_action ( ) ;
}
}
2023-06-15 11:20:49 +00:00
void SceneTreeEditor : : _edited ( ) {
TreeItem * which = tree - > get_next_selected ( nullptr ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( which ) ;
2023-06-15 11:20:49 +00:00
TreeItem * edited = tree - > get_edited ( ) ;
ERR_FAIL_NULL ( edited ) ;
if ( is_scene_tree_dock & & tree - > get_next_selected ( which ) ) {
List < Node * > nodes_to_rename ;
for ( TreeItem * item = which ; item ; item = tree - > get_next_selected ( item ) ) {
Node * n = get_node ( item - > get_metadata ( 0 ) ) ;
ERR_FAIL_NULL ( n ) ;
nodes_to_rename . push_back ( n ) ;
}
ERR_FAIL_COND ( nodes_to_rename . is_empty ( ) ) ;
2023-04-23 16:32:59 +00:00
2023-06-15 11:20:49 +00:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
undo_redo - > create_action ( TTR ( " Rename Nodes " ) , UndoRedo : : MERGE_DISABLE , nodes_to_rename . front ( ) - > get ( ) , true ) ;
TreeItem * item = which ;
String new_name = edited - > get_text ( 0 ) ;
for ( Node * n : nodes_to_rename ) {
rename_node ( n , new_name , item ) ;
item = tree - > get_next_selected ( item ) ;
}
2022-04-16 10:23:32 +00:00
2023-06-15 11:20:49 +00:00
undo_redo - > commit_action ( ) ;
} else {
Node * n = get_node ( which - > get_metadata ( 0 ) ) ;
ERR_FAIL_NULL ( n ) ;
rename_node ( n , which - > get_text ( 0 ) ) ;
}
2014-02-10 01:10:30 +00:00
}
Node * SceneTreeEditor : : get_selected ( ) {
return selected ;
}
2022-05-19 15:00:06 +00:00
void SceneTreeEditor : : set_marked ( const HashSet < Node * > & p_marked , bool p_selectable , bool p_children_selectable ) {
2020-05-14 14:41:43 +00:00
if ( tree_dirty ) {
2014-02-10 01:10:30 +00:00
_update_tree ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
marked = p_marked ;
marked_selectable = p_selectable ;
marked_children_selectable = p_children_selectable ;
_update_tree ( ) ;
}
void SceneTreeEditor : : set_marked ( Node * p_marked , bool p_selectable , bool p_children_selectable ) {
2022-05-19 15:00:06 +00:00
HashSet < Node * > s ;
2020-05-14 14:41:43 +00:00
if ( p_marked ) {
2014-02-10 01:10:30 +00:00
s . insert ( p_marked ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
set_marked ( s , p_selectable , p_children_selectable ) ;
}
2016-05-16 15:23:40 +00:00
void SceneTreeEditor : : set_filter ( const String & p_filter ) {
filter = p_filter ;
2022-05-24 15:24:25 +00:00
_update_filter ( nullptr , true ) ;
2016-05-16 15:23:40 +00:00
}
String SceneTreeEditor : : get_filter ( ) const {
return filter ;
}
2022-09-16 13:10:28 +00:00
String SceneTreeEditor : : get_filter_term_warning ( ) {
return filter_term_warning ;
}
2023-07-08 17:15:17 +00:00
void SceneTreeEditor : : set_show_all_nodes ( bool p_show_all_nodes ) {
show_all_nodes = p_show_all_nodes ;
_update_filter ( nullptr , true ) ;
}
2022-08-29 10:10:32 +00:00
void SceneTreeEditor : : set_as_scene_tree_dock ( ) {
is_scene_tree_dock = true ;
2022-03-25 17:06:46 +00:00
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : set_display_foreign_nodes ( bool p_display ) {
display_foreign = p_display ;
_update_tree ( ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-27 23:50:25 +00:00
void SceneTreeEditor : : set_valid_types ( const Vector < StringName > & p_valid ) {
valid_types = p_valid ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : set_editor_selection ( EditorSelection * p_selection ) {
editor_selection = p_selection ;
tree - > set_select_mode ( Tree : : SELECT_MULTI ) ;
tree - > set_cursor_can_exit_tree ( false ) ;
2020-02-21 17:28:45 +00:00
editor_selection - > connect ( " selection_changed " , callable_mp ( this , & SceneTreeEditor : : _selection_changed ) ) ;
2014-02-10 01:10:30 +00:00
}
void SceneTreeEditor : : _update_selection ( TreeItem * item ) {
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( item ) ;
2014-02-10 01:10:30 +00:00
NodePath np = item - > get_metadata ( 0 ) ;
2020-05-14 14:41:43 +00:00
if ( ! has_node ( np ) ) {
2016-01-23 22:51:51 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-01-23 22:51:51 +00:00
2014-02-10 01:10:30 +00:00
Node * n = get_node ( np ) ;
2020-05-14 14:41:43 +00:00
if ( ! n ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( editor_selection - > is_selected ( n ) ) {
2023-01-06 19:19:34 +00:00
if ( ! item - > is_selected ( 0 ) ) {
item - > select ( 0 ) ;
}
2020-05-14 14:41:43 +00:00
} else {
2023-01-06 19:19:34 +00:00
if ( item - > is_selected ( 0 ) ) {
TreeItem * previous_cursor_item = tree - > get_selected ( ) ;
item - > deselect ( 0 ) ;
previous_cursor_item - > set_as_cursor ( 0 ) ;
}
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2021-03-07 20:07:30 +00:00
TreeItem * c = item - > get_first_child ( ) ;
2014-02-10 01:10:30 +00:00
while ( c ) {
_update_selection ( c ) ;
c = c - > get_next ( ) ;
}
}
void SceneTreeEditor : : _selection_changed ( ) {
2020-05-14 14:41:43 +00:00
if ( ! editor_selection ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
TreeItem * root = tree - > get_root ( ) ;
2020-05-14 14:41:43 +00:00
if ( ! root ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
_update_selection ( root ) ;
}
void SceneTreeEditor : : _cell_collapsed ( Object * p_obj ) {
2020-05-14 14:41:43 +00:00
if ( updating_tree ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
if ( ! can_rename ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2017-08-24 20:58:51 +00:00
TreeItem * ti = Object : : cast_to < TreeItem > ( p_obj ) ;
2020-05-14 14:41:43 +00:00
if ( ! ti ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
bool collapsed = ti - > is_collapsed ( ) ;
NodePath np = ti - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
2023-09-09 15:24:40 +00:00
ERR_FAIL_NULL ( n ) ;
2014-02-10 01:10:30 +00:00
2016-06-28 16:10:15 +00:00
n - > set_display_folded ( collapsed ) ;
2014-02-10 01:10:30 +00:00
}
2016-05-11 14:46:08 +00:00
Variant SceneTreeEditor : : get_drag_data_fw ( const Point2 & p_point , Control * p_from ) {
2020-05-14 14:41:43 +00:00
if ( ! can_rename ) {
2016-05-11 14:46:08 +00:00
return Variant ( ) ; //not editable tree
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
2019-12-13 05:13:47 +00:00
if ( tree - > get_button_id_at_position ( p_point ) ! = - 1 ) {
return Variant ( ) ; //dragging from button
}
2022-09-29 09:53:28 +00:00
Vector < Node * > selected_nodes ;
2020-03-17 06:33:00 +00:00
Vector < Ref < Texture2D > > icons ;
2020-04-01 23:20:12 +00:00
TreeItem * next = tree - > get_next_selected ( nullptr ) ;
2016-05-11 14:46:08 +00:00
while ( next ) {
NodePath np = next - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
if ( n ) {
2023-11-01 13:56:14 +00:00
selected_nodes . push_back ( n ) ;
icons . push_back ( next - > get_icon ( 0 ) ) ;
2016-05-11 14:46:08 +00:00
}
next = tree - > get_next_selected ( next ) ;
}
2022-09-29 09:53:28 +00:00
if ( selected_nodes . is_empty ( ) ) {
2016-05-11 14:46:08 +00:00
return Variant ( ) ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
Array objs ;
2016-05-17 14:27:35 +00:00
int list_max = 10 ;
float opacity_step = 1.0f / list_max ;
float opacity_item = 1.0f ;
2022-09-29 09:53:28 +00:00
for ( int i = 0 ; i < selected_nodes . size ( ) ; i + + ) {
2016-05-17 14:27:35 +00:00
if ( i < list_max ) {
HBoxContainer * hb = memnew ( HBoxContainer ) ;
2017-01-12 21:27:27 +00:00
TextureRect * tf = memnew ( TextureRect ) ;
2023-08-13 00:33:39 +00:00
int icon_size = get_theme_constant ( SNAME ( " class_icon_size " ) , EditorStringName ( Editor ) ) ;
2023-07-10 13:00:18 +00:00
tf - > set_custom_minimum_size ( Size2 ( icon_size , icon_size ) ) ;
tf - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_ASPECT_CENTERED ) ;
tf - > set_expand_mode ( TextureRect : : EXPAND_IGNORE_SIZE ) ;
2016-05-17 14:27:35 +00:00
tf - > set_texture ( icons [ i ] ) ;
hb - > add_child ( tf ) ;
2022-09-29 09:53:28 +00:00
Label * label = memnew ( Label ( selected_nodes [ i ] - > get_name ( ) ) ) ;
2016-05-17 14:27:35 +00:00
hb - > add_child ( label ) ;
vb - > add_child ( hb ) ;
2016-10-03 19:33:42 +00:00
hb - > set_modulate ( Color ( 1 , 1 , 1 , opacity_item ) ) ;
2016-05-17 14:27:35 +00:00
opacity_item - = opacity_step ;
}
2022-09-29 09:53:28 +00:00
NodePath p = selected_nodes [ i ] - > get_path ( ) ;
2016-05-11 14:46:08 +00:00
objs . push_back ( p ) ;
}
set_drag_preview ( vb ) ;
Dictionary drag_data ;
drag_data [ " type " ] = " nodes " ;
drag_data [ " nodes " ] = objs ;
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_INBETWEEN | Tree : : DROP_MODE_ON_ITEM ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " nodes_dragged " ) ) ;
2016-05-11 14:46:08 +00:00
return drag_data ;
}
2016-10-27 14:32:41 +00:00
bool SceneTreeEditor : : _is_script_type ( const StringName & p_type ) const {
return ( script_types - > find ( p_type ) ) ;
}
2016-05-11 14:46:08 +00:00
bool SceneTreeEditor : : can_drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) const {
2020-05-14 14:41:43 +00:00
if ( ! can_rename ) {
2016-05-11 14:46:08 +00:00
return false ; //not editable tree
2020-05-14 14:41:43 +00:00
}
2016-05-16 15:23:40 +00:00
2016-05-11 14:46:08 +00:00
Dictionary d = p_data ;
2020-05-14 14:41:43 +00:00
if ( ! d . has ( " type " ) ) {
2016-05-11 14:46:08 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
2017-09-10 13:37:49 +00:00
TreeItem * item = tree - > get_item_at_position ( p_point ) ;
2020-05-14 14:41:43 +00:00
if ( ! item ) {
2016-07-18 18:00:14 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2016-07-18 18:00:14 +00:00
2017-09-10 13:37:49 +00:00
int section = tree - > get_drop_section_at_position ( p_point ) ;
2020-05-14 14:41:43 +00:00
if ( section < - 1 | | ( section = = - 1 & & ! item - > get_parent ( ) ) ) {
2016-07-18 18:00:14 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2016-07-18 18:00:14 +00:00
2016-05-11 23:57:52 +00:00
if ( String ( d [ " type " ] ) = = " files " ) {
Vector < String > files = d [ " files " ] ;
2020-05-14 14:41:43 +00:00
if ( files . size ( ) = = 0 ) {
2016-05-11 23:57:52 +00:00
return false ; //weird
2020-05-14 14:41:43 +00:00
}
2016-05-11 23:57:52 +00:00
2016-10-27 14:32:41 +00:00
if ( _is_script_type ( EditorFileSystem : : get_singleton ( ) - > get_file_type ( files [ 0 ] ) ) ) {
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_ON_ITEM ) ;
return true ;
}
2016-05-11 23:57:52 +00:00
2021-07-16 14:27:59 +00:00
bool scene_drop = true ;
2024-05-16 02:13:46 +00:00
bool audio_drop = true ;
2016-05-11 23:57:52 +00:00
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
2023-12-13 16:06:14 +00:00
String ftype = EditorFileSystem : : get_singleton ( ) - > get_file_type ( files [ i ] ) ;
2020-05-14 14:41:43 +00:00
if ( ftype ! = " PackedScene " ) {
2021-07-16 14:27:59 +00:00
scene_drop = false ;
2024-05-16 02:13:46 +00:00
}
if ( audio_drop & & ! ClassDB : : is_parent_class ( ftype , " AudioStream " ) ) {
audio_drop = false ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 23:57:52 +00:00
}
2021-07-16 14:27:59 +00:00
if ( scene_drop ) {
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_INBETWEEN | Tree : : DROP_MODE_ON_ITEM ) ;
2024-05-16 02:13:46 +00:00
return true ;
}
if ( audio_drop ) {
2021-07-16 14:27:59 +00:00
if ( files . size ( ) > 1 ) {
2024-05-16 02:13:46 +00:00
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_INBETWEEN ) ;
} else {
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_INBETWEEN | Tree : : DROP_MODE_ON_ITEM ) ;
2021-07-16 14:27:59 +00:00
}
2024-05-16 02:13:46 +00:00
return true ;
}
if ( files . size ( ) > 1 ) {
return false ;
2021-07-16 14:27:59 +00:00
}
2024-05-16 02:13:46 +00:00
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_ON_ITEM ) ;
2016-05-11 23:57:52 +00:00
return true ;
}
2019-08-16 19:05:01 +00:00
if ( String ( d [ " type " ] ) = = " script_list_element " ) {
ScriptEditorBase * se = Object : : cast_to < ScriptEditorBase > ( d [ " script_list_element " ] ) ;
if ( se ) {
String sp = se - > get_edited_resource ( ) - > get_path ( ) ;
if ( _is_script_type ( EditorFileSystem : : get_singleton ( ) - > get_file_type ( sp ) ) ) {
tree - > set_drop_mode_flags ( Tree : : DROP_MODE_ON_ITEM ) ;
return true ;
}
}
}
2023-11-01 13:56:14 +00:00
if ( filter . is_empty ( ) & & String ( d [ " type " ] ) = = " nodes " ) {
Array nodes = d [ " nodes " ] ;
for ( int i = 0 ; i < nodes . size ( ) ; i + + ) {
Node * n = get_node ( nodes [ i ] ) ;
// Nodes from an instantiated scene can't be rearranged.
if ( n & & n - > get_owner ( ) & & n - > get_owner ( ) ! = get_scene_node ( ) & & ! n - > get_owner ( ) - > get_scene_file_path ( ) . is_empty ( ) ) {
return false ;
}
}
return true ;
}
return false ;
2016-05-11 14:46:08 +00:00
}
2020-05-14 12:29:06 +00:00
2016-05-11 14:46:08 +00:00
void SceneTreeEditor : : drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) {
2020-05-14 14:41:43 +00:00
if ( ! can_drop_data_fw ( p_point , p_data , p_from ) ) {
2016-05-11 14:46:08 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
2017-09-10 13:37:49 +00:00
TreeItem * item = tree - > get_item_at_position ( p_point ) ;
2020-05-14 14:41:43 +00:00
if ( ! item ) {
2016-05-11 14:46:08 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-09-10 13:37:49 +00:00
int section = tree - > get_drop_section_at_position ( p_point ) ;
2020-05-14 14:41:43 +00:00
if ( section < - 1 ) {
2016-05-11 14:46:08 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
NodePath np = item - > get_metadata ( 0 ) ;
Node * n = get_node ( np ) ;
2020-05-14 14:41:43 +00:00
if ( ! n ) {
2016-05-11 14:46:08 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2016-05-11 14:46:08 +00:00
Dictionary d = p_data ;
2016-05-11 23:57:52 +00:00
if ( String ( d [ " type " ] ) = = " nodes " ) {
Array nodes = d [ " nodes " ] ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " nodes_rearranged " ) , nodes , np , section ) ;
2016-05-11 23:57:52 +00:00
}
2016-05-11 14:46:08 +00:00
2016-05-11 23:57:52 +00:00
if ( String ( d [ " type " ] ) = = " files " ) {
2016-10-27 14:32:41 +00:00
Vector < String > files = d [ " files " ] ;
String ftype = EditorFileSystem : : get_singleton ( ) - > get_file_type ( files [ 0 ] ) ;
if ( _is_script_type ( ftype ) ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " script_dropped " ) , files [ 0 ] , np ) ;
2016-10-27 14:32:41 +00:00
} else {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " files_dropped " ) , files , np , section ) ;
2016-10-27 14:32:41 +00:00
}
2016-05-11 23:57:52 +00:00
}
2019-08-16 19:05:01 +00:00
if ( String ( d [ " type " ] ) = = " script_list_element " ) {
ScriptEditorBase * se = Object : : cast_to < ScriptEditorBase > ( d [ " script_list_element " ] ) ;
if ( se ) {
String sp = se - > get_edited_resource ( ) - > get_path ( ) ;
if ( _is_script_type ( EditorFileSystem : : get_singleton ( ) - > get_file_type ( sp ) ) ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " script_dropped " ) , sp , np ) ;
2019-08-16 19:05:01 +00:00
}
}
}
2016-05-11 14:46:08 +00:00
}
2014-02-10 01:10:30 +00:00
2021-09-18 07:33:18 +00:00
void SceneTreeEditor : : _empty_clicked ( const Vector2 & p_pos , MouseButton p_button ) {
if ( p_button ! = MouseButton : : RIGHT ) {
return ;
}
_rmb_select ( p_pos ) ;
}
void SceneTreeEditor : : _rmb_select ( const Vector2 & p_pos , MouseButton p_button ) {
if ( p_button ! = MouseButton : : RIGHT ) {
return ;
}
2021-08-31 15:43:35 +00:00
emit_signal ( SNAME ( " rmb_pressed " ) , tree - > get_screen_position ( ) + p_pos ) ;
2016-05-16 02:41:48 +00:00
}
2014-09-03 02:13:40 +00:00
2021-04-27 15:43:49 +00:00
void SceneTreeEditor : : update_warning ( ) {
_warning_changed ( nullptr ) ;
}
2021-08-09 17:14:06 +00:00
2016-05-17 21:27:15 +00:00
void SceneTreeEditor : : _warning_changed ( Node * p_for_node ) {
//should use a timer
update_timer - > start ( ) ;
}
2016-05-16 15:23:40 +00:00
2021-08-09 17:14:06 +00:00
void SceneTreeEditor : : set_auto_expand_selected ( bool p_auto , bool p_update_settings ) {
if ( p_update_settings ) {
EditorSettings : : get_singleton ( ) - > set ( " docks/scene_tree/auto_expand_to_selected " , p_auto ) ;
}
auto_expand_selected = p_auto ;
}
2019-04-12 02:21:48 +00:00
void SceneTreeEditor : : set_connect_to_script_mode ( bool p_enable ) {
connect_to_script_mode = p_enable ;
update_tree ( ) ;
}
2019-06-06 02:13:57 +00:00
void SceneTreeEditor : : set_connecting_signal ( bool p_enable ) {
connecting_signal = p_enable ;
update_tree ( ) ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeEditor : : _bind_methods ( ) {
2020-12-06 15:09:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " _update_tree " ) , & SceneTreeEditor : : _update_tree , DEFVAL ( false ) ) ; // Still used by UndoRedo.
2014-02-10 01:10:30 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " update_tree " ) , & SceneTreeEditor : : update_tree ) ;
2016-05-17 21:27:15 +00:00
2014-02-10 01:10:30 +00:00
ADD_SIGNAL ( MethodInfo ( " node_selected " ) ) ;
ADD_SIGNAL ( MethodInfo ( " node_renamed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " node_prerename " ) ) ;
2014-05-08 05:43:19 +00:00
ADD_SIGNAL ( MethodInfo ( " node_changed " ) ) ;
2016-08-03 14:28:20 +00:00
ADD_SIGNAL ( MethodInfo ( " nodes_dragged " ) ) ;
2016-05-11 14:46:08 +00:00
ADD_SIGNAL ( MethodInfo ( " nodes_rearranged " , PropertyInfo ( Variant : : ARRAY , " paths " ) , PropertyInfo ( Variant : : NODE_PATH , " to_path " ) , PropertyInfo ( Variant : : INT , " type " ) ) ) ;
2020-02-17 21:06:54 +00:00
ADD_SIGNAL ( MethodInfo ( " files_dropped " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " files " ) , PropertyInfo ( Variant : : NODE_PATH , " to_path " ) , PropertyInfo ( Variant : : INT , " type " ) ) ) ;
2016-10-27 14:32:41 +00:00
ADD_SIGNAL ( MethodInfo ( " script_dropped " , PropertyInfo ( Variant : : STRING , " file " ) , PropertyInfo ( Variant : : NODE_PATH , " to_path " ) ) ) ;
2017-09-10 13:37:49 +00:00
ADD_SIGNAL ( MethodInfo ( " rmb_pressed " , PropertyInfo ( Variant : : VECTOR2 , " position " ) ) ) ;
2017-03-05 15:44:50 +00:00
2014-02-10 01:10:30 +00:00
ADD_SIGNAL ( MethodInfo ( " open " ) ) ;
ADD_SIGNAL ( MethodInfo ( " open_script " ) ) ;
}
SceneTreeEditor : : SceneTreeEditor ( bool p_label , bool p_can_rename , bool p_can_open_instance ) {
2020-04-01 23:20:12 +00:00
selected = nullptr ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
can_rename = p_can_rename ;
can_open_instance = p_can_open_instance ;
2020-04-01 23:20:12 +00:00
editor_selection = nullptr ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
if ( p_label ) {
Label * label = memnew ( Label ) ;
2021-07-08 13:29:15 +00:00
label - > set_theme_type_variation ( " HeaderSmall " ) ;
2017-03-29 15:29:38 +00:00
label - > set_position ( Point2 ( 10 , 0 ) ) ;
2016-05-04 01:25:37 +00:00
label - > set_text ( TTR ( " Scene Tree (Nodes): " ) ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
add_child ( label ) ;
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
tree = memnew ( Tree ) ;
2024-03-28 01:16:56 +00:00
tree - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2020-12-22 16:24:29 +00:00
tree - > set_anchor ( SIDE_RIGHT , ANCHOR_END ) ;
tree - > set_anchor ( SIDE_BOTTOM , ANCHOR_END ) ;
2014-02-10 01:10:30 +00:00
tree - > set_begin ( Point2 ( 0 , p_label ? 18 : 0 ) ) ;
tree - > set_end ( Point2 ( 0 , 0 ) ) ;
2021-03-12 16:58:25 +00:00
tree - > set_allow_reselect ( true ) ;
2022-02-08 09:14:58 +00:00
tree - > add_theme_constant_override ( " button_margin " , 0 ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
add_child ( tree ) ;
2016-03-08 23:00:52 +00:00
2023-01-14 02:37:19 +00:00
SET_DRAG_FORWARDING_GCD ( tree , SceneTreeEditor ) ;
2016-05-16 15:23:40 +00:00
if ( p_can_rename ) {
2016-05-16 02:41:48 +00:00
tree - > set_allow_rmb_select ( true ) ;
2021-09-18 07:33:18 +00:00
tree - > connect ( " item_mouse_selected " , callable_mp ( this , & SceneTreeEditor : : _rmb_select ) ) ;
tree - > connect ( " empty_clicked " , callable_mp ( this , & SceneTreeEditor : : _empty_clicked ) ) ;
2016-05-16 15:23:40 +00:00
}
2016-05-11 14:46:08 +00:00
2021-07-23 08:34:21 +00:00
tree - > connect ( " cell_selected " , callable_mp ( this , & SceneTreeEditor : : _selected_changed ) ) ;
2023-06-15 11:20:49 +00:00
tree - > connect ( " item_edited " , callable_mp ( this , & SceneTreeEditor : : _edited ) ) ;
2020-02-21 17:28:45 +00:00
tree - > connect ( " multi_selected " , callable_mp ( this , & SceneTreeEditor : : _cell_multi_selected ) ) ;
2021-09-18 07:33:18 +00:00
tree - > connect ( " button_clicked " , callable_mp ( this , & SceneTreeEditor : : _cell_button_pressed ) ) ;
2020-02-21 17:28:45 +00:00
tree - > connect ( " nothing_selected " , callable_mp ( this , & SceneTreeEditor : : _deselect_items ) ) ;
2014-02-10 01:10:30 +00:00
2014-03-14 01:57:24 +00:00
error = memnew ( AcceptDialog ) ;
add_child ( error ) ;
2016-05-17 21:27:15 +00:00
warning = memnew ( AcceptDialog ) ;
add_child ( warning ) ;
2017-08-23 20:25:14 +00:00
warning - > set_title ( TTR ( " Node Configuration Warning! " ) ) ;
2024-01-23 17:47:26 +00:00
warning - > set_flag ( Window : : FLAG_POPUP , true ) ;
2016-05-17 21:27:15 +00:00
2014-09-03 02:13:40 +00:00
last_hash = 0 ;
blocked = 0 ;
2016-05-17 21:27:15 +00:00
update_timer = memnew ( Timer ) ;
2022-07-28 20:56:41 +00:00
update_timer - > connect ( " timeout " , callable_mp ( this , & SceneTreeEditor : : _update_tree ) . bind ( false ) ) ;
2016-05-17 21:27:15 +00:00
update_timer - > set_one_shot ( true ) ;
update_timer - > set_wait_time ( 0.5 ) ;
add_child ( update_timer ) ;
2015-10-10 12:09:09 +00:00
2023-11-14 05:00:41 +00:00
update_node_tooltip_delay = memnew ( Timer ) ;
update_node_tooltip_delay - > set_wait_time ( 0.5 ) ;
update_node_tooltip_delay - > set_one_shot ( true ) ;
add_child ( update_node_tooltip_delay ) ;
2016-10-27 14:32:41 +00:00
script_types = memnew ( List < StringName > ) ;
2017-01-03 02:03:46 +00:00
ClassDB : : get_inheriters_from_class ( " Script " , script_types ) ;
2014-02-10 01:10:30 +00:00
}
SceneTreeEditor : : ~ SceneTreeEditor ( ) {
2016-10-27 14:32:41 +00:00
memdelete ( script_types ) ;
2014-02-10 01:10:30 +00:00
}
/******** DIALOG *********/
2023-11-26 15:44:18 +00:00
void SceneTreeDialog : : popup_scenetree_dialog ( Node * p_selected_node , Node * p_marked_node , bool p_marked_node_selectable , bool p_marked_node_children_selectable ) {
get_scene_tree ( ) - > set_marked ( p_marked_node , p_marked_node_selectable , p_marked_node_children_selectable ) ;
get_scene_tree ( ) - > set_selected ( p_selected_node ) ;
2020-07-11 16:45:19 +00:00
popup_centered_clamped ( Size2 ( 350 , 700 ) * EDSCALE ) ;
}
2023-07-08 17:15:17 +00:00
void SceneTreeDialog : : _show_all_nodes_changed ( bool p_button_pressed ) {
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " editor_metadata " , " show_all_nodes_for_node_selection " , p_button_pressed ) ;
tree - > set_show_all_nodes ( p_button_pressed ) ;
}
2023-07-17 21:40:27 +00:00
void SceneTreeDialog : : set_valid_types ( const Vector < StringName > & p_valid ) {
if ( p_valid . is_empty ( ) ) {
return ;
}
tree - > set_valid_types ( p_valid ) ;
HBoxContainer * hbox = memnew ( HBoxContainer ) ;
content - > add_child ( hbox ) ;
content - > move_child ( hbox , 0 ) ;
{
Label * label = memnew ( Label ) ;
hbox - > add_child ( label ) ;
label - > set_text ( TTR ( " Allowed: " ) ) ;
}
HFlowContainer * hflow = memnew ( HFlowContainer ) ;
hbox - > add_child ( hflow ) ;
hflow - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
for ( const StringName & type : p_valid ) {
HBoxContainer * hb = memnew ( HBoxContainer ) ;
hflow - > add_child ( hb ) ;
2023-09-29 13:58:13 +00:00
// Attempt to get the correct name and icon for script path types.
String name = type ;
Ref < Texture2D > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( type ) ;
// If we can't find a global class icon, try to find one for the script.
if ( icon . is_null ( ) & & ResourceLoader : : exists ( type , " Script " ) ) {
Ref < Script > node_script = ResourceLoader : : load ( type ) ;
if ( node_script . is_valid ( ) ) {
name = name . get_file ( ) ;
icon = EditorNode : : get_singleton ( ) - > get_object_icon ( node_script . ptr ( ) ) ;
}
}
2023-07-17 21:40:27 +00:00
TextureRect * trect = memnew ( TextureRect ) ;
hb - > add_child ( trect ) ;
trect - > set_expand_mode ( TextureRect : : EXPAND_IGNORE_SIZE ) ;
trect - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_ASPECT_CENTERED ) ;
2023-09-29 13:58:13 +00:00
trect - > set_meta ( " icon " , icon ) ;
2023-07-17 21:40:27 +00:00
valid_type_icons . push_back ( trect ) ;
Label * label = memnew ( Label ) ;
hb - > add_child ( label ) ;
2023-09-29 13:58:13 +00:00
label - > set_text ( name ) ;
2024-01-23 21:29:45 +00:00
label - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2023-07-17 21:40:27 +00:00
}
2023-07-08 17:15:17 +00:00
show_all_nodes - > show ( ) ;
2023-07-17 21:40:27 +00:00
}
2014-02-10 01:10:30 +00:00
void SceneTreeDialog : : _notification ( int p_what ) {
2019-04-15 23:21:51 +00:00
switch ( p_what ) {
2020-03-06 17:00:16 +00:00
case NOTIFICATION_VISIBILITY_CHANGED : {
2020-05-14 14:41:43 +00:00
if ( is_visible ( ) ) {
2020-03-06 17:00:16 +00:00
tree - > update_tree ( ) ;
2022-09-23 16:04:48 +00:00
// Select the search bar by default.
2023-12-18 14:46:56 +00:00
callable_mp ( ( Control * ) filter , & Control : : grab_focus ) . call_deferred ( ) ;
2020-05-14 14:41:43 +00:00
}
2020-03-06 17:00:16 +00:00
} break ;
2022-02-16 14:17:55 +00:00
2019-04-15 23:21:51 +00:00
case NOTIFICATION_ENTER_TREE : {
2024-05-14 12:28:18 +00:00
connect ( SceneStringName ( confirmed ) , callable_mp ( this , & SceneTreeDialog : : _select ) ) ;
2022-01-16 07:59:02 +00:00
} break ;
2022-02-16 14:17:55 +00:00
2022-01-16 07:59:02 +00:00
case NOTIFICATION_THEME_CHANGED : {
2023-09-23 19:22:33 +00:00
filter - > set_right_icon ( get_editor_theme_icon ( SNAME ( " Search " ) ) ) ;
for ( TextureRect * trect : valid_type_icons ) {
trect - > set_custom_minimum_size ( Vector2 ( get_theme_constant ( SNAME ( " class_icon_size " ) , EditorStringName ( Editor ) ) , 0 ) ) ;
2023-09-29 13:58:13 +00:00
trect - > set_texture ( trect - > get_meta ( " icon " ) ) ;
2023-09-23 19:22:33 +00:00
}
2019-04-15 23:21:51 +00:00
} break ;
2022-02-16 14:17:55 +00:00
2019-04-15 23:21:51 +00:00
case NOTIFICATION_EXIT_TREE : {
2024-05-14 12:28:18 +00:00
disconnect ( SceneStringName ( confirmed ) , callable_mp ( this , & SceneTreeDialog : : _select ) ) ;
2019-04-15 23:21:51 +00:00
} break ;
2014-02-10 01:10:30 +00:00
}
}
void SceneTreeDialog : : _cancel ( ) {
hide ( ) ;
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void SceneTreeDialog : : _select ( ) {
if ( tree - > get_selected ( ) ) {
2022-06-21 08:40:57 +00:00
// The signal may cause another dialog to be displayed, so be sure to hide this one first.
2014-02-10 01:10:30 +00:00
hide ( ) ;
2022-06-21 08:40:57 +00:00
emit_signal ( SNAME ( " selected " ) , tree - > get_selected ( ) - > get_path ( ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2022-07-14 04:03:27 +00:00
void SceneTreeDialog : : _selected_changed ( ) {
get_ok_button ( ) - > set_disabled ( ! tree - > get_selected ( ) ) ;
}
2019-07-06 17:09:34 +00:00
void SceneTreeDialog : : _filter_changed ( const String & p_filter ) {
tree - > set_filter ( p_filter ) ;
}
2014-02-10 01:10:30 +00:00
void SceneTreeDialog : : _bind_methods ( ) {
2017-01-03 02:03:46 +00:00
ClassDB : : bind_method ( " _cancel " , & SceneTreeDialog : : _cancel ) ;
2019-07-06 17:09:34 +00:00
2014-02-10 01:10:30 +00:00
ADD_SIGNAL ( MethodInfo ( " selected " , PropertyInfo ( Variant : : NODE_PATH , " path " ) ) ) ;
}
SceneTreeDialog : : SceneTreeDialog ( ) {
2016-05-04 01:25:37 +00:00
set_title ( TTR ( " Select a Node " ) ) ;
2023-07-17 21:40:27 +00:00
content = memnew ( VBoxContainer ) ;
add_child ( content ) ;
2019-07-06 17:09:34 +00:00
2023-07-08 17:15:17 +00:00
HBoxContainer * filter_hbc = memnew ( HBoxContainer ) ;
content - > add_child ( filter_hbc ) ;
2019-07-06 17:09:34 +00:00
filter = memnew ( LineEdit ) ;
2020-03-06 17:00:16 +00:00
filter - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2022-05-27 04:02:48 +00:00
filter - > set_placeholder ( TTR ( " Filter Nodes " ) ) ;
2022-01-16 07:59:02 +00:00
filter - > set_clear_button_enabled ( true ) ;
2022-02-08 09:14:58 +00:00
filter - > add_theme_constant_override ( " minimum_character_width " , 0 ) ;
2024-05-14 09:42:00 +00:00
filter - > connect ( SceneStringName ( text_changed ) , callable_mp ( this , & SceneTreeDialog : : _filter_changed ) ) ;
2023-07-08 17:15:17 +00:00
filter_hbc - > add_child ( filter ) ;
// Add 'Show All' button to HBoxContainer next to the filter, visible only when valid_types is defined.
show_all_nodes = memnew ( CheckButton ) ;
show_all_nodes - > set_text ( TTR ( " Show All " ) ) ;
show_all_nodes - > connect ( " toggled " , callable_mp ( this , & SceneTreeDialog : : _show_all_nodes_changed ) ) ;
show_all_nodes - > set_h_size_flags ( Control : : SIZE_SHRINK_BEGIN ) ;
show_all_nodes - > hide ( ) ;
filter_hbc - > add_child ( show_all_nodes ) ;
2014-02-10 01:10:30 +00:00
2018-11-02 18:05:38 +00:00
tree = memnew ( SceneTreeEditor ( false , false , true ) ) ;
2020-03-06 17:00:16 +00:00
tree - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2020-02-21 17:28:45 +00:00
tree - > get_scene_tree ( ) - > connect ( " item_activated " , callable_mp ( this , & SceneTreeDialog : : _select ) ) ;
2023-07-08 17:15:17 +00:00
// Initialize button state, must be done after the tree has been created to update its 'show_all_nodes' flag.
// This is also done before adding the tree to the content to avoid triggering unnecessary tree filtering.
show_all_nodes - > set_pressed ( EditorSettings : : get_singleton ( ) - > get_project_metadata ( " editor_metadata " , " show_all_nodes_for_node_selection " , false ) ) ;
2023-07-17 21:40:27 +00:00
content - > add_child ( tree ) ;
2022-07-14 04:03:27 +00:00
// Disable the OK button when no node is selected.
get_ok_button ( ) - > set_disabled ( ! tree - > get_selected ( ) ) ;
tree - > connect ( " node_selected " , callable_mp ( this , & SceneTreeDialog : : _selected_changed ) ) ;
2014-02-10 01:10:30 +00:00
}
SceneTreeDialog : : ~ SceneTreeDialog ( ) {
}