Browse Source

Add support for internal nodes

kobewi 4 years ago
parent
commit
a913ae8d56

+ 21 - 0
doc/classes/Node.xml

@@ -109,9 +109,11 @@
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="node" type="Node" />
 			<argument index="0" name="node" type="Node" />
 			<argument index="1" name="legible_unique_name" type="bool" default="false" />
 			<argument index="1" name="legible_unique_name" type="bool" default="false" />
+			<argument index="2" name="internal" type="int" enum="Node.InternalMode" default="0" />
 			<description>
 			<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.
 				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]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:
 				[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]
 				[codeblocks]
 				[gdscript]
 				[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.
 				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.
 				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.
 				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>
 			</description>
 		</method>
 		</method>
 		<method name="add_to_group">
 		<method name="add_to_group">
@@ -200,22 +203,28 @@
 		<method name="get_child" qualifiers="const">
 		<method name="get_child" qualifiers="const">
 			<return type="Node" />
 			<return type="Node" />
 			<argument index="0" name="idx" type="int" />
 			<argument index="0" name="idx" type="int" />
+			<argument index="1" name="include_internal" type="bool" default="false" />
 			<description>
 			<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.
 				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.
 				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].
 				To access a child node via its name, use [method get_node].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="get_child_count" qualifiers="const">
 		<method name="get_child_count" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
+			<argument index="0" name="include_internal" type="bool" default="false" />
 			<description>
 			<description>
 				Returns the number of child nodes.
 				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>
 			</description>
 		</method>
 		</method>
 		<method name="get_children" qualifiers="const">
 		<method name="get_children" qualifiers="const">
 			<return type="Node[]" />
 			<return type="Node[]" />
+			<argument index="0" name="include_internal" type="bool" default="false" />
 			<description>
 			<description>
 				Returns an array of references to node's children.
 				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>
 			</description>
 		</method>
 		</method>
 		<method name="get_editor_description" qualifiers="const">
 		<method name="get_editor_description" qualifiers="const">
@@ -231,8 +240,10 @@
 		</method>
 		</method>
 		<method name="get_index" qualifiers="const">
 		<method name="get_index" qualifiers="const">
 			<return type="int" />
 			<return type="int" />
+			<argument index="0" name="include_internal" type="bool" default="false" />
 			<description>
 			<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].
 				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>
 			</description>
 		</method>
 		</method>
 		<method name="get_network_master" qualifiers="const">
 		<method name="get_network_master" qualifiers="const">
@@ -460,6 +471,7 @@
 			<argument index="1" name="to_position" type="int" />
 			<argument index="1" name="to_position" type="int" />
 			<description>
 			<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.
 				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>
 			</description>
 		</method>
 		</method>
 		<method name="print_stray_nodes">
 		<method name="print_stray_nodes">
@@ -888,5 +900,14 @@
 			Duplicate using instancing.
 			Duplicate using instancing.
 			An instance stays linked to the original so when the original changes, the instance changes too.
 			An instance stays linked to the original so when the original changes, the instance changes too.
 		</constant>
 		</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>
 	</constants>
 </class>
 </class>

+ 2 - 2
scene/gui/box_container.cpp

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

+ 8 - 8
scene/gui/color_picker.cpp

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

+ 3 - 3
scene/gui/dialogs.cpp

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

+ 5 - 6
scene/gui/file_dialog.cpp

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

+ 1 - 1
scene/gui/gradient_edit.cpp

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

+ 2 - 3
scene/gui/graph_edit.cpp

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

+ 1 - 1
scene/gui/item_list.cpp

@@ -1656,7 +1656,7 @@ void ItemList::_bind_methods() {
 
 
 ItemList::ItemList() {
 ItemList::ItemList() {
 	scroll_bar = memnew(VScrollBar);
 	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));
 	scroll_bar->connect("value_changed", callable_mp(this, &ItemList::_scroll_changed));
 
 

+ 2 - 2
scene/gui/line_edit.cpp

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

+ 1 - 1
scene/gui/menu_button.cpp

@@ -172,7 +172,7 @@ MenuButton::MenuButton() {
 
 
 	popup = memnew(PopupMenu);
 	popup = memnew(PopupMenu);
 	popup->hide();
 	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("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));
 	popup->connect("popup_hide", callable_mp(this, &MenuButton::_popup_visibility_changed), varray(false));
 }
 }

+ 1 - 1
scene/gui/option_button.cpp

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

+ 1 - 1
scene/gui/popup.cpp

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

+ 4 - 4
scene/gui/popup_menu.cpp

@@ -1689,7 +1689,7 @@ PopupMenu::PopupMenu() {
 	// Margin Container
 	// Margin Container
 	margin_container = memnew(MarginContainer);
 	margin_container = memnew(MarginContainer);
 	margin_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
 	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));
 	margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
 
 
 	// Scroll Container
 	// Scroll Container
@@ -1703,7 +1703,7 @@ PopupMenu::PopupMenu() {
 	control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
 	control->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
 	control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	control->set_v_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));
 	control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
 
 
 	connect("window_input", callable_mp(this, &PopupMenu::gui_input));
 	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_wait_time(0.3);
 	submenu_timer->set_one_shot(true);
 	submenu_timer->set_one_shot(true);
 	submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
 	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 = memnew(Timer);
 	minimum_lifetime_timer->set_wait_time(0.3);
 	minimum_lifetime_timer->set_wait_time(0.3);
 	minimum_lifetime_timer->set_one_shot(true);
 	minimum_lifetime_timer->set_one_shot(true);
 	minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
 	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() {
 PopupMenu::~PopupMenu() {

+ 1 - 1
scene/gui/rich_text_label.cpp

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

+ 2 - 5
scene/gui/scroll_container.cpp

@@ -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_TOP, ANCHOR_BEGIN, 0);
 	v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
 	v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
 
 
-	h_scroll->raise();
-	v_scroll->raise();
-
 	_updating_scrollbars = false;
 	_updating_scrollbars = false;
 }
 }
 
 
@@ -618,12 +615,12 @@ void ScrollContainer::_bind_methods() {
 ScrollContainer::ScrollContainer() {
 ScrollContainer::ScrollContainer() {
 	h_scroll = memnew(HScrollBar);
 	h_scroll = memnew(HScrollBar);
 	h_scroll->set_name("_h_scroll");
 	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));
 	h_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
 
 
 	v_scroll = memnew(VScrollBar);
 	v_scroll = memnew(VScrollBar);
 	v_scroll->set_name("_v_scroll");
 	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));
 	v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
 
 
 	deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
 	deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");

+ 2 - 2
scene/gui/spin_box.cpp

@@ -278,7 +278,7 @@ void SpinBox::_bind_methods() {
 
 
 SpinBox::SpinBox() {
 SpinBox::SpinBox() {
 	line_edit = memnew(LineEdit);
 	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_anchors_and_offsets_preset(Control::PRESET_WIDE);
 	line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
 	line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -291,5 +291,5 @@ SpinBox::SpinBox() {
 
 
 	range_click_timer = memnew(Timer);
 	range_click_timer = memnew(Timer);
 	range_click_timer->connect("timeout", callable_mp(this, &SpinBox::_range_click_timeout));
 	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);
 }
 }

+ 1 - 1
scene/gui/tab_container.cpp

@@ -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()) {
 			if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
 				Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
 				Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
 				from_tabc->remove_child(moving_tabc);
 				from_tabc->remove_child(moving_tabc);
-				add_child(moving_tabc);
+				add_child(moving_tabc, false, INTERNAL_MODE_FRONT);
 				if (hover_now < 0) {
 				if (hover_now < 0) {
 					hover_now = get_tab_count() - 1;
 					hover_now = get_tab_count() - 1;
 				}
 				}

+ 8 - 8
scene/gui/text_edit.cpp

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

+ 5 - 6
scene/gui/tree.cpp

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

+ 108 - 27
scene/main/node.cpp

@@ -48,6 +48,7 @@
 #include <stdint.h>
 #include <stdint.h>
 
 
 VARIANT_ENUM_CAST(Node::ProcessMode);
 VARIANT_ENUM_CAST(Node::ProcessMode);
+VARIANT_ENUM_CAST(Node::InternalMode);
 
 
 int Node::orphan_node_count = 0;
 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) {
 void Node::move_child(Node *p_child, int p_pos) {
 	ERR_FAIL_NULL(p_child);
 	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.");
 	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).");
 	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
 	// Specifying one place beyond the end
 	// means the same as moving to the last position
 	// 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) {
 	if (p_child->data.pos == p_pos) {
@@ -339,7 +366,14 @@ void Node::raise() {
 		return;
 		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) {
 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();
 	p_child->data.pos = data.children.size();
 	data.children.push_back(p_child);
 	data.children.push_back(p_child);
 	p_child->data.parent = this;
 	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);
 	p_child->notification(NOTIFICATION_PARENTED);
 
 
 	if (data.tree) {
 	if (data.tree) {
@@ -1070,7 +1108,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
 	add_child_notify(p_child);
 	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_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 == 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
 	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
 #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.");
 	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);
 	_validate_child_name(p_child, p_legible_unique_name);
-
 	_add_child_nocheck(p_child, p_child->data.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) {
 void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
 	ERR_FAIL_NULL(p_sibling);
 	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(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.");
 	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() {
 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_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 );
 	//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);
 	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 {
 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--;
 	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;
 	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;
 	TypedArray<Node> arr;
-	int cc = get_child_count();
+	int cc = get_child_count(p_include_internal);
 	arr.resize(cc);
 	arr.resize(cc);
 	for (int i = 0; i < cc; i++) {
 	for (int i = 0; i < cc; i++) {
-		arr[i] = get_child(i);
+		arr[i] = get_child(i, p_include_internal);
 	}
 	}
 
 
 	return arr;
 	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("set_name", "name"), &Node::set_name);
 	ClassDB::bind_method(D_METHOD("get_name"), &Node::get_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("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("has_node", "path"), &Node::has_node);
 	ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_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);
 	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("set_owner", "owner"), &Node::set_owner);
 	ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_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("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"), &Node::print_tree);
 	ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
 	ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
 	ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
 	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_SCRIPTS);
 	BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
 	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("ready"));
 	ADD_SIGNAL(MethodInfo("renamed"));
 	ADD_SIGNAL(MethodInfo("renamed"));
 	ADD_SIGNAL(MethodInfo("tree_entered"));
 	ADD_SIGNAL(MethodInfo("tree_entered"));

+ 17 - 5
scene/main/node.h

@@ -73,6 +73,12 @@ public:
 		NAME_CASING_SNAKE_CASE
 		NAME_CASING_SNAKE_CASE
 	};
 	};
 
 
+	enum InternalMode {
+		INTERNAL_MODE_DISABLED,
+		INTERNAL_MODE_FRONT,
+		INTERNAL_MODE_BACK,
+	};
+
 	struct Comparator {
 	struct Comparator {
 		bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
 		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 *parent = nullptr;
 		Node *owner = nullptr;
 		Node *owner = nullptr;
 		Vector<Node *> children;
 		Vector<Node *> children;
+		int internal_children_front = 0;
+		int internal_children_back = 0;
 		int pos = -1;
 		int pos = -1;
 		int depth = -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.
 		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;
 	void _duplicate_signals(const Node *p_original, Node *p_copy) const;
 	Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) 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;
 	Array _get_groups() const;
 
 
 	Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
 	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);
 	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;
 	friend class SceneTree;
 
 
 	void _set_tree(SceneTree *p_tree);
 	void _set_tree(SceneTree *p_tree);
@@ -284,12 +295,12 @@ public:
 	StringName get_name() const;
 	StringName get_name() const;
 	void set_name(const String &p_name);
 	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 add_sibling(Node *p_sibling, bool p_legible_unique_name = false);
 	void remove_child(Node *p_child);
 	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;
 	bool has_node(const NodePath &p_path) const;
 	Node *get_node(const NodePath &p_path) const;
 	Node *get_node(const NodePath &p_path) const;
 	Node *get_node_or_null(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;
 	int get_persistent_group_count() const;
 
 
 	void move_child(Node *p_child, int p_pos);
 	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 raise();
 
 
 	void set_owner(Node *p_owner);
 	void set_owner(Node *p_owner);
@@ -334,7 +346,7 @@ public:
 	void get_owned_by(Node *p_by, List<Node *> *p_owned);
 	void get_owned_by(Node *p_by, List<Node *> *p_owned);
 
 
 	void remove_and_skip();
 	void remove_and_skip();
-	int get_index() const;
+	int get_index(bool p_include_internal = true) const;
 
 
 	Ref<Tween> create_tween();
 	Ref<Tween> create_tween();