TamboUI provides layout widgets for arranging children into structured compositions.
Layout widgets live in dev.tamboui.layout.* subpackages within the tamboui-core module.
Layout Overview
| Widget | Type | Description |
|---|---|---|
Stateless |
Multi-column grid layout |
|
Stateless |
CSS Grid-inspired layout with constraints and gutter |
|
Stateless |
5-region layout (top, bottom, left, right, center) |
|
Stateless |
Overlapping layers (painter’s algorithm) |
|
Stateless |
Wrap layout (flow left-to-right, wrap on overflow) |
Columns
A grid layout widget that arranges children into a fixed number of columns. Children are placed into cells whose positions are determined by the ordering mode.
Columns columns = Columns.builder()
.children(widget1, widget2, widget3, widget4)
.columnCount(2)
.spacing(1)
.order(ColumnOrder.ROW_FIRST)
.build();
columns.render(area, buffer);
Column ordering modes:
-
ColumnOrder.ROW_FIRST(default) - Items fill left-to-right, then top-to-bottom (like reading text) -
ColumnOrder.COLUMN_FIRST- Items fill top-to-bottom, then left-to-right (like newspaper columns)
Builder options:
-
children(Widget…)/children(List<Widget>)- The child widgets to arrange -
columnCount(int)- Number of columns (required, defaults to 1) -
spacing(int)- Gap between columns in cells (default: 0) -
flex(Flex)- How remaining space is distributed (default:Flex.START) -
order(ColumnOrder)- Child ordering mode (default:ROW_FIRST) -
columnWidths(Constraint…)- Per-column width constraints (default:fill()for all) -
rowHeights(int…)- Explicit row heights (default: equal distribution)
// Custom column widths and row-first ordering
Columns grid = Columns.builder()
.children(headerWidget, contentWidget, sidebarWidget, footerWidget)
.columnCount(2)
.columnWidths(Constraint.length(20), Constraint.fill())
.rowHeights(3, 10)
.spacing(1)
.build();
// Column-first ordering: fills top-to-bottom per column
Columns newspaperLayout = Columns.builder()
.children(article1, article2, article3, article4)
.columnCount(2)
.order(ColumnOrder.COLUMN_FIRST)
.build();
The widget-level Columns requires an explicit column count. For auto-detection based on child widths, use ColumnsElement in the Toolkit DSL (via the columns() factory method), which computes the column count from available width and child preferred widths.
|
Grid
A CSS Grid-inspired layout widget that arranges children into a grid with explicit control over grid dimensions, per-column/per-row sizing constraints, and gutter spacing.
Unlike Columns (which uses ordering modes and spacing), Grid provides independent horizontal and vertical gutter, per-column width constraints, per-row height constraints, and constraint cycling when fewer constraints than grid dimensions (matching Textual behavior).
Grid supports two mutually exclusive modes:
-
Children mode: Sequential placement using
children()andcolumnCount() -
Area mode: Template-based placement using
gridAreas()andarea()(CSS grid-template-areas style)
The staged builder pattern ensures compile-time safety — you cannot mix modes.
Children Mode
Grid grid = Grid.builder()
.children(widget1, widget2, widget3, widget4, widget5, widget6)
.columnCount(3)
.horizontalGutter(1)
.verticalGutter(1)
.build();
grid.render(area, buffer);
ChildrenBuilder options:
-
children(Widget…)/children(List<Widget>)- The child widgets to arrange -
columnCount(int)- Number of columns (defaults to 1) -
rowCount(int)- Optional maximum rows (validates children fit within columns × rows) -
horizontalGutter(int)- Gap between columns in cells (default: 0) -
verticalGutter(int)- Gap between rows in cells (default: 0) -
flex(Flex)- How remaining space is distributed (default:Flex.START) -
columnConstraints(Constraint…)- Per-column width constraints (default:fill()for all). Constraints cycle when fewer than the column count. -
rowConstraints(Constraint…)- Per-row height constraints. Constraints cycle when fewer than the row count. -
rowHeights(int…)- Explicit row heights (default: equal distribution)
// Grid with custom column widths and gutter
Grid dashboard = Grid.builder()
.children(cpuPanel, memoryPanel, diskPanel,
netUpPanel, netDownPanel, uptimePanel)
.columnCount(3)
.columnConstraints(
Constraint.length(16), // fixed first column
Constraint.fill(), // remaining columns fill
Constraint.fill()
)
.horizontalGutter(2)
.verticalGutter(1)
.build();
// Grid with row constraints
Grid sized = Grid.builder()
.children(header, content, sidebar, footer)
.columnCount(2)
.rowConstraints(Constraint.length(3), Constraint.fill())
.build();
// Column constraint cycling: single constraint applied to all columns
Grid uniform = Grid.builder()
.children(a, b, c, d, e, f)
.columnCount(3)
.columnConstraints(Constraint.length(10)) // all 3 cols get length(10)
.build();
Area Mode (Grid Template Areas)
Area mode uses CSS grid-template-areas style templates for defining layouts where cells can span multiple rows and columns.
Named areas must form contiguous rectangles — L-shapes and disconnected regions are rejected with a LayoutException.
// "Holy grail" layout with spanning regions
Grid layout = Grid.builder()
.gridAreas("header header header",
"nav main main",
"nav main main",
"footer footer footer")
.area("header", headerWidget)
.area("nav", navWidget)
.area("main", mainWidget)
.area("footer", footerWidget)
.horizontalGutter(1)
.verticalGutter(1)
.build();
layout.render(area, buffer);
AreaBuilder options:
-
gridAreas(String…)- Row templates defining named areas (use.for empty cells) -
area(String, Widget)- Assign a widget to a named area -
horizontalGutter(int)- Gap between columns (default: 0) -
verticalGutter(int)- Gap between rows (default: 0) -
flex(Flex)- How remaining space is distributed (default:Flex.START) -
columnConstraints(Constraint…)- Per-column width constraints -
rowConstraints(Constraint…)- Per-row height constraints
Template rules:
-
Each row is a space-separated list of area names
-
All rows must have the same number of columns
-
Area names must start with a letter (alphanumeric and underscores allowed)
-
Use
.(dot) for empty cells -
Named areas must form contiguous rectangles
// Dashboard with 2x2 spanning main area
Grid dashboard = Grid.builder()
.gridAreas("A A B",
"A A C",
"D D D")
.area("A", mainPanel) // 2x2 span
.area("B", sidePanel1)
.area("C", sidePanel2)
.area("D", statusBar) // full-width span
.horizontalGutter(1)
.verticalGutter(1)
.build();
// Empty cells with dot notation
Grid sparse = Grid.builder()
.gridAreas("A . B",
". C .")
.area("A", widget1)
.area("B", widget2)
.area("C", widget3)
.build();
Areas without assigned widgets render as empty space. Assigning a widget to an undefined area throws LayoutException.
|
The widget-level Grid requires an explicit column count or grid areas template. For auto-sizing based on child count (ceil(sqrt(n)) columns) and CSS property support, use GridElement in the Toolkit DSL (via the grid() factory method).
|
Dock
A 5-region dock layout widget that arranges children into top, bottom, left, right, and center regions — the most common TUI application structure (header + sidebar + content + footer).
Dock dock = Dock.builder()
.top(headerWidget)
.bottom(statusBarWidget)
.left(sidebarWidget)
.right(outlineWidget)
.center(editorWidget)
.topHeight(Constraint.length(3))
.bottomHeight(Constraint.length(1))
.leftWidth(Constraint.length(20))
.rightWidth(Constraint.length(20))
.build();
dock.render(area, buffer);
Builder options:
-
.top(Widget)/.bottom(Widget)/.left(Widget)/.right(Widget)/.center(Widget)- Set region widgets (all optional) -
.topHeight(Constraint)- Height constraint for the top region (default:length(1)) -
.bottomHeight(Constraint)- Height constraint for the bottom region (default:length(1)) -
.leftWidth(Constraint)- Width constraint for the left region (default:length(10)) -
.rightWidth(Constraint)- Width constraint for the right region (default:length(10))
Rendering algorithm: Top and bottom take full width, then the remaining middle area is split horizontally into left, center, and right. Omitted regions are skipped — for example, setting only center gives a full-area layout.
Stack
An overlapping layers widget where children render on top of each other using a painter’s algorithm (last child on top). Essential for dialogs, popups, floating overlays, and any scenario where UI elements need to overlap.
Stack stack = Stack.builder()
.children(backgroundWidget, dialogWidget)
.alignment(ContentAlignment.STRETCH)
.build();
stack.render(area, buffer);
Builder options:
-
.children(Widget…)/.children(List<Widget>)- The child widgets to stack -
.alignment(ContentAlignment)- How children are positioned (default:STRETCH)
Alignment modes: TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT, CENTER, CENTER_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT, STRETCH.
Flow
A wrap layout widget where items flow left-to-right and wrap to the next line when exceeding the available width. Useful for tag clouds, button groups, chip lists, and similar layouts where items should wrap naturally.
Flow flow = Flow.builder()
.item(tag1Widget, 8)
.item(tag2Widget, 12)
.item(tag3Widget, 6)
.horizontalSpacing(1)
.verticalSpacing(1)
.build();
flow.render(area, buffer);
Builder options:
-
.item(Widget, int width)/.item(Widget, int width, int height)- Add an item with explicit size -
.items(List<FlowItem>)- Set items from a list -
.horizontalSpacing(int)- Gap between items on the same row (default: 0) -
.verticalSpacing(int)- Gap between rows (default: 0)
The widget-level Flow requires explicit item widths via FlowItem since the Widget interface has no preferredWidth() method. For auto-measurement, use FlowElement in the Toolkit DSL (via the flow() factory method), which auto-measures children via Element.preferredWidth().
|
Using Layouts with Toolkit DSL
The Toolkit DSL provides fluent factories for all layout widgets:
import static dev.tamboui.toolkit.Toolkit.*;
// Multi-column grid (auto-detects column count from child widths)
columns(item1, item2, item3, item4, item5, item6)
.spacing(1)
// Explicit column count with column-first ordering
columns(child1, child2, child3, child4)
.columnCount(2)
.columnFirst()
// CSS Grid-inspired layout with gutter and constraints
grid(item1, item2, item3, item4, item5, item6)
.gridSize(3)
.gutter(1)
// Grid with explicit dimensions and column constraints
grid(header, content, sidebar, footer)
.gridSize(2, 2)
.gridColumns(Constraint.length(20), Constraint.fill())
.gutter(1, 0)
// Grid with template areas (CSS grid-template-areas style)
grid()
.gridAreas("header header header",
"nav main main",
"nav main main",
"footer footer footer")
.area("header", text("Header").bold())
.area("nav", list("Nav 1", "Nav 2"))
.area("main", text("Main Content"))
.area("footer", text("Footer").dim())
.gutter(1)
// 5-region dock layout
dock()
.top(text("Header").bold())
.bottom(text("Footer").dim())
.left(list("Nav 1", "Nav 2", "Nav 3"))
.center(text("Main Content"))
.topHeight(Constraint.length(3))
.leftWidth(Constraint.length(20))
// Overlapping layers (last child on top)
stack(backgroundElement, dialogElement)
.alignment(ContentAlignment.CENTER)
// Wrap layout (items flow and wrap)
flow(tag1, tag2, tag3, tag4, tag5)
.spacing(1)
.rowSpacing(1)
See API Levels for more details on the Toolkit DSL.