Skip to content

Commit fa673be

Browse files
committed
Merge pull request godotengine#91341 from bjornmp/NewMaster
Enforce custom nodes to keep their original type
2 parents 1001a8c + 06998a3 commit fa673be

10 files changed

+147
-25
lines changed

editor/editor_data.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ Variant EditorData::instantiate_custom_type(const String &p_type, const String &
547547
if (n) {
548548
n->set_name(p_type);
549549
}
550+
n->set_meta(SceneStringName(_custom_type_script), script);
550551
((Object *)ob)->set_script(script);
551552
return ob;
552553
}
@@ -1008,6 +1009,7 @@ Variant EditorData::script_class_instance(const String &p_class) {
10081009
// Store in a variant to initialize the refcount if needed.
10091010
Variant obj = ClassDB::instantiate(script->get_instance_base_type());
10101011
if (obj) {
1012+
Object::cast_to<Object>(obj)->set_meta(SceneStringName(_custom_type_script), script);
10111013
obj.operator Object *()->set_script(script);
10121014
}
10131015
return obj;

editor/editor_node.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4675,6 +4675,11 @@ void EditorNode::stop_child_process(OS::ProcessID p_pid) {
46754675
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
46764676
ERR_FAIL_NULL_V(p_object, nullptr);
46774677

4678+
const Node *node = Object::cast_to<const Node>(p_object);
4679+
if (node && node->has_meta(SceneStringName(_custom_type_script))) {
4680+
return node->get_meta(SceneStringName(_custom_type_script));
4681+
}
4682+
46784683
Ref<Script> scr = p_object->get_script();
46794684

46804685
if (scr.is_valid()) {

editor/editor_properties.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,6 +3221,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const
32213221
}
32223222

32233223
resource_picker->set_base_type(p_base_type);
3224+
resource_picker->set_resource_owner(p_object);
32243225
resource_picker->set_editable(true);
32253226
resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);
32263227
add_child(resource_picker);

editor/editor_resource_picker.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,9 @@ void EditorResourcePicker::_update_menu_items() {
224224
}
225225

226226
if (is_editable()) {
227-
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
227+
if (!_is_custom_type_script()) {
228+
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
229+
}
228230
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
229231

230232
// Check whether the resource has subresources.
@@ -694,6 +696,16 @@ bool EditorResourcePicker::_is_type_valid(const String &p_type_name, const HashS
694696
return false;
695697
}
696698

699+
bool EditorResourcePicker::_is_custom_type_script() const {
700+
Ref<Script> resource_as_script = edited_resource;
701+
702+
if (resource_as_script.is_valid() && resource_owner && resource_owner->has_meta(SceneStringName(_custom_type_script))) {
703+
return true;
704+
}
705+
706+
return false;
707+
}
708+
697709
Variant EditorResourcePicker::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
698710
if (edited_resource.is_valid()) {
699711
Dictionary drag_data = EditorNode::get_singleton()->drag_resource(edited_resource, p_from);
@@ -953,6 +965,10 @@ bool EditorResourcePicker::is_toggle_pressed() const {
953965
return assign_button->is_pressed();
954966
}
955967

968+
void EditorResourcePicker::set_resource_owner(Object *p_object) {
969+
resource_owner = p_object;
970+
}
971+
956972
void EditorResourcePicker::set_editable(bool p_editable) {
957973
editable = p_editable;
958974
assign_button->set_disabled(!editable && !edited_resource.is_valid());
@@ -1098,7 +1114,10 @@ void EditorScriptPicker::set_create_options(Object *p_menu_node) {
10981114
return;
10991115
}
11001116

1101-
menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT);
1117+
if (!(script_owner && script_owner->has_meta(SceneStringName(_custom_type_script)))) {
1118+
menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT);
1119+
}
1120+
11021121
if (script_owner) {
11031122
Ref<Script> scr = script_owner->get_script();
11041123
if (scr.is_valid()) {

editor/editor_resource_picker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class EditorResourcePicker : public HBoxContainer {
8181
CONVERT_BASE_ID = 1000,
8282
};
8383

84+
Object *resource_owner = nullptr;
85+
8486
PopupMenu *edit_menu = nullptr;
8587

8688
void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj);
@@ -102,6 +104,7 @@ class EditorResourcePicker : public HBoxContainer {
102104
void _ensure_allowed_types() const;
103105
bool _is_drop_valid(const Dictionary &p_drag_data) const;
104106
bool _is_type_valid(const String &p_type_name, const HashSet<StringName> &p_allowed_types) const;
107+
bool _is_custom_type_script() const;
105108

106109
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
107110
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
@@ -137,6 +140,8 @@ class EditorResourcePicker : public HBoxContainer {
137140
void set_toggle_pressed(bool p_pressed);
138141
bool is_toggle_pressed() const;
139142

143+
void set_resource_owner(Object *p_object);
144+
140145
void set_editable(bool p_editable);
141146
bool is_editable() const;
142147

editor/scene_tree_dock.cpp

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

27862787
void 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

28162833
void 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");

editor/scene_tree_dock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer {
115115
Button *button_instance = nullptr;
116116
Button *button_create_script = nullptr;
117117
Button *button_detach_script = nullptr;
118+
Button *button_extend_script = nullptr;
118119
MenuButton *button_tree_menu = nullptr;
119120

120121
Button *node_shortcuts_toggle = nullptr;

scene/property_utils.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
8989
*r_is_valid = false;
9090
}
9191

92+
// Handle special case "script" property, where the default value is either null or the custom type script.
93+
// Do this only if there's no states stack cache to trace for default values.
94+
if (!p_states_stack_cache && p_property == CoreStringName(script) && p_object->has_meta(SceneStringName(_custom_type_script))) {
95+
Ref<Script> ct_scr = p_object->get_meta(SceneStringName(_custom_type_script));
96+
if (r_is_valid) {
97+
*r_is_valid = true;
98+
}
99+
return ct_scr;
100+
}
101+
92102
Ref<Script> topmost_script;
93103

94104
if (const Node *node = Object::cast_to<Node>(p_object)) {

scene/scene_string_names.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ SceneStringNames::SceneStringNames() {
130130
shader_overrides_group = StaticCString::create("_shader_overrides_group_");
131131
shader_overrides_group_active = StaticCString::create("_shader_overrides_group_active_");
132132

133+
_custom_type_script = StaticCString::create("_custom_type_script");
134+
133135
pressed = StaticCString::create("pressed");
134136
id_pressed = StaticCString::create("id_pressed");
135137
toggled = StaticCString::create("toggled");

scene/scene_string_names.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ class SceneStringNames {
143143
StringName shader_overrides_group;
144144
StringName shader_overrides_group_active;
145145

146+
StringName _custom_type_script;
147+
146148
StringName pressed;
147149
StringName id_pressed;
148150
StringName toggled;

0 commit comments

Comments
 (0)