@@ -603,6 +603,10 @@ void Control::_validate_property(PropertyInfo &p_property) const {
603603 // Use the layout mode to display or hide advanced anchoring properties.
604604 LayoutMode _layout = _get_layout_mode ();
605605 bool use_anchors = (_layout == LayoutMode::LAYOUT_MODE_ANCHORS || _layout == LayoutMode::LAYOUT_MODE_UNCONTROLLED );
606+ // If using anchors, the position is, well...anchored based on the layout, so manipulating the position doesn't make sense
607+ if (use_anchors && p_property.name == " position" ) {
608+ p_property.usage |= PROPERTY_USAGE_READ_ONLY ;
609+ }
606610 if (!use_anchors && p_property.name == " anchors_preset" ) {
607611 p_property.usage ^= PROPERTY_USAGE_EDITOR ;
608612 }
@@ -925,6 +929,7 @@ void Control::_set_layout_mode(LayoutMode p_mode) {
925929 data.stored_use_custom_anchors = false ;
926930 set_anchors_and_offsets_preset (LayoutPreset::PRESET_TOP_LEFT , LayoutPresetMode::PRESET_MODE_KEEP_SIZE );
927931 set_grow_direction_preset (LayoutPreset::PRESET_TOP_LEFT );
932+ data.anchor_preset_active = false ;
928933 }
929934
930935 if (list_changed) {
@@ -1225,12 +1230,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
12251230 }
12261231}
12271232
1228- void Control::set_offsets_preset (LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin ) {
1233+ void Control::set_offsets_preset (LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_preset_margin ) {
12291234 ERR_MAIN_THREAD_GUARD ;
12301235 ERR_FAIL_INDEX ((int )p_preset, 16 );
12311236 ERR_FAIL_INDEX ((int )p_resize_mode, 4 );
12321237
1233- // Calculate the size if the node is not resized
1238+ data.anchor_preset_active = true ;
1239+ data.anchor_preset_margin = p_preset_margin;
1240+
12341241 Size2 min_size = get_minimum_size ();
12351242 Size2 new_size = get_size ();
12361243 if (p_resize_mode == PRESET_MODE_MINSIZE || p_resize_mode == PRESET_MODE_KEEP_HEIGHT ) {
@@ -1240,37 +1247,59 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
12401247 new_size.y = min_size.y ;
12411248 }
12421249
1250+ _compute_preset_offsets (p_preset, new_size, p_preset_margin);
1251+ _size_changed ();
1252+ }
1253+
1254+ void Control::_compute_preset_offsets (LayoutPreset p_preset, const Size2 &p_size, int p_preset_margin) {
12431255 Rect2 parent_rect = get_parent_anchorable_rect ();
12441256
12451257 real_t x = parent_rect.size .x ;
12461258 if (is_layout_rtl ()) {
1247- x = parent_rect.size .x - x - new_size .x ;
1259+ x = parent_rect.size .x - x - p_size .x ;
12481260 }
1249- // Left
1261+
1262+ const real_t pivot_corr_x = data.pivot_offset .x * (data.scale .x - 1 .0f );
1263+ const real_t pivot_corr_y = data.pivot_offset .y * (data.scale .y - 1 .0f );
1264+ const real_t scaled_width = p_size.x * data.scale .x ;
1265+ const real_t scaled_height = p_size.y * data.scale .y ;
1266+
1267+ // Left
12501268 switch (p_preset) {
12511269 case PRESET_TOP_LEFT :
12521270 case PRESET_BOTTOM_LEFT :
12531271 case PRESET_CENTER_LEFT :
1254- case PRESET_TOP_WIDE :
1255- case PRESET_BOTTOM_WIDE :
12561272 case PRESET_LEFT_WIDE :
1257- case PRESET_HCENTER_WIDE :
1258- case PRESET_FULL_RECT :
1259- data.offset [0 ] = x * (0.0 - data.anchor [0 ]) + p_margin + parent_rect.position .x ;
1273+ // Fixed-size axis: visual left edge at p_margin.
1274+ data.offset [0 ] = x * (0.0 - data.anchor [0 ]) + pivot_corr_x + p_preset_margin + parent_rect.position .x ;
1275+ break ;
1276+
1277+ case PRESET_RIGHT_WIDE :
1278+ // Fixed-size axis: visual left edge at parent_right - scaled_width - margin.
1279+ data.offset [0 ] = x * (1.0 - data.anchor [0 ]) + pivot_corr_x - scaled_width - p_preset_margin + parent_rect.position .x ;
12601280 break ;
12611281
12621282 case PRESET_CENTER_TOP :
12631283 case PRESET_CENTER_BOTTOM :
12641284 case PRESET_CENTER :
12651285 case PRESET_VCENTER_WIDE :
1266- data.offset [0 ] = x * (0.5 - data.anchor [0 ]) - new_size.x / 2 + parent_rect.position .x ;
1286+ // Fixed-size axis: visual center at parent horizontal center.
1287+ data.offset [0 ] = x * (0.5 - data.anchor [0 ]) + pivot_corr_x - scaled_width / 2 .0f + parent_rect.position .x ;
12671288 break ;
12681289
12691290 case PRESET_TOP_RIGHT :
12701291 case PRESET_BOTTOM_RIGHT :
12711292 case PRESET_CENTER_RIGHT :
1272- case PRESET_RIGHT_WIDE :
1273- data.offset [0 ] = x * (1.0 - data.anchor [0 ]) - new_size.x - p_margin + parent_rect.position .x ;
1293+ // Fixed-size axis: visual right edge at parent_right - margin.
1294+ data.offset [0 ] = x * (1.0 - data.anchor [0 ]) + pivot_corr_x - scaled_width - p_preset_margin + parent_rect.position .x ;
1295+ break ;
1296+
1297+ case PRESET_TOP_WIDE :
1298+ case PRESET_BOTTOM_WIDE :
1299+ case PRESET_HCENTER_WIDE :
1300+ case PRESET_FULL_RECT :
1301+ // Stretching axis: visual left edge at margin, accounting for pivot.
1302+ data.offset [0 ] = x * (0.0 - data.anchor [0 ]) + pivot_corr_x + p_preset_margin + parent_rect.position .x ;
12741303 break ;
12751304 }
12761305
@@ -1279,26 +1308,37 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
12791308 case PRESET_TOP_LEFT :
12801309 case PRESET_TOP_RIGHT :
12811310 case PRESET_CENTER_TOP :
1282- case PRESET_LEFT_WIDE :
1283- case PRESET_RIGHT_WIDE :
12841311 case PRESET_TOP_WIDE :
1285- case PRESET_VCENTER_WIDE :
1286- case PRESET_FULL_RECT :
1287- data.offset [1 ] = parent_rect.size .y * (0.0 - data.anchor [1 ]) + p_margin + parent_rect.position .y ;
1312+ // Fixed-size axis: visual top edge at p_margin.
1313+ data.offset [1 ] = parent_rect.size .y * (0.0 - data.anchor [1 ]) + pivot_corr_y + p_preset_margin + parent_rect.position .y ;
1314+ break ;
1315+
1316+ case PRESET_BOTTOM_WIDE :
1317+ // Fixed-size axis: visual top edge at parent_bottom - scaled_height - margin.
1318+ data.offset [1 ] = parent_rect.size .y * (1.0 - data.anchor [1 ]) + pivot_corr_y - scaled_height - p_preset_margin + parent_rect.position .y ;
12881319 break ;
12891320
12901321 case PRESET_CENTER_LEFT :
12911322 case PRESET_CENTER_RIGHT :
12921323 case PRESET_CENTER :
12931324 case PRESET_HCENTER_WIDE :
1294- data.offset [1 ] = parent_rect.size .y * (0.5 - data.anchor [1 ]) - new_size.y / 2 + parent_rect.position .y ;
1325+ // Fixed-size axis: visual center at parent vertical center.
1326+ data.offset [1 ] = parent_rect.size .y * (0.5 - data.anchor [1 ]) + pivot_corr_y - scaled_height / 2 .0f + parent_rect.position .y ;
12951327 break ;
12961328
12971329 case PRESET_BOTTOM_LEFT :
12981330 case PRESET_BOTTOM_RIGHT :
12991331 case PRESET_CENTER_BOTTOM :
1300- case PRESET_BOTTOM_WIDE :
1301- data.offset [1 ] = parent_rect.size .y * (1.0 - data.anchor [1 ]) - new_size.y - p_margin + parent_rect.position .y ;
1332+ // Fixed-size axis: visual bottom edge at parent_bottom - margin.
1333+ data.offset [1 ] = parent_rect.size .y * (1.0 - data.anchor [1 ]) + pivot_corr_y - scaled_height - p_preset_margin + parent_rect.position .y ;
1334+ break ;
1335+
1336+ case PRESET_LEFT_WIDE :
1337+ case PRESET_RIGHT_WIDE :
1338+ case PRESET_VCENTER_WIDE :
1339+ case PRESET_FULL_RECT :
1340+ // Stretching axis: visual top edge at margin, accounting for pivot.
1341+ data.offset [1 ] = parent_rect.size .y * (0.0 - data.anchor [1 ]) + pivot_corr_y + p_preset_margin + parent_rect.position .y ;
13021342 break ;
13031343 }
13041344
@@ -1308,25 +1348,33 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
13081348 case PRESET_BOTTOM_LEFT :
13091349 case PRESET_CENTER_LEFT :
13101350 case PRESET_LEFT_WIDE :
1311- data.offset [2 ] = x * (0.0 - data.anchor [2 ]) + new_size.x + p_margin + parent_rect.position .x ;
1351+ // Fixed-size axis: right = logical_left + p_size.x.
1352+ data.offset [2 ] = x * (0.0 - data.anchor [2 ]) + pivot_corr_x + p_size.x + p_preset_margin + parent_rect.position .x ;
13121353 break ;
13131354
13141355 case PRESET_CENTER_TOP :
13151356 case PRESET_CENTER_BOTTOM :
13161357 case PRESET_CENTER :
13171358 case PRESET_VCENTER_WIDE :
1318- data.offset [2 ] = x * (0.5 - data.anchor [2 ]) + new_size.x / 2 + parent_rect.position .x ;
1359+ // Fixed-size axis: centered.
1360+ data.offset [2 ] = x * (0.5 - data.anchor [2 ]) + pivot_corr_x + p_size.x - scaled_width / 2 .0f + parent_rect.position .x ;
13191361 break ;
13201362
13211363 case PRESET_TOP_RIGHT :
13221364 case PRESET_BOTTOM_RIGHT :
13231365 case PRESET_CENTER_RIGHT :
1324- case PRESET_TOP_WIDE :
13251366 case PRESET_RIGHT_WIDE :
1367+ // Fixed-size axis: right edge at parent_right - margin.
1368+ data.offset [2 ] = x * (1.0 - data.anchor [2 ]) + pivot_corr_x + p_size.x * (1 .0f - data.scale .x ) - p_preset_margin + parent_rect.position .x ;
1369+ break ;
1370+
1371+ case PRESET_TOP_WIDE :
13261372 case PRESET_BOTTOM_WIDE :
13271373 case PRESET_HCENTER_WIDE :
13281374 case PRESET_FULL_RECT :
1329- data.offset [2 ] = x * (1.0 - data.anchor [2 ]) - p_margin + parent_rect.position .x ;
1375+ // Stretching axis: visual right edge at parent_right - margin.
1376+ // Divide by scale so visual_width = parent_width - 2*margin regardless of scale.
1377+ data.offset [2 ] = pivot_corr_x + p_preset_margin + (x - 2 .0f * p_preset_margin) / data.scale .x - x + parent_rect.position .x ;
13301378 break ;
13311379 }
13321380
@@ -1336,29 +1384,35 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
13361384 case PRESET_TOP_RIGHT :
13371385 case PRESET_CENTER_TOP :
13381386 case PRESET_TOP_WIDE :
1339- data.offset [3 ] = parent_rect.size .y * (0.0 - data.anchor [3 ]) + new_size.y + p_margin + parent_rect.position .y ;
1387+ // Fixed-size axis: bottom = logical_top + p_size.y.
1388+ data.offset [3 ] = parent_rect.size .y * (0.0 - data.anchor [3 ]) + pivot_corr_y + p_size.y + p_preset_margin + parent_rect.position .y ;
13401389 break ;
13411390
13421391 case PRESET_CENTER_LEFT :
13431392 case PRESET_CENTER_RIGHT :
13441393 case PRESET_CENTER :
13451394 case PRESET_HCENTER_WIDE :
1346- data.offset [3 ] = parent_rect.size .y * (0.5 - data.anchor [3 ]) + new_size.y / 2 + parent_rect.position .y ;
1395+ // Fixed-size axis: centered.
1396+ data.offset [3 ] = parent_rect.size .y * (0.5 - data.anchor [3 ]) + pivot_corr_y + p_size.y - scaled_height / 2 .0f + parent_rect.position .y ;
13471397 break ;
13481398
13491399 case PRESET_BOTTOM_LEFT :
13501400 case PRESET_BOTTOM_RIGHT :
13511401 case PRESET_CENTER_BOTTOM :
1402+ case PRESET_BOTTOM_WIDE :
1403+ // Fixed-size axis: bottom edge at parent_bottom - margin.
1404+ data.offset [3 ] = parent_rect.size .y * (1.0 - data.anchor [3 ]) + pivot_corr_y + p_size.y * (1 .0f - data.scale .y ) - p_preset_margin + parent_rect.position .y ;
1405+ break ;
1406+
13521407 case PRESET_LEFT_WIDE :
13531408 case PRESET_RIGHT_WIDE :
1354- case PRESET_BOTTOM_WIDE :
13551409 case PRESET_VCENTER_WIDE :
13561410 case PRESET_FULL_RECT :
1357- data.offset [3 ] = parent_rect.size .y * (1.0 - data.anchor [3 ]) - p_margin + parent_rect.position .y ;
1411+ // Stretching axis: visual bottom edge at parent_bottom - margin.
1412+ // Divide by scale so visual_height = parent_height - 2*margin regardless of scale.
1413+ data.offset [3 ] = pivot_corr_y + p_preset_margin + (parent_rect.size .y - 2 .0f * p_preset_margin) / data.scale .y - parent_rect.size .y + parent_rect.position .y ;
13581414 break ;
13591415 }
1360-
1361- _size_changed ();
13621416}
13631417
13641418void Control::set_anchors_and_offsets_preset (LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) {
@@ -1507,10 +1561,22 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
15071561 }
15081562#endif // TOOLS_ENABLED
15091563
1564+ // For corner/center anchor presets, re-align to the preset using the new size.
1565+ // Wide/full presets are driven by the parent rect, not by explicit size; skip them.
15101566 if (p_keep_offsets) {
15111567 _compute_anchors (Rect2 (data.pos_cache , new_size), data.offset , data.anchor );
15121568 } else {
1513- _compute_offsets (Rect2 (data.pos_cache , new_size), data.anchor , data.offset );
1569+ bool preset_applied = false ;
1570+ if (is_inside_tree () && data.anchor_preset_active ) {
1571+ int current_preset = _get_anchors_layout_preset ();
1572+ if (current_preset != -1 ) {
1573+ _compute_preset_offsets ((LayoutPreset)current_preset, new_size, data.anchor_preset_margin );
1574+ preset_applied = true ;
1575+ }
1576+ }
1577+ if (!preset_applied) {
1578+ _compute_offsets (Rect2 (data.pos_cache , new_size), data.anchor , data.offset );
1579+ }
15141580 }
15151581 _size_changed ();
15161582}
@@ -1576,6 +1642,16 @@ void Control::set_scale(const Vector2 &p_scale) {
15761642 if (data.scale .y == 0 ) {
15771643 data.scale .y = CMP_EPSILON ;
15781644 }
1645+
1646+ // Re-apply the anchor preset offsets so that visual alignment (center, right-edge, etc.) stays correct for the new scale value.
1647+ if (is_inside_tree () && data.anchor_preset_active ) {
1648+ int current_preset = _get_anchors_layout_preset ();
1649+ if (current_preset != -1 ) {
1650+ _compute_preset_offsets ((LayoutPreset)current_preset, get_size (), data.anchor_preset_margin );
1651+ _size_changed ();
1652+ }
1653+ }
1654+
15791655 queue_redraw ();
15801656 _notify_transform ();
15811657 queue_accessibility_update ();
@@ -1620,6 +1696,16 @@ void Control::set_pivot_offset(const Vector2 &p_pivot) {
16201696 }
16211697
16221698 data.pivot_offset = p_pivot;
1699+
1700+ // Re-apply the anchor preset offsets so that visual alignment (center, right-edge, etc.) stays correct for the new scale value.
1701+ if (is_inside_tree () && data.anchor_preset_active ) {
1702+ int current_preset = _get_anchors_layout_preset ();
1703+ if (current_preset != -1 ) {
1704+ _compute_preset_offsets ((LayoutPreset)current_preset, get_size (), data.anchor_preset_margin );
1705+ _size_changed ();
1706+ }
1707+ }
1708+
16231709 queue_redraw ();
16241710 _notify_transform ();
16251711 queue_accessibility_update ();
0 commit comments