2
0
Эх сурвалжийг харах

Add support for internal nodes

kobewi 4 жил өмнө
parent
commit
a913ae8d56

+ 21 - 0
doc/classes/Node.xml

@@ -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>

+ 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);
 	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);
 	}

+ 8 - 8
scene/gui/color_picker.cpp

@@ -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));

+ 3 - 3
scene/gui/dialogs.cpp

@@ -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);

+ 5 - 6
scene/gui/file_dialog.cpp

@@ -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();

+ 1 - 1
scene/gui/gradient_edit.cpp

@@ -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) {

+ 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);
-	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

+ 1 - 1
scene/gui/item_list.cpp

@@ -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));
 

+ 2 - 2
scene/gui/line_edit.cpp

@@ -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);

+ 1 - 1
scene/gui/menu_button.cpp

@@ -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));
 }

+ 1 - 1
scene/gui/option_button.cpp

@@ -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));

+ 1 - 1
scene/gui/popup.cpp

@@ -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);
 }

+ 4 - 4
scene/gui/popup_menu.cpp

@@ -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() {

+ 1 - 1
scene/gui/rich_text_label.cpp

@@ -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);

+ 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_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");

+ 2 - 2
scene/gui/spin_box.cpp

@@ -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);
 }

+ 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()) {
 				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;
 				}

+ 8 - 8
scene/gui/text_edit.cpp

@@ -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));

+ 5 - 6
scene/gui/tree.cpp

@@ -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));

+ 108 - 27
scene/main/node.cpp

@@ -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"));

+ 17 - 5
scene/main/node.h

@@ -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();