Anchors and Pivot
How elements snap to their parents — UMG-style anchoring, applied to s&box.
Table of contents
- The 12 anchors
- How anchor + X/Y/W/H produce a rect
- Stretch — X/Y/W/H are margins
- Pivot
- Reparenting and anchor preservation
- When to use each anchor
- What happens when the parent resizes
- See also
The 12 anchors
┌─────────────────┐
│ TL TC TR │
│ │
│ ML MC MR │
│ │
│ BL BC BR │
└─────────────────┘
↓
Stretch / StretchHorizontal / StretchVertical
9 single-point anchors + 3 stretch variants.
| Anchor | Reference point on parent | X means | Y means |
|---|---|---|---|
| TopLeft | (0, 0) | offset from left edge → right | offset from top edge → down |
| TopCenter | (0.5w, 0) | offset from horizontal center | offset from top edge → down |
| TopRight | (w, 0) | offset from right edge → left | offset from top edge → down |
| MiddleLeft | (0, 0.5h) | offset from left → right | offset from vertical center |
| MiddleCenter | (0.5w, 0.5h) | offset from horizontal center | offset from vertical center |
| MiddleRight | (w, 0.5h) | offset from right → left | offset from vertical center |
| BottomLeft | (0, h) | offset from left → right | offset from bottom → up |
| BottomCenter | (0.5w, h) | offset from horizontal center | offset from bottom → up |
| BottomRight | (w, h) | offset from right → left | offset from bottom → up |
| Stretch | full parent rect | X = left margin | Y = top margin (W = right margin, H = bottom margin) |
| StretchHorizontal | full width, vertical center | X = left margin, Y = vertical offset | (W = right margin, H = element height) |
| StretchVertical | full height, horizontal center | X = horizontal offset, Y = top margin | (W = element width, H = bottom margin) |
How anchor + X/Y/W/H produce a rect
For non-Stretch anchors:
finalX = parent.X + refX + signX × ownX − pivotX × ownW
finalY = parent.Y + refY + signY × ownY − pivotY × ownH
Where refX/Y is the anchor reference point on the parent, signX/Y is +1 or -1 depending on left-anchored vs right-anchored, and pivotX/Y is the element’s own pivot (0..1).
The math is implemented in SuiLayoutSolver.ResolveAbsoluteRect (forward) and SuiLayoutSolver.RectToLayoutValues (inverse, used by drag-to-move).
Stretch — X/Y/W/H are margins
Stretch is special: X/Y/W/H don’t position the element. They are margins that shrink the element inward from the parent’s edges.
Stretch with X=8 Y=8 W=8 H=8:
┌─────────────────────────────────┐
│ 8 │
│ ┌───────────────────────┐ │
│ 8 │ │ 8 │ ← element fills parent minus 8px each side
│ │ element │ │
│ └───────────────────────┘ │
│ 8 │
└─────────────────────────────────┘
Generated SCSS:
.stretched {
position: absolute;
left: 8px;
top: 8px;
right: 8px;
bottom: 8px;
/* no width/height — left/right and top/bottom together size it */
}
StretchHorizontal and StretchVertical mix margin-style on one axis with normal X/Y/W/H on the other.
Pivot
Pivot is a 0..1 fraction indicating which point inside the element is “the pivot.”
(0, 0)— top-left corner. Default for TopLeft anchor.(0.5, 0.5)— center. Default when picking MiddleCenter anchor.(1, 1)— bottom-right corner.
Affects:
- The meaning of Position (X/Y offsets the pivot to that position relative to the anchor, not the top-left corner).
- Rotation (V2) — element rotates around its pivot point.
For most cases, pivot is set automatically when you pick an anchor (the anchor’s natural pivot). Manual tweaks are rare.
Reparenting and anchor preservation
When you change an element’s anchor in the Details panel, the editor uses SuiSetAnchorCommand which:
- Snapshots the element’s current rect (logical coords)
- Updates
Layout.Anchorto the new value - Re-computes X/Y/W/H so the element occupies the same on-screen rect under the new anchor
So changing anchor doesn’t make the element jump — it just changes the reference point. Useful for “I want this Top-Left element to anchor to the right edge as the window resizes” workflows.
When to use each anchor
| Goal | Anchor |
|---|---|
| Hud overlay at top-left (HP bar) | TopLeft |
| Notification banner top-center | TopCenter |
| Minimap at top-right | TopRight |
| Centered modal | MiddleCenter |
| Status text at bottom-left | BottomLeft |
| Hotbar at bottom-center | BottomCenter |
| Inventory bag at bottom-right | BottomRight |
| Full-screen backdrop (dim) | Stretch |
| Top stripe / header bar | StretchHorizontal |
| Side rail / sidebar | StretchVertical |
What happens when the parent resizes
This is one of the most common points of confusion, especially for users coming from UMG. The short version:
Single-point anchors (TopLeft, MiddleCenter, BottomRight, etc.) — child stays the same size, repositions to the new anchor point.
Stretch anchors (Stretch, StretchHorizontal, StretchVertical) — child resizes with the parent, maintaining its margins.
Worked example
Parent panel sized 400×300, with a child Button at:
- Width: 100, Height: 40
- Anchor: MiddleCenter
- X: 0, Y: 0
The Button renders at the parent’s center, 100×40.
Now you resize the parent to 800×600.
What happens: the Button is now at the new center (which moved), but still 100×40. It does NOT grow to fill more space proportionally.
Before resize (parent 400×300): After resize (parent 800×600):
┌──────────────────────┐ ┌──────────────────────────────────────┐
│ │ │ │
│ ┌──────┐ │ │ │
│ │ BTN │ 100×40 │ │ │
│ └──────┘ │ │ ┌──────┐ │
│ │ │ │ BTN │ still 100×40 │
└──────────────────────┘ │ └──────┘ │
│ │
│ │
└──────────────────────────────────────┘
This matches UMG default behavior exactly.
How to make the child grow with the parent
| Want | Anchor | Behavior |
|---|---|---|
| Child fills parent minus margins | Stretch with X/Y/W/H = desired margins | Width and height both scale |
| Child stretches horizontally, fixed height | StretchHorizontal | Width scales, height stays |
| Child stretches vertically, fixed width | StretchVertical | Height scales, width stays |
| Multiple children share space along an axis | Flex layout on the parent (HorizontalBox / VerticalBox / Grid) | Children distribute per justify-content + per-child flex-grow if set |
Why we don’t have a “scale everything proportionally” mode
This is a feature gap on purpose. UMG has the ScaleBox widget for this case, and we don’t ship an equivalent in V1.0. It’s on the V1.5+ roadmap as a candidate ScaleBox-equivalent element type or a AutoScaleChildren flag on Panel.
If you need proportional scaling today, the workaround is:
- Use a Flex container (HorizontalBox / VerticalBox / Grid) — children scale within the flex layout
- Use Stretch anchor with margins for absolutely-positioned items
- Use the canvas scale mode (
ScreenHeight1080on the root document) for whole-UI scaling based on resolution
Why this design
Anchor systems describe where an element lives relative to its parent. Sizing is a separate axis from positioning. Conflating them — “the anchor also scales me” — is what causes the most common UMG layout bugs: UI elements that grow into other elements on resolution change because their anchor “owned” their size unexpectedly.
By keeping size and anchor orthogonal, the designer explicitly opts into stretching (via Stretch anchors or Flex layout) when they want it. When they don’t, the element stays the size they designed it.
See also
- Layout modes — Absolute vs Flex
- Details panel · Transform