Documentation
¶
Overview ¶
Package layout partitions terminal screen space into rectangular regions using a constraint-based solver.
Under the hood, it relies on the Cassowary constraint solver algorithm to resolve competing size requirements. Each constraint carries a priority, so the solver can pick the best trade-off when not every requirement fits.
How It Works ¶
A Layout takes the available area and a list of constraints (Len, Ratio, Percent, Fill, Min, Max) and produces a set of non-overlapping rectangles. The solver tries to honour every constraint; when that is impossible it relaxes lower-priority ones first.
You are not required to use Layout at all. If you prefer manual control, you can compute uv.Rectangle values yourself with plain arithmetic.
Acknowledgements ¶
This implementation is heavily based on Ratatui source code and is roughly 1:1 translation from Rust with some minor API adjustments.
Index ¶
- type Constraint
- type Direction
- type Fill
- type Flex
- type Layout
- func (l Layout) Split(area uv.Rectangle) Splitted
- func (l Layout) SplitWithSpacers(area uv.Rectangle) (segments, spacers Splitted)
- func (l Layout) WithConstraints(constraints ...Constraint) Layout
- func (l Layout) WithDirection(direction Direction) Layout
- func (l Layout) WithFlex(flex Flex) Layout
- func (l Layout) WithPadding(padding Padding) Layout
- func (l Layout) WithSpacing(spacing int) Layout
- type Len
- type Max
- type Min
- type Padding
- type Percent
- type Ratio
- type Splitted
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Constraint ¶
type Constraint interface {
// contains filtered or unexported methods
}
Constraint describes how a single segment of a Layout should be sized.
Each constraint type expresses a different kind of sizing rule: fixed (Len), proportional (Percent, Ratio), bounded (Min, Max), or greedy (Fill). Proportional constraints are evaluated against the full area being split rather than the remaining space after fixed constraints have been applied.
When the solver cannot satisfy every constraint, it resolves conflicts according to the following priority order (highest first):
type Direction ¶
type Direction int
Direction controls whether a Layout arranges its segments horizontally (left to right) or vertically (top to bottom).
type Fill ¶
type Fill int
Fill distributes remaining space proportionally among all Fill segments according to their respective weights.
A Fill segment only expands into space left over after higher-priority constraints have been satisfied. Multiple Fill segments share that leftover in proportion to their integer values.
Examples ¶
[Fill(1), Fill(2), Fill(3)] ┌──────┐┌───────────────┐┌───────────────────────┐ │ 8 px ││ 17 px ││ 25 px │ └──────┘└───────────────┘└───────────────────────┘ [Fill(1), Percent(50), Fill(1)] ┌───────────┐┌───────────────────────┐┌──────────┐ │ 13 px ││ 25 px ││ 12 px │ └───────────┘└───────────────────────┘└──────────┘
type Flex ¶
type Flex int
Flex controls how leftover space is distributed once every segment's constraint has been resolved. It is analogous to the CSS justify-content property and is used together with Layout.
const ( // FlexStart pushes segments to the leading edge of the area, leaving // any surplus space at the trailing edge. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐┌──────20 px───────┐┌──────20 px───────┐ // │ Percent(20) ││ Length(20) ││ Fixed(20) │ // └──────────────┘└──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐┌──────20 px───────┐ // │ Max(20) ││ Max(20) │ // └──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ // │ Max(20) │ // └──────────────────┘ FlexStart Flex = iota // FlexLegacy fills the entire area by assigning surplus space to the // lowest-priority trailing segment. This reproduces the original // Ratatui/tui-rs layout behaviour. // // The examples below show how surplus space is allocated for different // constraint combinations. Recall the priority order (highest first): // // - [Min] // - [Max] // - [Len] // - [Percent] // - [Ratio] // - [Fill] // // With all-[Len] constraints the surplus goes to the final segment. // // <----------------------------------- 80 px ------------------------------------> // ┌──────20 px───────┐┌──────20 px───────┐┌────────────────40 px─────────────────┐ // │ Length(20) ││ Length(20) ││ Length(20) │ // └──────────────────┘└──────────────────┘└──────────────────────────────────────┘ // ^^^^^^^^^^^^^^^^ EXCESS ^^^^^^^^^^^^^^^^ // // [Fill] has the lowest priority, so it always absorbs surplus. // // <----------------------------------- 80 px ------------------------------------> // ┌──────20 px───────┐┌──────20 px───────┐┌──────20 px───────┐┌──────20 px───────┐ // │ Fill(0) ││ Max(20) ││ Length(20) ││ Length(20) │ // └──────────────────┘└──────────────────┘└──────────────────┘└──────────────────┘ // ^^^^^^ EXCESS ^^^^^^ // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌──────────────────────────60 px───────────────────────────┐┌──────20 px───────┐ // │ Min(20) ││ Max(20) │ // └──────────────────────────────────────────────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌────────────────────────────────────80 px─────────────────────────────────────┐ // │ Max(20) │ // └──────────────────────────────────────────────────────────────────────────────┘ FlexLegacy // FlexEnd pushes segments to the trailing edge of the area, leaving // surplus space at the leading edge. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐┌──────20 px───────┐┌──────20 px───────┐ // │ Percent(20) ││ Length(20) ││ Length(20) │ // └──────────────┘└──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐┌──────20 px───────┐ // │ Max(20) ││ Max(20) │ // └──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ // │ Max(20) │ // └──────────────────┘ FlexEnd // FlexCenter places segments in the middle of the area, distributing // surplus space equally before the first and after the last segment. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐┌──────20 px───────┐┌──────20 px───────┐ // │ Percent(20) ││ Length(20) ││ Length(20) │ // └──────────────┘└──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐┌──────20 px───────┐ // │ Max(20) ││ Max(20) │ // └──────────────────┘└──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ // │ Max(20) │ // └──────────────────┘ FlexCenter // FlexSpaceBetween distributes surplus space equally between adjacent // segments, with no space before the first or after the last. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐ ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Percent(20) │ │ Length(20) │ │ Length(20) │ // └──────────────┘ └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Max(20) │ │ Max(20) │ // └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌────────────────────────────────────80 px─────────────────────────────────────┐ // │ Max(20) │ // └──────────────────────────────────────────────────────────────────────────────┘ FlexSpaceBetween // FlexSpaceEvenly distributes surplus space so that every gap // (including before the first and after the last segment) is the same width. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐ ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Percent(20) │ │ Length(20) │ │ Length(20) │ // └──────────────┘ └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Max(20) │ │ Max(20) │ // └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ // │ Max(20) │ // └──────────────────┘ FlexSpaceEvenly // FlexSpaceAround places equal space on both sides of each segment. // Adjacent segments therefore have twice the gap of the outer edges. // // # Examples // // <------------------------------------80 px-------------------------------------> // ┌────16 px─────┐ ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Percent(20) │ │ Length(20) │ │ Length(20) │ // └──────────────┘ └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ ┌──────20 px───────┐ // │ Max(20) │ │ Max(20) │ // └──────────────────┘ └──────────────────┘ // // <------------------------------------80 px-------------------------------------> // ┌──────20 px───────┐ // │ Max(20) │ // └──────────────────┘ FlexSpaceAround )
type Layout ¶
type Layout struct {
Direction Direction
Constraints []Constraint
Padding Padding
// Spacing is the gap between adjacent segments, measured in cells.
// A negative value causes segments to overlap by that many cells.
Spacing int
Flex Flex
}
Layout splits a rectangular area into smaller rectangles using a set of constraints. It is the primary building block for structuring terminal user interfaces.
Fields:
- Direction: whether segments flow vertically or horizontally.
- Constraints: the sizing rules (Len, Ratio, Percent, Fill, Min, Max).
- Padding: inset applied to the outer area before solving.
- Flex: strategy for distributing leftover space among segments.
- Spacing: gap (or overlap, if negative) between adjacent segments.
Internally, sizes are resolved by a Cassowary linear-constraint solver that satisfies as many rules as it can, preferring higher-priority constraints when trade-offs are necessary.
func Horizontal ¶
func Horizontal(constraints ...Constraint) Layout
Horizontal is shorthand for New(DirectionHorizontal, constraints...).
func New ¶
func New(direction Direction, constraints ...Constraint) Layout
New returns a Layout configured with the given direction and constraints.
func Vertical ¶
func Vertical(constraints ...Constraint) Layout
Vertical is shorthand for New(DirectionVertical, constraints...).
func (Layout) Split ¶
Split partitions the area into content rectangles according to the layout's direction and constraints.
Because every constraint is evaluated against the total area, mixing relative constraints (Percent, Ratio) with absolute ones (Min, Max, Len) can produce ambiguous results. For example, splitting 100 cells as [Min(20), Percent(50), Percent(50)] will not necessarily yield [20, 40, 40].
func (Layout) SplitWithSpacers ¶
SplitWithSpacers divides the given area into content segments and the gaps (spacers) between them. It returns both slices; use Layout.Split if you only need the content rectangles.
func (Layout) WithConstraints ¶
func (l Layout) WithConstraints(constraints ...Constraint) Layout
WithConstraints returns a shallow copy of the layout with the given constraints appended to its existing list.
func (Layout) WithDirection ¶
WithDirection returns a shallow copy of the layout using the specified direction.
func (Layout) WithFlex ¶
WithFlex returns a shallow copy of the layout using the specified flex strategy.
func (Layout) WithPadding ¶
WithPadding returns a shallow copy of the layout using the specified padding.
func (Layout) WithSpacing ¶
WithSpacing returns a shallow copy of the layout using the specified spacing value.
type Len ¶
type Len int
Len fixes the segment to exactly the given number of cells.
Examples ¶
[Len(20), Len(20)] ┌──────────────────┐┌──────────────────┐ │ 20 px ││ 20 px │ └──────────────────┘└──────────────────┘ [Len(20), Len(30)] ┌──────────────────┐┌────────────────────────────┐ │ 20 px ││ 30 px │ └──────────────────┘└────────────────────────────┘
type Max ¶
type Max int
Max caps the segment at the given number of cells.
Examples ¶
[Percent(0), Max(20)] ┌────────────────────────────┐┌──────────────────┐ │ 30 px ││ 20 px │ └────────────────────────────┘└──────────────────┘ [Percent(0), Max(10)] ┌──────────────────────────────────────┐┌────────┐ │ 40 px ││ 10 px │ └──────────────────────────────────────┘└────────┘
type Min ¶
type Min int
Min ensures the segment is no smaller than the given number of cells.
Examples ¶
[Percent(100), Min(20)] ┌────────────────────────────┐┌──────────────────┐ │ 30 px ││ 20 px │ └────────────────────────────┘└──────────────────┘ [Percent(100), Min(10)] ┌──────────────────────────────────────┐┌────────┐ │ 40 px ││ 10 px │ └──────────────────────────────────────┘└────────┘
type Padding ¶
type Padding struct {
Top, Right, Bottom, Left int
}
Padding defines the inset applied to a Layout's outer area before solving.
type Percent ¶
type Percent int
Percent sizes the segment as a fraction of the total area.
The integer value is treated as a percentage (0-100+) and multiplied by the total area; the result is rounded to the nearest cell.
Because only whole integers are accepted, some fractions (e.g. 1/3) cannot be represented exactly. Consider Ratio or Fill instead.
Examples ¶
[Percent(75), Fill(1)] ┌────────────────────────────────────┐┌──────────┐ │ 38 px ││ 12 px │ └────────────────────────────────────┘└──────────┘ [Percent(50), Fill(1)] ┌───────────────────────┐┌───────────────────────┐ │ 25 px ││ 25 px │ └───────────────────────┘└───────────────────────┘
type Ratio ¶
type Ratio struct{ Num, Den int }
Ratio sizes the segment as a numerator/denominator fraction of the total area.
The fraction is converted to a float, multiplied by the area, and rounded to the nearest cell.
Examples ¶
[Ratio(1, 2) ; 2] ┌───────────────────────┐┌───────────────────────┐ │ 25 px ││ 25 px │ └───────────────────────┘└───────────────────────┘ [Ratio(1, 4) ; 4] ┌───────────┐┌──────────┐┌───────────┐┌──────────┐ │ 13 px ││ 12 px ││ 13 px ││ 12 px │ └───────────┘└──────────┘└───────────┘└──────────┘
type Splitted ¶
Splitted holds the rectangles produced by a Layout.Split call.
func (Splitted) Assign ¶
Assign stores each resulting rectangle into the corresponding pointer.
Nil pointers are silently skipped.
Panics when len(areas) exceeds the number of rectangles in Splitted.
Examples ¶
var top, bottom uv.Rectangle
layout.New(layout.Fill(1), layout.Len(1)).
Split(area).
Assign(&top, &bottom)