Skip to content

Layout

Tuika uses integer-native flex layout for application structure. Flex is the general container, Flow is the concise choice for intrinsic items that wrap, and Grid is a deliberately small equal-column grid. All three measure ordinary third-party View implementations through the same MeasureRequest contract.

Flex containers and items

Container style controls direction, wrapping, padding, gaps, and alignment:

use tuika::prelude::*;

let tags = Flex::row()
    .wrap(FlexWrap::Wrap)
    .column_gap(1)
    .row_gap(1)
    .align_content(AlignContent::SpaceBetween)
    .auto(element(Text::raw("rust")))
    .auto(element(Text::raw("terminal-ui")));
# let _ = tags;

align positions items within one line. align_content distributes the lines themselves when wrapping creates more than one. justify distributes space on each line. Use row_gap and column_gap when the two axes need different spacing; gap sets both.

Child sizing belongs to FlexItemStyle, separately from the container:

# use tuika::prelude::*;
let item = FlexItemStyle::default()
    .basis(Dimension::Fixed(12))
    .grow(1)
    .shrink(1)
    .min_main(6)
    .max_main(20)
    .align_self(Align::Center);
let row = Flex::row().styled(item, element(Text::raw("resizable")));
# let _ = row;

The basis is the starting main-axis size. Positive free space is shared by grow; negative free space is removed according to shrink, without crossing the item’s min/max constraints. Integer cells are assigned by rounding track boundaries, so the final child reaches the exact container boundary without losing or double-counting a cell.

Wrapping and line alignment

FlexWrap::Wrap starts a new line when the next item’s outer main size no longer fits. Each line resolves grow, shrink, justification, and item alignment independently. AlignContent::{Start, Center, End, Stretch, SpaceBetween} then places completed lines on the cross axis. NoWrap remains the default.

Flow packages the common intrinsic wrapping case:

# use tuika::prelude::*;
let flow = Flow::new()
    .gap(1)
    .item(element(Text::raw("one")))
    .item(element(Text::raw("two")))
    .item(element(Text::raw("a-longer-item")));
# let _ = flow;

Choose Flex when children need grow/shrink, fixed or percentage bases, or per-item alignment. Choose Flow when every child is intrinsic and wrapping is the main behavior.

Flow versus Grid

Flow packs variable-width items and wraps wherever the next item stops fitting. Grid::new(columns) creates a fixed number of equal-width columns, fills them row-major, and derives each row’s height from its tallest cell.

# use tuika::prelude::*;
let grid = Grid::new(3)
    .column_gap(1)
    .row_gap(1)
    .cell(element(Text::raw("a")))
    .cell(element(Text::raw("b")))
    .cell(element(Text::raw("c")));
# let _ = grid;

Grid intentionally omits CSS Grid’s named lines, implicit tracks, spanning, dense packing, and independent track definitions. Those features add a large constraint language while terminal applications usually need a stable column count and predictable clipping. Compose nested Flex/Grid containers when a screen needs a more irregular structure.

Measurement requests

View::measure_request receives optional known axes and an AvailableSpace mode for unresolved axes:

  • Definite(n) means at most n cells are available.
  • MinContent asks for the smallest useful intrinsic contribution.
  • MaxContent asks for the preferred unconstrained contribution.
  • known_width and known_height mean the parent has already fixed that axis.

Flex and Grid preserve those modes when measuring third-party views and add a known axis once a fixed basis or grid track has been resolved. Existing views that only implement measure remain compatible through the default adapter. Because MeasureRequest and AvailableSpace are non-exhaustive, downstream implementations should match with a fallback arm so future request metadata can be added compatibly.

Migrating existing layouts

  • LayoutStyle::gap became independent row_gap and column_gap fields. Use Flex::gap(n) when both should remain equal.
  • Item::dimension moved into Item::style.basis. Prefer Item::styled(FlexItemStyle, intrinsic) for direct solver use.
  • Flex::{auto,grow,fixed} remain concise compatibility builders. Use Flex::styled when shrink, min/max, or per-child alignment matters.
  • Existing no-wrap layouts keep their behavior unless FlexWrap::Wrap is set.

The public solve_layout result includes child rectangles and flex-line metadata for hosts that need hit testing, scrolling, or layout inspection without painting.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close