Repository: godotengine/godot (language: cpp)

Issue:
`GraphNode` doesn't shrink automatically
### Tested versions

v4.4.dev.custom_build [abf47965f]

### System information

Debian Linux X11

### Issue description

`GraphNode` doesn't shrink automatically when
- `size_flags_vertical = SIZE_SHRINK_CENTER`
- the size of child Control nodes decreases

I would expect, that a container node with `SIZE_SHRINK_CENTER` automatically shrinks, when the size of their child nodes decreases.


### Steps to reproduce

1. Load and run MRP
2. Drag the top `HSlider` to the right to increase the vertical size of the labels.
3. Drag the top `HSlider` to the left to decrease the vertical size of the labels.

After 3:
- The label inside the GraphNode became smaller, but the GraphNode didn't adjust its size and appears too big
- For comparison there is a working example on the right side of the scene with a HBoxContainer, that adjusts its vertical size automatically (indicated by the red color)


### Minimal reproduction project (MRP)

[bug-graph-edit-resize.zip](https://github.com/user-attachments/files/18172298/bug-graph-edit-resize.zip)

`GraphNode` doesn't shrink automatically
### Tested versions

v4.4.dev.custom_build [abf47965f]

### System information

Debian Linux X11

### Issue description

`GraphNode` doesn't shrink automatically when
- `size_flags_vertical = SIZE_SHRINK_CENTER`
- the size of child Control nodes decreases

I would expect, that a container node with `SIZE_SHRINK_CENTER` automatically shrinks, when the size of their child nodes decreases.


### Steps to reproduce

1. Load and run MRP
2. Drag the top `HSlider` to the right to increase the vertical size of the labels.
3. Drag the top `HSlider` to the left to decrease the vertical size of the labels.

After 3:
- The label inside the GraphNode became smaller, but the GraphNode didn't adjust its size and appears too big
- For comparison there is a working example on the right side of the scene with a HBoxContainer, that adjusts its vertical size automatically (indicated by the red color)


### Minimal reproduction project (MRP)

[bug-graph-edit-resize.zip](https://github.com/user-attachments/files/18172298/bug-graph-edit-resize.zip)



Discussion / hints:
This has been bugging me as well, and it seems like its a problem among a lot of the UI/Control/Container nodes
Looks like the same as #92922
This has been bugging me as well, and it seems like its a problem among a lot of the UI/Control/Container nodes
Looks like the same as #92922

Fix patch:
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 03d8e51285f8..2bc4d3a5e5ef 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -867,6 +867,13 @@ void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (
 	r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
 }
 
+void Control::_compute_edge_positions(Rect2 p_rect, real_t (&r_edge_positions)[4]) {
+	for (int i = 0; i < 4; i++) {
+		real_t area = p_rect.size[i & 1];
+		r_edge_positions[i] = data.offset[i] + (data.anchor[i] * area);
+	}
+}
+
 /// Presets and layout modes.
 
 void Control::_set_layout_mode(LayoutMode p_mode) {
@@ -1392,10 +1399,13 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) {
 	}
 #endif // TOOLS_ENABLED
 
+	real_t edge_pos[4];
+	_compute_edge_positions(get_parent_anchorable_rect(), edge_pos);
+	Size2 offset_size(edge_pos[2] - edge_pos[0], edge_pos[3] - edge_pos[1]);
 	if (p_keep_offsets) {
-		_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
+		_compute_anchors(Rect2(p_point, offset_size), data.offset, data.anchor);
 	} else {
-		_compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
+		_compute_offsets(Rect2(p_point, offset_size), data.anchor, data.offset);
 	}
 	_size_changed();
 }
@@ -1682,14 +1692,8 @@ Size2 Control::get_combined_minimum_size() const {
 
 void Control::_size_changed() {
 	Rect2 parent_rect = get_parent_anchorable_rect();
-
 	real_t edge_pos[4];
-
-	for (int i = 0; i < 4; i++) {
-		real_t area = parent_rect.size[i & 1];
-		edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
-	}
-
+	_compute_edge_positions(parent_rect, edge_pos);
 	Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
 	Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
 
diff --git a/scene/gui/control.h b/scene/gui/control.h
index b2001fdd259f..349fb78f58b3 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -300,6 +300,7 @@ class Control : public CanvasItem {
 
 	void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
 	void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
+	void _compute_edge_positions(Rect2 p_rect, real_t (&r_edge_positions)[4]);
 
 	void _set_layout_mode(LayoutMode p_mode);
 	void _update_layout_mode();


Test patch:
diff --git a/tests/scene/test_control.h b/tests/scene/test_control.h
index e9d38c97e759..68a4f0015123 100644
--- a/tests/scene/test_control.h
+++ b/tests/scene/test_control.h
@@ -990,6 +990,38 @@ TEST_CASE("[SceneTree][Control] Anchoring") {
 	memdelete(test_control);
 }
 
+TEST_CASE("[SceneTree][Control] Set position does not cause size side-effects") {
+	Control *test_control = memnew(Control);
+	test_control->set_size(Size2(1, 1));
+	test_control->set_custom_minimum_size(Size2(2, 2));
+	Window *root = SceneTree::get_singleton()->get_root();
+	root->add_child(test_control);
+
+	SUBCASE("Shrinks after setting position and smaller custom minimum size (without keeping offsets)") {
+		test_control->set_position(Point2(10, 10), false);
+		SceneTree::get_singleton()->process(0);
+
+		test_control->set_custom_minimum_size(Size2(0, 0));
+		SceneTree::get_singleton()->process(0);
+		CHECK_MESSAGE(
+				test_control->get_size().is_equal_approx(Vector2(1, 1)),
+				"Should shrink to original size after setting a smaller custom minimum size.");
+	}
+
+	SUBCASE("Shrinks after setting position and smaller custom minimum size (while keeping offsets)") {
+		test_control->set_position(Point2(10, 10), true);
+		SceneTree::get_singleton()->process(0);
+
+		test_control->set_custom_minimum_size(Size2(0, 0));
+		SceneTree::get_singleton()->process(0);
+		CHECK_MESSAGE(
+				test_control->get_size().is_equal_approx(Vector2(1, 1)),
+				"Should shrink to original size after setting a smaller custom minimum size.");
+	}
+
+	memdelete(test_control);
+}
+
 TEST_CASE("[SceneTree][Control] Custom minimum size") {
 	Control *test_control = memnew(Control);
 	test_control->set_custom_minimum_size(Size2(4, 2));