Layout
Divide the screen with constraints, and split it into resizable panes.
Fluent builder for creating named layout regions. Sizes are computed with exact integer arithmetic: regions never overlap, never leave the area, and rounding never drops a cell.
var layout = Layout.Vertical(screen)
.Add("header", Constraint.Length(3)) // Exactly 3 rows (shrunk only if fixed sizes don't fit)
.Add("content", Constraint.Fill()) // Fill remaining space
.Add("footer", Constraint.Length(1))
.Spacing(1) // Optional: empty cells between regions
.Build(); // Optional: the indexer builds on first use
buf.SetString(layout["header"].X, layout["header"].Y, "Title", Style.Default.Bold());
Widget.List().Items(items).Draw(buf, layout["content"]);
var columns = Layout.Split(area, Direction.Horizontal, Constraint.Percentage(30), Constraint.Fill());
| Constraint | Size |
|---|---|
Length(n) |
n cells |
Percentage(p), Ratio(a, b) |
that share of the space, rounded down |
Min(n) |
at least n, then grows like Fill(1) |
Max(n) |
grows like Fill(1), up to n |
Fill(weight) |
shares the leftover in proportion to weight |
When the fixed sizes don't fit, Length, Percentage and Ratio shrink proportionally first,
then Min. Space that nothing can grow into is placed by .Flex(Flex.Start | End | Center | SpaceBetween). .Margin(new Margin(1)) insets the area first.
Split panes
Widget.Split(state) divides an area into two panes with a one-cell divider and returns them for you to draw
into. The user drags the divider (with WithMouse()) or moves it with Alt+Shift and the arrows. A
SplitState is sized by Ratio (the panes scale with the terminal) or, with SplitState.Fixed(direction, cells, pane), keeps one pane at a size in cells, such as a sidebar. MinFirst and MinSecond (3 by default)
limit how small the user can make each pane, and Changed fires when they move it. Splits nest:
private readonly SplitState _sidebar = SplitState.Fixed(Direction.Horizontal, 24);
public override bool HandleMouse(MouseEvent mouse) => _sidebar.HandleMouse(mouse);
public override bool HandleKey(ConsoleKeyInfo key) => _sidebar.HandleKey(key);
public override void Render(CellBuffer buf, Rect area, RenderContext ctx)
{
var (left, right) = Widget.Split(_sidebar).Draw(buf, area);
ctx.Render(_files, buf, left);
ctx.Render(_preview, buf, right);
}
Focused children see keys first, and the list, scroll and tree states take arrows with any modifier, so a pane built on them passes Alt keys on to keep Alt+Shift+arrows for the split. The text states leave Alt with the arrows, Home and End unhandled too.