Add support for internal nodes

This commit is contained in:
kobewi 2021-08-25 15:49:30 +02:00
parent d04aa9a114
commit a913ae8d56
21 changed files with 196 additions and 88 deletions

View File

@ -109,9 +109,11 @@
<return type="void" />
<argument index="0" name="node" type="Node" />
<argument index="1" name="legible_unique_name" type="bool" default="false" />
<argument index="2" name="internal" type="int" enum="Node.InternalMode" default="0" />
<description>
Adds a child node. Nodes can have any number of children, but every child must have a unique name. Child nodes are automatically deleted when the parent node is deleted, so an entire scene can be removed by deleting its topmost node.
If [code]legible_unique_name[/code] is [code]true[/code], the child node will have a human-readable name based on the name of the node being instantiated instead of its type.
If [code]internal[/code] is different than [constant INTERNAL_MODE_DISABLED], the child will be added as internal node. Such nodes are ignored by methods like [method get_children], unless their parameter [code]include_internal[/code] is [code]true[/code].The intended usage is to hide the internal nodes from the user, so the user won't accidentally delete or modify them. Used by some GUI nodes, e.g. [ColorPicker]. See [enum InternalMode] for available modes.
[b]Note:[/b] If the child node already has a parent, the function will fail. Use [method remove_child] first to remove the node from its current parent. For example:
[codeblocks]
[gdscript]
@ -141,6 +143,7 @@
Adds a [code]sibling[/code] node to current's node parent, at the same level as that node, right below it.
If [code]legible_unique_name[/code] is [code]true[/code], the child node will have a human-readable name based on the name of the node being instantiated instead of its type.
Use [method add_child] instead of this method if you don't need the child node to be added below a specific node in the list of children.
[b]Note:[/b] If this node is internal, the new sibling will be internal too (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
<method name="add_to_group">
@ -200,22 +203,28 @@
<method name="get_child" qualifiers="const">
<return type="Node" />
<argument index="0" name="idx" type="int" />
<argument index="1" name="include_internal" type="bool" default="false" />
<description>
Returns a child node by its index (see [method get_child_count]). This method is often used for iterating all children of a node.
Negative indices access the children from the last one.
If [code]include_internal[/code] is [code]true[/code], internal children are skipped (see [code]internal[/code] parameter in [method add_child]).
To access a child node via its name, use [method get_node].
</description>
</method>
<method name="get_child_count" qualifiers="const">
<return type="int" />
<argument index="0" name="include_internal" type="bool" default="false" />
<description>
Returns the number of child nodes.
If [code]include_internal[/code] is [code]false[/code], internal children aren't counted (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
<method name="get_children" qualifiers="const">
<return type="Node[]" />
<argument index="0" name="include_internal" type="bool" default="false" />
<description>
Returns an array of references to node's children.
If [code]include_internal[/code] is [code]false[/code], the returned array won't include internal children (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
<method name="get_editor_description" qualifiers="const">
@ -231,8 +240,10 @@
</method>
<method name="get_index" qualifiers="const">
<return type="int" />
<argument index="0" name="include_internal" type="bool" default="false" />
<description>
Returns the node's order in the scene tree branch. For example, if called on the first child node the position is [code]0[/code].
If [code]include_internal[/code] is [code]false[/code], the index won't take internal children into account, i.e. first non-internal child will have index of 0 (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
<method name="get_network_master" qualifiers="const">
@ -460,6 +471,7 @@
<argument index="1" name="to_position" type="int" />
<description>
Moves a child node to a different position (order) among the other children. Since calls, signals, etc are performed by tree order, changing the order of children nodes may be useful.
[b]Note:[/b] Internal children can only be moved within their expected "internal range" (see [code]internal[/code] parameter in [method add_child]).
</description>
</method>
<method name="print_stray_nodes">
@ -888,5 +900,14 @@
Duplicate using instancing.
An instance stays linked to the original so when the original changes, the instance changes too.
</constant>
<constant name="INTERNAL_MODE_DISABLED" value="0" enum="InternalMode">
Node will not be internal.
</constant>
<constant name="INTERNAL_MODE_FRONT" value="1" enum="InternalMode">
Node will be placed at the front of parent's node list, before any non-internal sibling.
</constant>
<constant name="INTERNAL_MODE_BACK" value="2" enum="InternalMode">
Node will be placed at the back of parent's node list, after any non-internal sibling.
</constant>
</constants>
</class>

View File

@ -351,11 +351,11 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
Label *l = memnew(Label);
l->set_theme_type_variation("HeaderSmall");
l->set_text(p_label);
add_child(l);
add_child(l, false, INTERNAL_MODE_FRONT);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
mc->add_child(p_control);
add_child(mc);
add_child(mc, false, INTERNAL_MODE_FRONT);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
}

View File

@ -1139,7 +1139,7 @@ void ColorPicker::_bind_methods() {
ColorPicker::ColorPicker() :
BoxContainer(true) {
HBoxContainer *hb_edit = memnew(HBoxContainer);
add_child(hb_edit);
add_child(hb_edit, false, INTERNAL_MODE_FRONT);
hb_edit->set_v_size_flags(SIZE_EXPAND_FILL);
hb_edit->add_child(uv_edit);
@ -1151,7 +1151,7 @@ ColorPicker::ColorPicker() :
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
HBoxContainer *hb_smpl = memnew(HBoxContainer);
add_child(hb_smpl);
add_child(hb_smpl, false, INTERNAL_MODE_FRONT);
hb_smpl->add_child(sample);
sample->set_h_size_flags(SIZE_EXPAND_FILL);
@ -1165,12 +1165,12 @@ ColorPicker::ColorPicker() :
btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed));
VBoxContainer *vbl = memnew(VBoxContainer);
add_child(vbl);
add_child(vbl, false, INTERNAL_MODE_FRONT);
add_child(memnew(HSeparator));
add_child(memnew(HSeparator), false, INTERNAL_MODE_FRONT);
VBoxContainer *vbr = memnew(VBoxContainer);
add_child(vbr);
add_child(vbr, false, INTERNAL_MODE_FRONT);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
for (int i = 0; i < 4; i++) {
@ -1273,11 +1273,11 @@ ColorPicker::ColorPicker() :
set_pick_color(Color(1, 1, 1));
add_child(preset_separator);
add_child(preset_separator, false, INTERNAL_MODE_FRONT);
preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
preset_container->set_columns(preset_column_count);
add_child(preset_container);
add_child(preset_container, false, INTERNAL_MODE_FRONT);
btn_add_preset->set_icon_align(Button::ALIGN_CENTER);
btn_add_preset->set_tooltip(RTR("Add current color as a preset."));
@ -1405,7 +1405,7 @@ void ColorPickerButton::_update_picker() {
picker = memnew(ColorPicker);
picker->set_anchors_and_offsets_preset(PRESET_WIDE);
popup->add_child(picker);
add_child(popup);
add_child(popup, false, INTERNAL_MODE_FRONT);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));
popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));

View File

@ -319,7 +319,7 @@ AcceptDialog::AcceptDialog() {
set_clamp_to_embedder(true);
bg = memnew(Panel);
add_child(bg);
add_child(bg, false, INTERNAL_MODE_FRONT);
hbc = memnew(HBoxContainer);
@ -331,9 +331,9 @@ AcceptDialog::AcceptDialog() {
label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END);
label->set_begin(Point2(margin, margin));
label->set_end(Point2(-margin, -button_margin - 10));
add_child(label);
add_child(label, false, INTERNAL_MODE_FRONT);
add_child(hbc);
add_child(hbc, false, INTERNAL_MODE_FRONT);
hbc->add_spacer();
ok = memnew(Button);

View File

@ -924,7 +924,7 @@ FileDialog::FileDialog() {
show_hidden_files = default_show_hidden_files;
vbox = memnew(VBoxContainer);
add_child(vbox);
add_child(vbox, false, INTERNAL_MODE_FRONT);
vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed));
mode = FILE_MODE_SAVE_FILE;
@ -1023,8 +1023,7 @@ FileDialog::FileDialog() {
filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected));
confirm_save = memnew(ConfirmationDialog);
// confirm_save->set_as_top_level(true);
add_child(confirm_save);
add_child(confirm_save, false, INTERNAL_MODE_FRONT);
confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed));
@ -1036,16 +1035,16 @@ FileDialog::FileDialog() {
makedirname = memnew(LineEdit);
makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
makevb->add_margin_child(TTRC("Name:"), makedirname);
add_child(makedialog);
add_child(makedialog, false, INTERNAL_MODE_FRONT);
makedialog->register_text_enter(makedirname);
makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm));
mkdirerr = memnew(AcceptDialog);
mkdirerr->set_text(TTRC("Could not create folder."));
add_child(mkdirerr);
add_child(mkdirerr, false, INTERNAL_MODE_FRONT);
exterr = memnew(AcceptDialog);
exterr->set_text(TTRC("Must use a valid extension."));
add_child(exterr);
add_child(exterr, false, INTERNAL_MODE_FRONT);
update_filters();
update_dir();

View File

@ -48,7 +48,7 @@ GradientEdit::GradientEdit() {
picker = memnew(ColorPicker);
popup->add_child(picker);
add_child(popup);
add_child(popup, false, INTERNAL_MODE_FRONT);
}
int GradientEdit::_get_point_from_pos(int x) {

View File

@ -366,7 +366,6 @@ void GraphEdit::_graph_node_raised(Node *p_gn) {
}
move_child(connections_layer, first_not_comment);
top_layer->raise();
emit_signal(SNAME("node_selected"), p_gn);
}
@ -2246,14 +2245,14 @@ GraphEdit::GraphEdit() {
zoom_max = (1 * Math::pow(zoom_step, 4));
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer);
add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw));
top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input));
connections_layer = memnew(Control);
add_child(connections_layer);
add_child(connections_layer, false, INTERNAL_MODE_FRONT);
connections_layer->connect("draw", callable_mp(this, &GraphEdit::_connections_layer_draw));
connections_layer->set_name("CLAYER");
connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offset

View File

@ -1656,7 +1656,7 @@ void ItemList::_bind_methods() {
ItemList::ItemList() {
scroll_bar = memnew(VScrollBar);
add_child(scroll_bar);
add_child(scroll_bar, false, INTERNAL_MODE_FRONT);
scroll_bar->connect("value_changed", callable_mp(this, &ItemList::_scroll_changed));

View File

@ -2220,7 +2220,7 @@ void LineEdit::_bind_methods() {
void LineEdit::_ensure_menu() {
if (!menu) {
menu = memnew(PopupMenu);
add_child(menu);
add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
@ -2305,7 +2305,7 @@ LineEdit::LineEdit() {
set_mouse_filter(MOUSE_FILTER_STOP);
caret_blink_timer = memnew(Timer);
add_child(caret_blink_timer);
add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);

View File

@ -172,7 +172,7 @@ MenuButton::MenuButton() {
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
add_child(popup, false, INTERNAL_MODE_FRONT);
popup->connect("about_to_popup", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(true));
popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false));
}

View File

@ -351,7 +351,7 @@ OptionButton::OptionButton() {
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
add_child(popup, false, INTERNAL_MODE_FRONT);
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));

View File

@ -255,5 +255,5 @@ void PopupPanel::_notification(int p_what) {
PopupPanel::PopupPanel() {
panel = memnew(Panel);
add_child(panel);
add_child(panel, false, INTERNAL_MODE_FRONT);
}

View File

@ -1689,7 +1689,7 @@ PopupMenu::PopupMenu() {
// Margin Container
margin_container = memnew(MarginContainer);
margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
add_child(margin_container);
add_child(margin_container, false, INTERNAL_MODE_FRONT);
margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
// Scroll Container
@ -1703,7 +1703,7 @@ PopupMenu::PopupMenu() {
control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
scroll_container->add_child(control);
scroll_container->add_child(control, false, INTERNAL_MODE_FRONT);
control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
connect("window_input", callable_mp(this, &PopupMenu::gui_input));
@ -1712,13 +1712,13 @@ PopupMenu::PopupMenu() {
submenu_timer->set_wait_time(0.3);
submenu_timer->set_one_shot(true);
submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
add_child(submenu_timer);
add_child(submenu_timer, false, INTERNAL_MODE_FRONT);
minimum_lifetime_timer = memnew(Timer);
minimum_lifetime_timer->set_wait_time(0.3);
minimum_lifetime_timer->set_one_shot(true);
minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
add_child(minimum_lifetime_timer);
add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT);
}
PopupMenu::~PopupMenu() {

View File

@ -4361,7 +4361,7 @@ RichTextLabel::RichTextLabel() {
current_frame = main;
vscroll = memnew(VScrollBar);
add_child(vscroll);
add_child(vscroll, false, INTERNAL_MODE_FRONT);
vscroll->set_drag_node(String(".."));
vscroll->set_step(1);
vscroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);

View File

@ -228,9 +228,6 @@ void ScrollContainer::_update_scrollbar_position() {
v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
h_scroll->raise();
v_scroll->raise();
_updating_scrollbars = false;
}
@ -618,12 +615,12 @@ void ScrollContainer::_bind_methods() {
ScrollContainer::ScrollContainer() {
h_scroll = memnew(HScrollBar);
h_scroll->set_name("_h_scroll");
add_child(h_scroll);
add_child(h_scroll, false, INTERNAL_MODE_BACK);
h_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
v_scroll = memnew(VScrollBar);
v_scroll->set_name("_v_scroll");
add_child(v_scroll);
add_child(v_scroll, false, INTERNAL_MODE_BACK);
v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");

View File

@ -278,7 +278,7 @@ void SpinBox::_bind_methods() {
SpinBox::SpinBox() {
line_edit = memnew(LineEdit);
add_child(line_edit);
add_child(line_edit, false, INTERNAL_MODE_FRONT);
line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
@ -291,5 +291,5 @@ SpinBox::SpinBox() {
range_click_timer = memnew(Timer);
range_click_timer->connect("timeout", callable_mp(this, &SpinBox::_range_click_timeout));
add_child(range_click_timer);
add_child(range_click_timer, false, INTERNAL_MODE_FRONT);
}

View File

@ -905,7 +905,7 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
from_tabc->remove_child(moving_tabc);
add_child(moving_tabc);
add_child(moving_tabc, false, INTERNAL_MODE_FRONT);
if (hover_now < 0) {
hover_now = get_tab_count() - 1;
}

View File

@ -5004,7 +5004,7 @@ void TextEdit::_paste_internal() {
void TextEdit::_generate_context_menu() {
if (!menu) {
menu = memnew(PopupMenu);
add_child(menu);
add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
@ -5012,7 +5012,7 @@ void TextEdit::_generate_context_menu() {
menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO);
menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR);
menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL);
menu->add_child(menu_dir);
menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
menu_ctl->set_name("CTLMenu");
@ -5034,7 +5034,7 @@ void TextEdit::_generate_context_menu() {
menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
menu->add_child(menu_ctl);
menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
@ -5948,8 +5948,8 @@ TextEdit::TextEdit() {
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
add_child(h_scroll);
add_child(v_scroll);
add_child(h_scroll, false, INTERNAL_MODE_FRONT);
add_child(v_scroll, false, INTERNAL_MODE_FRONT);
h_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
@ -5958,19 +5958,19 @@ TextEdit::TextEdit() {
/* Caret. */
caret_blink_timer = memnew(Timer);
add_child(caret_blink_timer);
add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
/* Selection. */
click_select_held = memnew(Timer);
add_child(click_select_held);
add_child(click_select_held, false, INTERNAL_MODE_FRONT);
click_select_held->set_wait_time(0.05);
click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held));
idle_detect = memnew(Timer);
add_child(idle_detect);
add_child(idle_detect, false, INTERNAL_MODE_FRONT);
idle_detect->set_one_shot(true);
idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec"));
idle_detect->connect("timeout", callable_mp(this, &TextEdit::_push_current_op));

View File

@ -4795,12 +4795,11 @@ Tree::Tree() {
popup_menu = memnew(PopupMenu);
popup_menu->hide();
add_child(popup_menu);
// popup_menu->set_as_top_level(true);
add_child(popup_menu, false, INTERNAL_MODE_FRONT);
popup_editor = memnew(Popup);
popup_editor->set_wrap_controls(true);
add_child(popup_editor);
add_child(popup_editor, false, INTERNAL_MODE_FRONT);
popup_editor_vb = memnew(VBoxContainer);
popup_editor->add_child(popup_editor_vb);
popup_editor_vb->add_theme_constant_override("separation", 0);
@ -4818,12 +4817,12 @@ Tree::Tree() {
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
add_child(h_scroll);
add_child(v_scroll);
add_child(h_scroll, false, INTERNAL_MODE_FRONT);
add_child(v_scroll, false, INTERNAL_MODE_FRONT);
range_click_timer = memnew(Timer);
range_click_timer->connect("timeout", callable_mp(this, &Tree::_range_click_timeout));
add_child(range_click_timer);
add_child(range_click_timer, false, INTERNAL_MODE_FRONT);
h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));

View File

@ -48,6 +48,7 @@
#include <stdint.h>
VARIANT_ENUM_CAST(Node::ProcessMode);
VARIANT_ENUM_CAST(Node::InternalMode);
int Node::orphan_node_count = 0;
@ -291,14 +292,40 @@ void Node::_propagate_exit_tree() {
void Node::move_child(Node *p_child, int p_pos) {
ERR_FAIL_NULL(p_child);
ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos));
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
// We need to check whether node is internal and move it only in the relevant node range.
if (p_child->_is_internal_front()) {
ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos));
_move_child(p_child, p_pos);
} else if (p_child->_is_internal_back()) {
ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos));
_move_child(p_child, data.children.size() - data.internal_children_back + p_pos);
} else {
ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos));
_move_child(p_child, p_pos + data.internal_children_front);
}
}
void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup).");
// Specifying one place beyond the end
// means the same as moving to the last position
if (p_pos == data.children.size()) {
p_pos--;
if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly.
if (p_child->_is_internal_front()) {
if (p_pos == data.internal_children_front) {
p_pos--;
}
} else if (p_child->_is_internal_back()) {
if (p_pos == data.children.size()) {
p_pos--;
}
} else {
if (p_pos == data.children.size() - data.internal_children_back) {
p_pos--;
}
}
}
if (p_child->data.pos == p_pos) {
@ -339,7 +366,14 @@ void Node::raise() {
return;
}
data.parent->move_child(this, data.parent->data.children.size() - 1);
// Internal children move within a different index range.
if (_is_internal_front()) {
data.parent->move_child(this, data.parent->data.internal_children_front - 1);
} else if (_is_internal_back()) {
data.parent->move_child(this, data.parent->data.internal_children_back - 1);
} else {
data.parent->move_child(this, data.parent->get_child_count(false) - 1);
}
}
void Node::add_child_notify(Node *p_child) {
@ -1058,6 +1092,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
p_child->data.pos = data.children.size();
data.children.push_back(p_child);
p_child->data.parent = this;
if (data.internal_children_back > 0) {
_move_child(p_child, data.children.size() - data.internal_children_back - 1);
}
p_child->notification(NOTIFICATION_PARENTED);
if (data.tree) {
@ -1070,7 +1108,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
add_child_notify(p_child);
}
void Node::add_child(Node *p_child, bool p_legible_unique_name) {
void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) {
ERR_FAIL_NULL(p_child);
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent
@ -1079,19 +1117,35 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
#endif
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead.");
/* Validate name */
_validate_child_name(p_child, p_legible_unique_name);
_add_child_nocheck(p_child, p_child->data.name);
if (p_internal == INTERNAL_MODE_FRONT) {
_move_child(p_child, data.internal_children_front);
data.internal_children_front++;
} else if (p_internal == INTERNAL_MODE_BACK) {
if (data.internal_children_back > 0) {
_move_child(p_child, data.children.size() - 1, true);
}
data.internal_children_back++;
}
}
void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
ERR_FAIL_NULL(p_sibling);
ERR_FAIL_NULL(data.parent);
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead.");
get_parent()->add_child(p_sibling, p_legible_unique_name);
get_parent()->move_child(p_sibling, this->get_index() + 1);
InternalMode internal = INTERNAL_MODE_DISABLED;
if (_is_internal_front()) { // The sibling will have the same internal status.
internal = INTERNAL_MODE_FRONT;
} else if (_is_internal_back()) {
internal = INTERNAL_MODE_BACK;
}
data.parent->add_child(p_sibling, p_legible_unique_name, internal);
data.parent->_move_child(p_sibling, get_index() + 1);
}
void Node::_propagate_validate_owner() {
@ -1145,7 +1199,12 @@ void Node::remove_child(Node *p_child) {
ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name()));
//ERR_FAIL_COND( p_child->data.blocked > 0 );
//if (data.scene) { does not matter
// If internal child, update the counter.
if (p_child->_is_internal_front()) {
data.internal_children_front--;
} else if (p_child->_is_internal_back()) {
data.internal_children_back--;
}
p_child->_set_tree(nullptr);
//}
@ -1175,17 +1234,29 @@ void Node::remove_child(Node *p_child) {
}
}
int Node::get_child_count() const {
return data.children.size();
int Node::get_child_count(bool p_include_internal) const {
if (p_include_internal) {
return data.children.size();
} else {
return data.children.size() - data.internal_children_front - data.internal_children_back;
}
}
Node *Node::get_child(int p_index) const {
if (p_index < 0) {
p_index += data.children.size();
Node *Node::get_child(int p_index, bool p_include_internal) const {
if (p_include_internal) {
if (p_index < 0) {
p_index += data.children.size();
}
ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
return data.children[p_index];
} else {
if (p_index < 0) {
p_index += data.children.size() - data.internal_children_front - data.internal_children_back;
}
ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr);
p_index += data.internal_children_front;
return data.children[p_index];
}
ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
return data.children[p_index];
}
Node *Node::_get_child_by_name(const StringName &p_name) const {
@ -1717,7 +1788,13 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
data.blocked--;
}
int Node::get_index() const {
int Node::get_index(bool p_include_internal) const {
// p_include_internal = false doesn't make sense if the node is internal.
ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false.");
if (data.parent && !p_include_internal) {
return data.pos - data.parent->data.internal_children_front;
}
return data.pos;
}
@ -2429,12 +2506,12 @@ void Node::queue_delete() {
}
}
TypedArray<Node> Node::_get_children() const {
TypedArray<Node> Node::_get_children(bool p_include_internal) const {
TypedArray<Node> arr;
int cc = get_child_count();
int cc = get_child_count(p_include_internal);
arr.resize(cc);
for (int i = 0; i < cc; i++) {
arr[i] = get_child(i);
arr[i] = get_child(i, p_include_internal);
}
return arr;
@ -2581,11 +2658,11 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name);
ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name);
ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name"), &Node::add_child, DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0));
ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child);
ClassDB::bind_method(D_METHOD("get_child_count"), &Node::get_child_count);
ClassDB::bind_method(D_METHOD("get_children"), &Node::_get_children);
ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child);
ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript.
ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node);
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);
ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null);
@ -2609,7 +2686,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner);
ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner);
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false));
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
@ -2747,6 +2824,10 @@ void Node::_bind_methods() {
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED);
BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT);
BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK);
ADD_SIGNAL(MethodInfo("ready"));
ADD_SIGNAL(MethodInfo("renamed"));
ADD_SIGNAL(MethodInfo("tree_entered"));

View File

@ -73,6 +73,12 @@ public:
NAME_CASING_SNAKE_CASE
};
enum InternalMode {
INTERNAL_MODE_DISABLED,
INTERNAL_MODE_FRONT,
INTERNAL_MODE_BACK,
};
struct Comparator {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
@ -97,6 +103,8 @@ private:
Node *parent = nullptr;
Node *owner = nullptr;
Vector<Node *> children;
int internal_children_front = 0;
int internal_children_back = 0;
int pos = -1;
int depth = -1;
int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
@ -172,12 +180,15 @@ private:
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const;
TypedArray<Node> _get_children() const;
TypedArray<Node> _get_children(bool p_include_internal = true) const;
Array _get_groups() const;
Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
_FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; }
_FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
friend class SceneTree;
void _set_tree(SceneTree *p_tree);
@ -284,12 +295,12 @@ public:
StringName get_name() const;
void set_name(const String &p_name);
void add_child(Node *p_child, bool p_legible_unique_name = false);
void add_child(Node *p_child, bool p_legible_unique_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
void add_sibling(Node *p_sibling, bool p_legible_unique_name = false);
void remove_child(Node *p_child);
int get_child_count() const;
Node *get_child(int p_index) const;
int get_child_count(bool p_include_internal = true) const;
Node *get_child(int p_index, bool p_include_internal = true) const;
bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;
@ -327,6 +338,7 @@ public:
int get_persistent_group_count() const;
void move_child(Node *p_child, int p_pos);
void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false);
void raise();
void set_owner(Node *p_owner);
@ -334,7 +346,7 @@ public:
void get_owned_by(Node *p_by, List<Node *> *p_owned);
void remove_and_skip();
int get_index() const;
int get_index(bool p_include_internal = true) const;
Ref<Tween> create_tween();