Layouts

Reflex provides five ways to add an object into a view. Each expresses a different layout intent: flowing inline with siblings, growing into the leftover space, overlaying at named alignment points, stretching to cover the parent, or pinning at exact coordinates.

Conventions used in every diagram:

  • Dashed grey rectangle is the parent object (the layout container).
  • Solid coloured rectangles are the children added to that parent.
  • Labels match the variable names you'd see in the code snippet beside the diagram.

1. AddInline

Lays a child out inline with its siblings along the parent's flow axis. Each child sizes itself, and the next one starts where the previous ended. The flow direction is set on the parent with SetFlow.

kFlowX (horizontal row)

Children lay out left-to-right inside the parent.

parent (kFlowX) m_a m_b m_c
GLX::SetFlow(m_parent, GLX::kFlowX);
GLX::AddInline(m_parent, m_a);
GLX::AddInline(m_parent, m_b);
GLX::AddInline(m_parent, m_c);

When to use: any row of buttons, tabs, toolbar icons, a label-plus-value pair, anything you'd naturally read left-to-right.

kFlowY (vertical stack)

Same call, different flow flag. Children stack top-to-bottom.

parent (kFlowY) m_a m_b m_c
GLX::SetFlow(m_parent, GLX::kFlowY);
GLX::AddInline(m_parent, m_a);
GLX::AddInline(m_parent, m_b);
GLX::AddInline(m_parent, m_c);

When to use: page sections stacked vertically, form rows, list items, navigation columns.


2. AddInlineFlex (one or more children grow to fill the axis)

Same flow as AddInline, but the flex children stretch to absorb any leftover space on the flow axis. Non-flex siblings keep their natural size.

parent (kFlowX) m_a m_b (flex: grows) m_c
GLX::SetFlow(m_parent, GLX::kFlowX);
GLX::AddInline(m_parent, m_a);          // natural width
GLX::AddInlineFlex(m_parent, m_b);      // grows to fill leftover space
GLX::AddInline(m_parent, m_c);          // natural width

When to use: a search bar that should fill the space between a fixed-size logo and a fixed-size avatar; a table cell that takes the remaining width; the editor pane between a sidebar and a status panel.

If multiple children use AddInlineFlex, they share the leftover space.

Cross-axis Orientation

AddInline and AddInlineFlex take an optional ortho parameter that controls how each child is positioned on the cross axis (the axis perpendicular to the flow). Values:

  • kOrientationNear: pin to start (left in FlowY)
  • kOrientationCenter: center on cross axis
  • kOrientationFar: pin to end (right in FlowY)
  • kOrientationFit: stretch (the default)

Example: GLX::AddInline(m_row, m_label, GLX::kOrientationCenter); centres the label vertically inside a kFlowX row, instead of stretching it to fill the row's height.


3. AddFloat (overlay at a corner or edge)

Floated children sit on top of the parent's inline-flow content at one of nine alignment points. Inline siblings lay out as if the floated child weren't there.

parent TopLeft TopCenter TopRight CenterLeft Center CenterRight BottomLeft BottomCenter BottomRight
GLX::AddFloat(m_panel, m_close_x, GLX::kAlignmentTopRight);
GLX::AddFloat(m_screen, m_fab,    GLX::kAlignmentBottomRight);
GLX::AddFloat(m_overlay, m_dialog, GLX::kAlignmentCenter);

When to use: close buttons in panel corners, floating action buttons, badges and notification dots, centred modals, pinned status indicators ("LIVE", "NEW"), tooltip anchors. Anything you'd reach for position: absolute for in CSS.

The diagram above shows each alignment as a separate box just for clarity. In practice you'd float one or two children, not all nine.


4. AddStretch (fills the entire parent rect)

A AddFloat variant with both axes set to fit. The child fills the entire parent, ignoring any inline siblings underneath.

parent (with inline siblings underneath) m_a m_b m_c m_overlay (AddStretch)
GLX::AddInline(m_parent, m_a);
GLX::AddInline(m_parent, m_b);
GLX::AddInline(m_parent, m_c);

GLX::AddStretch(m_parent, m_overlay);    // covers everything above

When to use: loading spinners and busy overlays, modal backdrops, "tap to dismiss" hit-targets that should cover the whole screen, blocking interaction with the content beneath.

AddStretch(parent, child) is literally AddFloat(parent, child, kOrientationFit, kOrientationFit). Use AddStretch when that's the intent; it reads clearer.


5. AddAbsolute (pixel-precise position)

Child positioned at exact (x, y) coordinates inside the parent. No alignment logic. No flow participation.

parent y = 40 x = 160 m_thumb SetPosition({160, 40})
GLX::AddAbsolute(m_parent, m_thumb);
m_thumb.SetPosition({ 160, 40 });

// Or in one call:
GLX::AddAbsolute(m_parent, m_thumb, { 160, 40 });

When to use: slider thumbs whose position you compute from a value, drag-and-drop ghost cursors, charts where you place data-points at calculated coordinates, anything where the coordinates come from arithmetic rather than an alignment.


Controlling content-driven sizing with EnableAutoFit

void EnableAutoFit(Object& object, bool x, bool y);   // shrink-to-content per axis

When autofit is on for an axis, the object reports a content-driven size on that axis (text width, the bounding box of its children, and so on). When off, the object surrenders that dimension to whatever the layout decides: style size, parent stretch, or its flex share.

This matters because every inline calculation reads child sizes first. A child that "looks the right size" because of its content can quietly distort sibling layout or flex distribution.

Worked example: a clean 50/50 horizontal split

Two children, both flex, both in kFlowX. The intuition is that each should get half the parent's width. With autofit-X on (the default for most objects), each child first reserves its content width and the leftover is then split flex-style. Children with different content end up unequal, even though they are both AddInlineFlex.

Turn autofit-X off on both, and neither child contributes a preferred width. The flex calculation has nothing to subtract first, so it splits the parent down the middle.

parent (kFlowX, 480 wide) m_left (50%) m_right (50%)
GLX::SetFlow(m_split, GLX::kFlowX);
GLX::AddInlineFlex(m_split, m_left);
GLX::AddInlineFlex(m_split, m_right);

GLX::EnableAutoFit(m_left,  false, true);   // ignore content width on X
GLX::EnableAutoFit(m_right, false, true);   // ignore content width on X

The same trick generalises: turn autofit off on the flow axis for every flex child whose content width you want excluded from the split.

Other common uses:

  • EnableAutoFit(false, true) for wrapped flow layouts (kStandardLayoutWrapped), so width is fixed by the parent and children know where to wrap while height grows row by row.
  • EnableAutoFit(false, true) for sidebar containers that should fill a fixed-width gutter but only be as tall as their items.
  • EnableAutoFit(true, false) flips the same idea: width hugs content, height is laid out for you.

How to create a centered Page Container

The classic web pattern: a content column that fills the page on narrow screens but tops out at a maximum width and centers itself when there's room.

m_page (kFlowX | kFlowCenter, wider than max) m_content (flex, max width 1200) auto gutter auto gutter
// 1. Centre items along the flow axis on the page container
GLX::SetFlow(m_page, GLX::kFlowX | GLX::kFlowCenter);

// 2. Add the content column as flex so it grows to fill the page width
GLX::AddInlineFlex(m_page, m_content);

// 3. Cap how wide it can grow. Once capped, kFlowCenter keeps it centered.
GLX::SetBounds(m_content, kNullKey, { 0, 0 }, { 1200, GLX::kLarge.h });

// 4. Stop content from driving width. The bounds and flex own the X axis.
GLX::EnableAutoFit(m_content, false, true);

Three building blocks doing one job each:

  • AddInlineFlex: the content wants to fill the page width.
  • SetBounds(..., max): caps that want at 1200 px.
  • kFlowCenter: once the content stops growing, the parent's leftover space is split evenly to either side, centering the column.

Swap to kFlowY | kFlowCenter and the same recipe gives you a vertically centred panel that grows to a maximum height.