Layout
Layout decides where every element sits and how big it is. Each frame where something changed, IcyUI asks every element how big it wants to be, then gives it a rectangle to fill. Panels decide how their children share their space; each child's size, margins and alignment decide where it sits in the share it gets.
Measure, then arrange
Layout runs in two passes, from the canvas down:
- Measure: each element reports its desired size, from its content (text, an image, its children) and its own size properties.
- Arrange: each parent gives each child a rectangle. The child places itself in it according to its margin and alignment, and arranges its own children.
Changing a property that affects size (text, font size, margins, children) marks the element for a new measure; the next frame lays it out again. You don't call layout yourself.
Size
| Property | Meaning |
|---|---|
| Width / Height | A fixed size. Unset by default (NaN), meaning "size to content, or to the space given". |
| MinWidth / MaxWidth (and heights) | Limits on the size the element ends up with. |
| Margin | Space outside the element, between it and its neighbours or parent. |
| Padding | Space inside the element, between its border and its content. |
A Thickness is written in whole units: "8" (all sides), "8,4" (left/right, top/bottom) or "8,4,8,0" (left, top, right,
bottom).
Alignment
Inside the rectangle it's given, an element is placed by HorizontalAlignment (Left,
Center, Right, Stretch) and VerticalAlignment (Top, Center, Bottom, Stretch).
Stretch fills the rectangle unless a Width/Height is set; the others size the element to its content.
<!-- Each child gets the space its parent offers; its size, margin and alignment decide where it sits in it. -->
<StackPanel Orientation="Vertical" Width="320" Padding="8">
<Button HorizontalAlignment="Left" Margin="0,0,0,8">Left</Button>
<Button Margin="0,0,0,8">Centered (the theme's default for buttons)</Button>
<Button HorizontalAlignment="Stretch" Margin="0,0,0,8">Stretch</Button>
<Button HorizontalAlignment="Right" MinWidth="160">Right, at least 160 wide</Button>
</StackPanel>
Elements stretch by default. The default theme centers buttons, so a button added straight to a canvas doesn't cover
the game; a local HorizontalAlignment="Stretch" on a button fills its slot again.
Panels
| Panel | How it shares its space |
|---|---|
StackPanel |
One child after another, in a column or a row. |
Grid |
Rows and columns sized Auto, in pixels or as shares (*, 2*); children span cells. |
WrapGrid |
Equal cells in rows that wrap, for inventories. |
ScrollViewer |
Content larger than itself, scrolled. |
SplitPane |
Two panes with a draggable divider. |
See Panels and Scrolling for each one's properties. A list (a
ListBox or WrapGrid) outside a ScrollViewer sizes to its items.
Scale
Layout works in UI units. The canvas multiplies them by its scale (the display's DPI, or a reference resolution) when it draws, so a 200-unit-wide panel stays the same physical size on a 4K screen. See DPI and UI scaling.
Pitfalls
- Limits are
NaNwhen unset. In your own controls, don't passWidthorMinWidthstraight toMath.Min,Math.Maxorfloat.Clamp: aNaNbound givesNaN. Checkfloat.IsNaNfirst. - A stretched child in a sized-to-content parent has nothing to stretch to. In a
StackPanelrow,Stretchalong the row means "size to content"; give the child aWidthor use aGridwith a*column. - Hidden elements keep their space. See Canvas, the host and the element tree.