Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Split a layout

retroglyph-ui’s layout engine divides one Rect into several, ratatui-style: no widget tree, no retained layout state, just a function that takes an area and a list of constraints and hands back the resulting Rects for that one frame.

split_h and split_v

split_h divides a Rect into side-by-side columns; split_v divides it into stacked rows. Both take the same Constraint list, one per resulting pane, and hand back a Vec<Rect> in the same order:

    fn draw_row(
        surface: &mut Surface<'_>,
        row: Rect,
        caption: &str,
        constraints: &[Constraint],
        labels: &[&str],
    ) {
        let style = Style::default();
        surface.print((row.left(), row.top()), caption, style);
        let body = Rect::new(row.left(), row.top() + 1, row.width(), row.height() - 1);
        let panes = split_h(body, constraints);
        for (pane, label) in panes.iter().zip(labels) {
            Self::draw_pane(surface, *pane, label);
        }
    }

Choosing a Constraint

  • Constraint::Fixed(n) reserves exactly n columns/rows, first, regardless of the other panes.
  • Constraint::Fill(weight) divides whatever’s left over after every Fixed/Min/Max pane is reserved, in proportion to each pane’s own weight: Fill(1), Fill(2), Fill(3) splits the remainder 1:2:3, not into three equal thirds.
  • Constraint::Min(n)/Constraint::Max(n) floor or cap a pane’s share of the fill remainder; they weigh 1 in that division regardless of n.

19_weighted_fill is a complete, static reference for all four combined: equal thirds, a weighted ratio, a Fixed pane plus a weighted remainder, and Min/Max mixed with Fill:

cargo run --example 19_weighted_fill --features crossterm
cargo run --example 19_weighted_fill --features software
cargo run --example 19_weighted_fill  # headless fallback, prints a few frames to stdout

Nesting a split

Neither function is aware of the other: split_v’s output Rects are ordinary Rects, so a pane from one call is exactly what the other call’s first argument expects. 11_sokoban’s screen layout is a split_v (a one-row title bar over the rest of the screen) feeding one of its rows into a split_h (the play field next to the status Panel), and there’s no limit to how many levels deep that composes.

Layout: spacing and Flex alignment

Layout wraps split_h/split_v with two optional builder calls, for the cases plain split_h/split_v don’t cover:

Layout::vertical([Constraint::Fixed(1), Constraint::Fill(1)])
    .spacing(1)
    .flex(Flex::Center)
    .split(area)

.spacing(n) carves a fixed-cell gap (or, via Spacing::Overlap(n), a shared border) between every adjacent pair of panes; split_with_gaps returns those gap Rects alongside the panes for drawing dividers into. .flex(Flex) controls where leftover space goes when constraints don’t consume a Rect’s full extent (every pane is Fixed, say, and they don’t add up to the whole width): Flex’s Start (the default, matching plain split_h/split_v: leftover space trails after the last pane), Center, End, or SpaceBetween/SpaceAround to distribute gaps between panes instead.

See also

  • Draw a panel and Handle a click, for what typically goes inside a split pane.
  • retroglyph_core::grid::Rect itself, if a split’s ratio-based math is overkill and a layout is simpler to compute by hand.