Skip to content

Commit a1c1d0f

Browse files
Anchors now adjust to size & scale changes
1 parent d890d7d commit a1c1d0f

2 files changed

Lines changed: 125 additions & 31 deletions

File tree

‎scene/gui/control.cpp‎

Lines changed: 117 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

13641418
void 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();

‎scene/gui/control.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ class Control : public CanvasItem {
230230
bool block_minimum_size_adjust = false;
231231

232232
bool size_warning = true;
233+
int anchor_preset_margin = 0;
234+
bool anchor_preset_active = false;
233235
/// @}
234236
/// Container Sizing
235237
/// @{
@@ -335,6 +337,8 @@ class Control : public CanvasItem {
335337
void _set_anchors_layout_preset(int p_preset);
336338
int _get_anchors_layout_preset() const;
337339

340+
void _compute_preset_offsets(LayoutPreset p_preset, const Size2 &p_size, int p_margin = 0);
341+
338342
void _update_minimum_size_cache() const;
339343
void _update_minimum_size();
340344
void _size_changed();
@@ -512,6 +516,10 @@ class Control : public CanvasItem {
512516
GrowDirection get_v_grow_direction() const;
513517

514518
void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
519+
/// Sets the control's offsets to align with a layout preset.
520+
/// @param p_preset: The layout preset from LayoutPreset enum.
521+
/// @param p_resize_mode: How to determine the resulting size (PRESET_MODE_MINSIZE, PRESET_MODE_KEEP_SIZE, etc.).
522+
/// @param p_margin: Gap between the control and parent edges.
515523
void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
516524
void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
517525
void set_grow_direction_preset(LayoutPreset p_preset);

0 commit comments

Comments
 (0)