Skip to content

feat(#203): a wrapping flow layout - #204

Merged
octalide merged 6 commits into
devfrom
feat/203
Oct 1, 2026
Merged

octalide merged 6 commits into
devfrom
feat/203

Conversation

@octalide

@octalide octalide commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #203

Plan

  • blit.layout: a Flow options record beside Stack (align, gap, row_gap, the flow's own w/h, item_w/item_h) and layout.flow(gap, row_gap). layout.rows(...) builds a flow frame. resolve places an item at the pen. When the width is known, the row already holds an item, and the item would pass the right edge (1/64 px slack), it wraps to the next row one row gap below the row's bottom. An item wider than a whole row is clamped to the row width, so it takes the row alone and the widget cuts its own label (fit_box, fit_pair). Across, items size and align (start, center, end, stretch) against their row's height. commit tracks rows. A row's height is its tallest item, need_h stacks the rows, and need_w is the one-row natural width.
  • blit.widget: flow(ctx) gives the options at the style system's gaps: the layout style's margin.r between items, margin.b between rows. begin_flow/end_flow open a flow as the next item of the current frame, sized in its parent exactly as begin_stack is. Both share one open and close path (enter, leave). Each row's natural height goes in the state store under an id derived from the flow's id and the row index, so items align in a row whose height depends on later items. It settles one frame late, like stacks. A flow that is not start-aligned, or that its parent aligns by its size, spends its first frame measuring, as stacks do.
  • A flow is an ordinary frame, so it nests in stacks, windows, docked bodies and overlays, and stacks nest in it.
  • Docs: layout and widget headers, README "Widgets & layout". Gallery layout section: wrapping tags and label/value pairs cut with fit_pair.

Decisions the issue left open:

  • Flows wrap horizontally only. A vertical (column-wrapping) flow is not built.
  • A flow's natural width is its items in one row. A width fitted to content (limit(fit(), 0, max)) wraps at max and is as narrow as its items when they fit in less. That is how a flow inside an overlay (which fits its content) gets a wrap width. An open width with no max never wraps.
  • Fill and frac along x in a flow take the rest of the current row (or a fraction of it), as same_line does.
  • Alignment uses layout.Align in the options, as Stack.align does. The style system has no alignment field, so the gaps come from the style and alignment from the options.

cellar's readout becomes: size_next(limit(fit(), 0, max)), begin_flow(ui, "readout", flow(ui)), then one place and one fit_pair per pair, then end_flow.

Ran: mach test . --filter for layout__, flow__, space__, dock__, overlay__ and window__, the gallery test (golden unchanged), and mach fmt --check on the root and demo/gallery.

a flow is a frame laid out along x whose items wrap: one that would pass the
right edge of a row already holding an item starts the next row a row gap
below. a row is as tall as its tallest item and its items size and align
across against the height it measured last frame. an item wider than a whole
row takes it alone at the row's width. the natural width is the items in one
row, so a flow fitted to its content with a largest size wraps there.
widget.flow gives the options at the layout style's gaps, margin.r between
items and margin.b between rows. a flow is sized in its parent as a stack is
and shares its open and close path. each row's natural height is kept in the
state store under the flow's id and the row's index, so items align in rows
whose height comes from later items. a flow that is not start-aligned, or
that its parent aligns by its size, spends its first frame measuring.
@octalide
octalide force-pushed the feat/203 branch 2 times, most recently from fb69de1 to 302c70b Compare October 1, 2026 20:01
@octalide
octalide marked this pull request as ready for review October 1, 2026 20:01
@octalide
octalide merged commit 46e9b4e into dev Oct 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: a wrapping flow layout

1 participant