@@ -1667,6 +1667,7 @@ void SceneTreeDock::_notification(int p_what) {
16671667 button_instance->set_icon (get_editor_theme_icon (SNAME (" Instance" )));
16681668 button_create_script->set_icon (get_editor_theme_icon (SNAME (" ScriptCreate" )));
16691669 button_detach_script->set_icon (get_editor_theme_icon (SNAME (" ScriptRemove" )));
1670+ button_extend_script->set_icon (get_editor_theme_icon (SNAME (" ScriptExtend" )));
16701671 button_tree_menu->set_icon (get_editor_theme_icon (SNAME (" GuiTabMenuHl" )));
16711672
16721673 filter->set_right_icon (get_editor_theme_icon (SNAME (" Search" )));
@@ -2784,33 +2785,49 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
27842785}
27852786
27862787void SceneTreeDock::_update_script_button () {
2787- if (!profile_allow_script_editing) {
2788- button_create_script->hide ();
2789- button_detach_script->hide ();
2790- } else if (editor_selection->get_selection ().size () == 0 ) {
2791- button_create_script->hide ();
2792- button_detach_script->hide ();
2793- } else if (editor_selection->get_selection ().size () == 1 ) {
2794- Node *n = editor_selection->get_selected_node_list ().front ()->get ();
2795- if (n->get_script ().is_null ()) {
2796- button_create_script->show ();
2797- button_detach_script->hide ();
2798- } else {
2799- button_create_script->hide ();
2800- button_detach_script->show ();
2801- }
2802- } else {
2803- button_create_script->hide ();
2788+ bool can_create_script = false ;
2789+ bool can_detach_script = false ;
2790+ bool can_extend_script = false ;
2791+
2792+ if (profile_allow_script_editing) {
28042793 Array selection = editor_selection->get_selected_nodes ();
2794+
28052795 for (int i = 0 ; i < selection.size (); i++) {
28062796 Node *n = Object::cast_to<Node>(selection[i]);
2807- if (!n->get_script ().is_null ()) {
2808- button_detach_script->show ();
2809- return ;
2797+ Ref<Script> s = n->get_script ();
2798+ Ref<Script> cts;
2799+
2800+ if (n->has_meta (SceneStringName (_custom_type_script))) {
2801+ cts = n->get_meta (SceneStringName (_custom_type_script));
2802+ }
2803+
2804+ if (selection.size () == 1 ) {
2805+ if (s.is_valid ()) {
2806+ if (cts.is_valid () && s == cts) {
2807+ can_extend_script = true ;
2808+ }
2809+ } else {
2810+ can_create_script = true ;
2811+ }
2812+ }
2813+
2814+ if (s.is_valid ()) {
2815+ if (cts.is_valid ()) {
2816+ if (s != cts) {
2817+ can_detach_script = true ;
2818+ break ;
2819+ }
2820+ } else {
2821+ can_detach_script = true ;
2822+ break ;
2823+ }
28102824 }
28112825 }
2812- button_detach_script->hide ();
28132826 }
2827+
2828+ button_create_script->set_visible (can_create_script);
2829+ button_detach_script->set_visible (can_detach_script);
2830+ button_extend_script->set_visible (can_extend_script);
28142831}
28152832
28162833void SceneTreeDock::_selection_changed () {
@@ -3057,7 +3074,28 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro
30573074 Node *newnode = p_by_node;
30583075
30593076 if (p_keep_properties) {
3060- Node *default_oldnode = Object::cast_to<Node>(ClassDB::instantiate (oldnode->get_class ()));
3077+ Node *default_oldnode = nullptr ;
3078+
3079+ // If we're dealing with a custom node type, we need to create a default instance of the custom type instead of the native type for property comparison.
3080+ if (oldnode->has_meta (SceneStringName (_custom_type_script))) {
3081+ Ref<Script> cts = oldnode->get_meta (SceneStringName (_custom_type_script));
3082+ default_oldnode = Object::cast_to<Node>(get_editor_data ()->script_class_instance (cts->get_global_name ()));
3083+ if (default_oldnode) {
3084+ default_oldnode->set_name (cts->get_global_name ());
3085+ get_editor_data ()->instantiate_object_properties (default_oldnode);
3086+ } else {
3087+ // Legacy custom type, registered with "add_custom_type()".
3088+ // TODO: Should probably be deprecated in 4.x.
3089+ const EditorData::CustomType *custom_type = get_editor_data ()->get_custom_type_by_path (cts->get_path ());
3090+ if (custom_type) {
3091+ default_oldnode = Object::cast_to<Node>(get_editor_data ()->instantiate_custom_type (custom_type->name , cts->get_instance_base_type ()));
3092+ }
3093+ }
3094+ }
3095+
3096+ if (!default_oldnode) {
3097+ default_oldnode = Object::cast_to<Node>(ClassDB::instantiate (oldnode->get_class ()));
3098+ }
30613099
30623100 List<PropertyInfo> pinfo;
30633101 oldnode->get_property_list (&pinfo);
@@ -3542,6 +3580,27 @@ void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) {
35423580 undo_redo->add_undo_method (ed, " live_debug_remove_node" , NodePath (String (edited_scene->get_path_to (n)).path_join (new_node->get_name ())));
35433581 undo_redo->commit_action ();
35443582 } else {
3583+ // Check if dropped script is compatible.
3584+ if (n->has_meta (SceneStringName (_custom_type_script))) {
3585+ Ref<Script> ct_scr = n->get_meta (SceneStringName (_custom_type_script));
3586+ if (!scr->inherits_script (ct_scr)) {
3587+ String custom_type_name = ct_scr->get_global_name ();
3588+
3589+ // Legacy custom type, registered with "add_custom_type()".
3590+ if (custom_type_name.is_empty ()) {
3591+ const EditorData::CustomType *custom_type = get_editor_data ()->get_custom_type_by_path (ct_scr->get_path ());
3592+ if (custom_type) {
3593+ custom_type_name = custom_type->name ;
3594+ } else {
3595+ custom_type_name = TTR (" <unknown>" );
3596+ }
3597+ }
3598+
3599+ WARN_PRINT_ED (vformat (" Script does not extend type: '%s'." , custom_type_name));
3600+ return ;
3601+ }
3602+ }
3603+
35453604 undo_redo->create_action (TTR (" Attach Script" ), UndoRedo::MERGE_DISABLE, n);
35463605 undo_redo->add_do_method (InspectorDock::get_singleton (), " store_script_properties" , n);
35473606 undo_redo->add_undo_method (InspectorDock::get_singleton (), " store_script_properties" , n);
@@ -3649,6 +3708,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
36493708
36503709 Ref<Script> existing_script;
36513710 bool existing_script_removable = true ;
3711+ bool allow_attach_new_script = true ;
36523712 if (selection.size () == 1 ) {
36533713 Node *selected = selection.front ()->get ();
36543714
@@ -3672,6 +3732,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
36723732 if (EditorNode::get_singleton ()->get_object_custom_type_base (selected) == existing_script) {
36733733 existing_script_removable = false ;
36743734 }
3735+
3736+ if (selected->has_meta (SceneStringName (_custom_type_script))) {
3737+ allow_attach_new_script = false ;
3738+ }
36753739 }
36763740
36773741 if (profile_allow_editing) {
@@ -3692,7 +3756,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
36923756
36933757 if (full_selection.size () == 1 ) {
36943758 add_separator = true ;
3695- menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ScriptCreate" )), ED_GET_SHORTCUT (" scene_tree/attach_script" ), TOOL_ATTACH_SCRIPT);
3759+ if (allow_attach_new_script) {
3760+ menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ScriptCreate" )), ED_GET_SHORTCUT (" scene_tree/attach_script" ), TOOL_ATTACH_SCRIPT);
3761+ }
3762+
36963763 if (existing_script.is_valid ()) {
36973764 menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ScriptExtend" )), ED_GET_SHORTCUT (" scene_tree/extend_script" ), TOOL_EXTEND_SCRIPT);
36983765 }
@@ -4601,6 +4668,14 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
46014668 filter_hbc->add_child (button_detach_script);
46024669 button_detach_script->hide ();
46034670
4671+ button_extend_script = memnew (Button);
4672+ button_extend_script->set_flat (true );
4673+ button_extend_script->connect (SceneStringName (pressed), callable_mp (this , &SceneTreeDock::_tool_selected).bind (TOOL_EXTEND_SCRIPT, false ));
4674+ button_extend_script->set_tooltip_text (TTR (" Extend the script of the selected node." ));
4675+ button_extend_script->set_shortcut (ED_GET_SHORTCUT (" scene_tree/extend_script" ));
4676+ filter_hbc->add_child (button_extend_script);
4677+ button_extend_script->hide ();
4678+
46044679 button_tree_menu = memnew (MenuButton);
46054680 button_tree_menu->set_flat (false );
46064681 button_tree_menu->set_theme_type_variation (" FlatMenuButton" );
0 commit comments