Documentation
¶
Index ¶
- func MeasureText(content string, wrap WrapMode, maxWidth int) (width, height int)
- func PreservesCrossSize(node LayoutNode, parentAxis Axis) bool
- type Axis
- type BoxModel
- func (b BoxModel) BorderBox() Rect
- func (b BoxModel) BorderBoxHeight() int
- func (b BoxModel) BorderBoxWidth() int
- func (b BoxModel) BorderOrigin() (x, y int)
- func (b BoxModel) ClampScrollOffsetX(offset int) int
- func (b BoxModel) ClampScrollOffsetY(offset int) int
- func (b BoxModel) ContentBox() Rect
- func (b BoxModel) ContentHeight() int
- func (b BoxModel) ContentOrigin() (x, y int)
- func (b BoxModel) ContentWidth() int
- func (b BoxModel) EffectiveVirtualHeight() int
- func (b BoxModel) EffectiveVirtualWidth() int
- func (b BoxModel) IsScrollable() bool
- func (b BoxModel) IsScrollableX() bool
- func (b BoxModel) IsScrollableY() bool
- func (b BoxModel) MarginBox() Rect
- func (b BoxModel) MarginBoxHeight() int
- func (b BoxModel) MarginBoxWidth() int
- func (b BoxModel) MaxScrollX() int
- func (b BoxModel) MaxScrollY() int
- func (b BoxModel) PaddingBox() Rect
- func (b BoxModel) PaddingBoxHeight() int
- func (b BoxModel) PaddingBoxWidth() int
- func (b BoxModel) TotalHorizontalInset() int
- func (b BoxModel) TotalVerticalInset() int
- func (b BoxModel) UsableContentBox() Rect
- func (b BoxModel) Validate()
- func (b BoxModel) VirtualContentRect() Rect
- func (b BoxModel) VisibleContentRect() Rect
- func (b BoxModel) WithBorder(border EdgeInsets) BoxModel
- func (b BoxModel) WithClampedScrollOffset() BoxModel
- func (b BoxModel) WithMargin(margin EdgeInsets) BoxModel
- func (b BoxModel) WithPadding(padding EdgeInsets) BoxModel
- func (b BoxModel) WithScrollOffset(offsetX, offsetY int) BoxModel
- func (b BoxModel) WithScrollbarHeight(height int) BoxModel
- func (b BoxModel) WithScrollbarWidth(width int) BoxModel
- func (b BoxModel) WithScrollbars(width, height int) BoxModel
- func (b BoxModel) WithSize(width, height int) BoxModel
- func (b BoxModel) WithVirtualSize(virtualWidth, virtualHeight int) BoxModel
- type BoxNode
- type ColumnNode
- type ComputedLayout
- type Constraints
- type CrossAxisAlignment
- type DockEdge
- type DockNode
- type EdgeInsets
- type FlexNode
- type HorizontalAlignment
- type LayoutNode
- type LinearNode
- type MainAxisAlignment
- type PercentNode
- type PositionedChild
- type Rect
- type RowNode
- type ScrollableNode
- type SizePreserver
- type SplitPaneNode
- type StackChild
- type StackNode
- type TextNode
- type VerticalAlignment
- type WrapMode
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MeasureText ¶
MeasureText measures text content and returns content-box dimensions. It handles wrapping based on WrapMode and available width.
Parameters:
- content: The text to measure
- wrap: Wrapping mode (WrapNone, WrapWord, WrapChar)
- maxWidth: Maximum width in cells (0 or negative = unbounded)
Returns:
- width: The width of the widest line (in cells)
- height: Number of lines (after wrapping)
func PreservesCrossSize ¶
func PreservesCrossSize(node LayoutNode, parentAxis Axis) bool
PreservesCrossSize checks if a node preserves its cross-axis size for the given axis. For a horizontal parent (Row), cross axis is height. For a vertical parent (Column), cross axis is width. Returns false if node doesn't implement SizePreserver.
Types ¶
type BoxModel ¶
type BoxModel struct {
// Border-box dimensions (content + padding + border)
Width int
Height int
// Insets (padding and border shrink inward, margin expands outward)
Padding EdgeInsets
Border EdgeInsets // Typically uniform (1,1,1,1) for borders
Margin EdgeInsets
// Scrolling (optional - zero values mean no scrolling)
// VirtualWidth/Height represent the total scrollable content size.
// When set, content can extend beyond the visible viewport.
VirtualWidth int // Total virtual content width (0 = same as computed ContentWidth)
VirtualHeight int // Total virtual content height (0 = same as computed ContentHeight)
ScrollOffsetX int // Horizontal scroll offset
ScrollOffsetY int // Vertical scroll offset
// ScrollbarWidth is the space reserved for a vertical scrollbar.
// This reduces the usable content width when vertical scrolling is enabled.
ScrollbarWidth int
// ScrollbarHeight is the space reserved for a horizontal scrollbar.
// This reduces the usable content height when horizontal scrolling is enabled.
ScrollbarHeight int
}
BoxModel describes a rectangular region with CSS border-box semantics. Width and Height refer to the border-box (content + padding + border). Content dimensions are computed by subtracting padding and border from the border-box. Margin is added externally to get the margin-box (total allocated space).
The model consists of four nested boxes:
- Content box: computed as border-box minus padding and border
- Padding box: border-box minus border
- Border box: the stored Width/Height dimensions
- Margin box: border-box plus margin (the outermost boundary)
func (BoxModel) BorderBox ¶
BorderBox returns the border box as a Rect. The position is relative to the margin box origin.
func (BoxModel) BorderBoxHeight ¶
BorderBoxHeight returns the height of the border box. This is the stored Height value.
func (BoxModel) BorderBoxWidth ¶
BorderBoxWidth returns the width of the border box. This is the stored Width value.
func (BoxModel) BorderOrigin ¶
BorderOrigin returns the offset from the margin box origin to the border origin. This is useful for drawing borders.
func (BoxModel) ClampScrollOffsetX ¶
ClampScrollOffsetX clamps the given offset to valid horizontal scroll bounds.
func (BoxModel) ClampScrollOffsetY ¶
ClampScrollOffsetY clamps the given offset to valid vertical scroll bounds.
func (BoxModel) ContentBox ¶
ContentBox returns the computed content area as a Rect. Content dimensions are computed by subtracting padding and border from the border-box. Use for: laying out content within the available space. The position is relative to the margin box origin.
func (BoxModel) ContentHeight ¶
ContentHeight computes the content height by subtracting padding and border from the border-box. Returns 0 if insets exceed the border-box height.
func (BoxModel) ContentOrigin ¶
ContentOrigin returns the offset from the margin box origin to the content origin. This is useful for positioning content within a rendered box.
func (BoxModel) ContentWidth ¶
ContentWidth computes the content width by subtracting padding and border from the border-box. Returns 0 if insets exceed the border-box width.
func (BoxModel) EffectiveVirtualHeight ¶
EffectiveVirtualHeight returns the virtual content height. Returns computed ContentHeight() if VirtualHeight is not set (0).
func (BoxModel) EffectiveVirtualWidth ¶
EffectiveVirtualWidth returns the virtual content width. Returns computed ContentWidth() if VirtualWidth is not set (0).
func (BoxModel) IsScrollable ¶
IsScrollable returns true if scrolling is possible in either direction.
func (BoxModel) IsScrollableX ¶
IsScrollableX returns true if horizontal scrolling is possible. This is true when virtual width exceeds the usable content width (accounting for vertical scrollbar space if vertical scrolling is enabled).
func (BoxModel) IsScrollableY ¶
IsScrollableY returns true if vertical scrolling is possible. This is true when virtual height exceeds the usable content height (accounting for horizontal scrollbar space if horizontal scrolling is enabled).
func (BoxModel) MarginBox ¶
MarginBox returns the margin box as a Rect. This is always positioned at (0,0) since it's the outermost boundary.
func (BoxModel) MarginBoxHeight ¶
MarginBoxHeight returns the height of the margin box (border-box plus vertical margin). This is the total height including margin.
func (BoxModel) MarginBoxWidth ¶
MarginBoxWidth returns the width of the margin box (border-box plus horizontal margin). This is the total width including margin.
func (BoxModel) MaxScrollX ¶
MaxScrollX returns the maximum valid horizontal scroll offset. Returns 0 if not scrollable.
func (BoxModel) MaxScrollY ¶
MaxScrollY returns the maximum valid vertical scroll offset. Returns 0 if not scrollable.
func (BoxModel) PaddingBox ¶
PaddingBox returns the padding box as a Rect. The position is relative to the margin box origin.
func (BoxModel) PaddingBoxHeight ¶
PaddingBoxHeight returns the height of the padding box (border-box minus border).
func (BoxModel) PaddingBoxWidth ¶
PaddingBoxWidth returns the width of the padding box (border-box minus border).
func (BoxModel) TotalHorizontalInset ¶
TotalHorizontalInset returns the total horizontal space taken by all insets.
func (BoxModel) TotalVerticalInset ¶
TotalVerticalInset returns the total vertical space taken by all insets.
func (BoxModel) UsableContentBox ¶
UsableContentBox returns the content area available for child widgets. This subtracts space reserved for scrollbars from the computed content area:
- Vertical scrollbar (ScrollbarWidth) reduces width when IsScrollableY()
- Horizontal scrollbar (ScrollbarHeight) reduces height when IsScrollableX()
Use for: laying out children, determining available space for content. The position is relative to the margin box origin.
func (BoxModel) Validate ¶
func (b BoxModel) Validate()
Validate checks that all BoxModel fields have valid values. Panics if any field has an invalid value (negative dimensions, invalid constraints, etc.). Note: Negative margins are allowed (CSS behavior) for overlapping/pull effects.
func (BoxModel) VirtualContentRect ¶
VirtualContentRect returns the full virtual content area. For non-scrollable boxes, this equals the content dimensions.
func (BoxModel) VisibleContentRect ¶
VisibleContentRect returns the visible portion of the virtual content. The rect is in virtual content coordinates (not screen coordinates). For non-scrollable boxes, this returns a rect starting at (0,0).
func (BoxModel) WithBorder ¶
func (b BoxModel) WithBorder(border EdgeInsets) BoxModel
WithBorder returns a new BoxModel with the specified border. Panics if any border value is negative.
func (BoxModel) WithClampedScrollOffset ¶
WithClampedScrollOffset returns a new BoxModel with scroll offsets clamped to valid bounds.
func (BoxModel) WithMargin ¶
func (b BoxModel) WithMargin(margin EdgeInsets) BoxModel
WithMargin returns a new BoxModel with the specified margin. Panics if any margin value is negative.
func (BoxModel) WithPadding ¶
func (b BoxModel) WithPadding(padding EdgeInsets) BoxModel
WithPadding returns a new BoxModel with the specified padding. Panics if any padding value is negative.
func (BoxModel) WithScrollOffset ¶
WithScrollOffset returns a new BoxModel with the specified scroll offset.
func (BoxModel) WithScrollbarHeight ¶
WithScrollbarHeight returns a new BoxModel with the specified horizontal scrollbar height. Panics if height is negative.
func (BoxModel) WithScrollbarWidth ¶
WithScrollbarWidth returns a new BoxModel with the specified vertical scrollbar width. Panics if width is negative.
func (BoxModel) WithScrollbars ¶
WithScrollbars returns a new BoxModel with the specified scrollbar dimensions. Panics if width or height is negative.
func (BoxModel) WithSize ¶
WithSize returns a new BoxModel with the specified border-box dimensions. Negative values are clamped to 0, since dimensions often come from layout calculations that can legitimately underflow (e.g., when the terminal is resized too small).
func (BoxModel) WithVirtualSize ¶
WithVirtualSize returns a new BoxModel with virtual content dimensions for scrolling. Negative values are clamped to 0, since virtual dimensions may come from layout calculations that can legitimately underflow.
type BoxNode ¶
type BoxNode struct {
// Fixed size (if MeasureFunc is nil).
// These are border-box dimensions.
Width int
Height int
// Node's own min/max constraints.
// These are merged with parent constraints to form effective constraints.
// NOTE: 0 means "no constraint" (unconstrained), not "zero size".
// This is a pragmatic trade-off for API simplicity in TUI contexts.
// If you need a box that shrinks to zero, use MeasureFunc or set Width/Height directly.
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Insets
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Expand flags force the box to fill available space on that axis.
// When true and Width/Height is 0, the box expands to MaxWidth/MaxHeight.
// This is used when a widget's dimension is Flex() or Percent().
ExpandWidth bool
ExpandHeight bool
// MeasureFunc for dynamic sizing (overrides Width/Height if set).
// Receives CONTENT-BOX constraints (available space for content, after subtracting padding/border).
// Returns CONTENT-BOX dimensions (just the content size, not including padding/border).
// ComputeLayout adds padding/border back automatically.
// This keeps MeasureFunc simple - it only measures content, not decoration.
// Can use constraints.IsTightWidth() etc. to detect forced vs flexible sizing.
MeasureFunc func(constraints Constraints) (width, height int)
}
BoxNode is a leaf node representing a fixed or measured box. It implements LayoutNode and produces a BoxModel with no children.
func (*BoxNode) ComputeLayout ¶
func (b *BoxNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the BoxNode's layout given parent constraints. It returns a ComputedLayout with the resulting BoxModel and no children.
type ColumnNode ¶
type ColumnNode struct {
// Spacing is the gap between children.
Spacing int
// MainAlign controls vertical distribution of children.
MainAlign MainAxisAlignment
// CrossAlign controls horizontal positioning of children.
CrossAlign CrossAxisAlignment
// Children to lay out.
Children []LayoutNode
// Container's own insets (optional).
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Container's own size constraints (optional, 0 means unconstrained).
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Expand flags force the container to fill available space on that axis.
// Used when the widget's dimension is Flex() to mean "fill available space".
ExpandWidth bool
ExpandHeight bool
// Preserve flags indicate the container should resist stretching on that axis.
// Used when the widget's dimension is explicitly Auto to mean "fit content, don't stretch".
PreserveWidth bool
PreserveHeight bool
}
ColumnNode lays out children vertically (top to bottom). This is a thin wrapper around LinearNode with Axis=Vertical.
func (*ColumnNode) ComputeLayout ¶
func (c *ColumnNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the column layout by delegating to LinearNode.
func (*ColumnNode) PreservesHeight ¶
func (c *ColumnNode) PreservesHeight() bool
PreservesHeight implements SizePreserver.
func (*ColumnNode) PreservesWidth ¶
func (c *ColumnNode) PreservesWidth() bool
PreservesWidth implements SizePreserver.
type ComputedLayout ¶
type ComputedLayout struct {
// Box is the computed BoxModel for this node.
Box BoxModel
// Children contains the positioned child layouts.
// nil for leaf nodes.
Children []PositionedChild
}
ComputedLayout is the result of layout computation. It contains the computed BoxModel for a node and its positioned children (if any).
type Constraints ¶
Constraints represents the min/max size constraints passed from parent to child. This allows expressing tight constraints (min == max), loose constraints (min = 0), and range constraints (min < max).
func Loose ¶
func Loose(maxWidth, maxHeight int) Constraints
Loose creates constraints where the node can be any size from 0 up to the given max.
func Tight ¶
func Tight(width, height int) Constraints
Tight creates constraints where the node must be exactly the given size. Both min and max are set to the same value.
func TightHeight ¶
func TightHeight(maxWidth, height int) Constraints
TightHeight creates constraints with a fixed height but flexible width.
func TightWidth ¶
func TightWidth(width, maxHeight int) Constraints
TightWidth creates constraints with a fixed width but flexible height.
func Unbounded ¶
func Unbounded() Constraints
Unbounded creates constraints with no limits. Useful for measuring intrinsic size.
func (Constraints) Constrain ¶
func (c Constraints) Constrain(width, height int) (int, int)
Constrain clamps the given width and height to satisfy these constraints.
func (Constraints) IsTight ¶
func (c Constraints) IsTight() bool
IsTight returns true if both dimensions are tightly constrained (min == max).
func (Constraints) IsTightHeight ¶
func (c Constraints) IsTightHeight() bool
IsTightHeight returns true if height is tightly constrained (min == max).
func (Constraints) IsTightWidth ¶
func (c Constraints) IsTightWidth() bool
IsTightWidth returns true if width is tightly constrained (min == max).
func (Constraints) WithNodeConstraints ¶
func (c Constraints) WithNodeConstraints(minW, maxW, minH, maxH int) Constraints
WithNodeConstraints applies a node's own min/max constraints to parent constraints. Node constraints use 0 to mean "unconstrained".
Precedence rules:
- Node's explicit MaxWidth/MaxHeight takes precedence (represents user intent like Width: Cells(4))
- If parent's min exceeds node's explicit max, parent's min is lowered to match
- Node's min is clamped to parent's max (can't exceed available space)
- Without explicit node max, parent constraints pass through (e.g., stretch works)
This matches Flutter/CSS behavior where explicit size constraints are respected even when a parent wants to stretch the child.
If the resulting constraints are invalid (min > max), min wins. This handles user configuration errors like MinHeight: 60, MaxHeight: 40.
type CrossAxisAlignment ¶
type CrossAxisAlignment int
CrossAxisAlignment controls how children are positioned along the cross axis.
const ( // CrossAxisStart aligns children at the start of the cross axis. CrossAxisStart CrossAxisAlignment = iota // CrossAxisCenter centers children along the cross axis. CrossAxisCenter // CrossAxisEnd aligns children at the end of the cross axis. CrossAxisEnd // CrossAxisStretch stretches children to fill the cross axis. // Children are re-laid out with tight cross-axis constraints. CrossAxisStretch )
type DockNode ¶
type DockNode struct {
// Docked children for each edge.
// Multiple children on the same edge are processed in order,
// each consuming space from the remaining area.
Top []LayoutNode
Bottom []LayoutNode
Left []LayoutNode
Right []LayoutNode
// Body fills the remaining space after all edges are processed.
// Optional - if nil, the dock only contains edge children.
Body LayoutNode
// DockOrder specifies the order in which edges are processed.
// If nil or empty, defaults to [DockTop, DockBottom, DockLeft, DockRight].
// Each edge in this list is processed once, laying out all children for that edge.
DockOrder []DockEdge
// Container's own insets (optional).
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Container's own size constraints (optional, 0 means unconstrained).
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
}
DockNode lays out children by docking them to edges (WPF DockPanel style). Each docked child consumes space from the remaining area in order. The body fills whatever space remains after all edges are processed.
Processing order is determined by DockOrder. If not specified, the default order is: Top, Bottom, Left, Right. This matches WPF's LastChildFill behavior when Body is set.
Example: With Top=[header], Bottom=[footer], Body=content:
- header takes full width at top, consumes its height
- footer takes full width at bottom, consumes its height
- content fills the remaining space in the middle
func (*DockNode) ComputeLayout ¶
func (d *DockNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the layout for this dock container.
type EdgeInsets ¶
type EdgeInsets struct {
Top, Right, Bottom, Left int
}
EdgeInsets represents spacing around the four edges of a box.
func EdgeInsetsAll ¶
func EdgeInsetsAll(value int) EdgeInsets
EdgeInsetsAll creates EdgeInsets with the same value for all sides.
func EdgeInsetsTRBL ¶
func EdgeInsetsTRBL(top, right, bottom, left int) EdgeInsets
EdgeInsetsTRBL creates EdgeInsets with individual values for each side.
func EdgeInsetsXY ¶
func EdgeInsetsXY(horizontal, vertical int) EdgeInsets
EdgeInsetsXY creates EdgeInsets with separate horizontal and vertical values.
func (EdgeInsets) Horizontal ¶
func (e EdgeInsets) Horizontal() int
Horizontal returns the total horizontal inset (Left + Right).
func (EdgeInsets) Vertical ¶
func (e EdgeInsets) Vertical() int
Vertical returns the total vertical inset (Top + Bottom).
type FlexNode ¶
type FlexNode struct {
// Child is the wrapped layout node.
Child LayoutNode
// Flex is the proportion of remaining space this child should receive.
// Must be > 0. Defaults to 1 if not specified.
// A child with Flex: 2 gets twice the space of a sibling with Flex: 1.
Flex float64
}
FlexNode wraps a child with flex behavior for use in LinearNode. It's "transparent" - it doesn't produce its own box, just metadata for the parent.
FlexNode is only meaningful as a direct child of LinearNode (Row/Column). The parent inspects FlexNode.Flex to determine space distribution, then layouts FlexNode.Child with the calculated constraints.
The output ComputedLayout is the child's layout - FlexNode doesn't appear in the layout tree. This ensures output indices match input indices, which is critical for widget-to-layout mapping during rendering.
Example:
row := &RowNode{
Children: []LayoutNode{
&BoxNode{Width: 100}, // Fixed 100 cells
&FlexNode{Flex: 1, Child: a}, // Gets 1/3 of remaining
&FlexNode{Flex: 2, Child: b}, // Gets 2/3 of remaining
},
}
FlexNode + MainAxisAlignment Composition: Flex children absorb remaining space first, then MainAxisAlignment distributes any leftover (e.g., if flex children hit MaxWidth). This matches CSS flexbox behavior where flex-grow and justify-content interact.
func IsFlexNode ¶
func IsFlexNode(node LayoutNode) (*FlexNode, bool)
IsFlexNode returns true if the node is a FlexNode. Used by LinearNode to identify flex children during layout.
func (*FlexNode) ComputeLayout ¶
func (f *FlexNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout delegates to the child. This is only called if FlexNode is used outside a LinearNode context, in which case the Flex value is ignored and the child is measured normally.
type HorizontalAlignment ¶
type HorizontalAlignment int
HorizontalAlignment specifies horizontal positioning within available space.
const ( // HAlignStart aligns content at the start (left). HAlignStart HorizontalAlignment = iota // HAlignCenter centers content horizontally. HAlignCenter // HAlignEnd aligns content at the end (right). HAlignEnd )
type LayoutNode ¶
type LayoutNode interface {
// ComputeLayout computes this node's size and positions all children.
// Constraints specify the min/max bounds the node must fit within.
// Returns a ComputedLayout containing the resulting BoxModel and positioned children.
ComputeLayout(constraints Constraints) ComputedLayout
}
LayoutNode represents a node in the layout tree. It can be a leaf (BoxNode) or a container (RowNode, ColumnNode, etc.).
type LinearNode ¶
type LinearNode struct {
// Axis determines the layout direction.
// Horizontal: children flow left-to-right (like Row)
// Vertical: children flow top-to-bottom (like Column)
Axis Axis
// Spacing is the gap between children along the main axis.
Spacing int
// MainAlign controls distribution of children along the main axis.
MainAlign MainAxisAlignment
// CrossAlign controls positioning of children along the cross axis.
CrossAlign CrossAxisAlignment
// Children to lay out.
Children []LayoutNode
// Container's own insets (optional).
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Container's own size constraints (optional, 0 means unconstrained).
// These are border-box constraints, applied after content-based sizing
// but before parent constraints. Allows containers to enforce minimum
// sizes independent of their children.
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Expand flags force the container to fill available space on that axis.
// When true, MinWidth/MinHeight is set to MaxWidth/MaxHeight from constraints,
// creating tight constraints that force expansion instead of shrink-wrapping.
// This is used when a widget's dimension is Flex() to mean "fill available space".
ExpandWidth bool
ExpandHeight bool
// Preserve flags indicate the container should resist stretching on that axis.
// When true, the container keeps its natural (content-fitted) size even when
// a parent tries to stretch it via CrossAxisStretch. This is used when a
// widget's dimension is explicitly Auto to mean "fit content, don't stretch".
PreserveWidth bool
PreserveHeight bool
}
LinearNode lays out children along a single axis (horizontal or vertical). This is the shared implementation for Row (Horizontal) and Column (Vertical). The algorithm operates on "main axis" and "cross axis" concepts, making the code axis-agnostic.
func (*LinearNode) ComputeLayout ¶
func (l *LinearNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the layout for this linear container.
func (*LinearNode) PreservesHeight ¶
func (l *LinearNode) PreservesHeight() bool
PreservesHeight implements SizePreserver. Returns true if this node should resist vertical stretching.
func (*LinearNode) PreservesWidth ¶
func (l *LinearNode) PreservesWidth() bool
PreservesWidth implements SizePreserver. Returns true if this node should resist horizontal stretching.
type MainAxisAlignment ¶
type MainAxisAlignment int
MainAxisAlignment controls how children are distributed along the main axis when there is extra space available.
const ( // MainAxisStart packs children at the start of the main axis. MainAxisStart MainAxisAlignment = iota // MainAxisCenter centers children along the main axis. MainAxisCenter // MainAxisEnd packs children at the end of the main axis. MainAxisEnd // MainAxisSpaceBetween distributes extra space evenly between children. // First child at start, last child at end, equal gaps between. MainAxisSpaceBetween // MainAxisSpaceAround distributes extra space evenly around children. // Each child gets equal space on both sides (gaps between children are 2x edge gaps). MainAxisSpaceAround // MainAxisSpaceEvenly distributes extra space so all gaps (including edges) are equal. MainAxisSpaceEvenly )
type PercentNode ¶
type PercentNode struct {
// Child is the wrapped layout node.
Child LayoutNode
// Percent is the percentage of parent space (0-100+).
// Values over 100 are allowed and will cause overflow.
Percent float64
// Axis determines which constraint dimension to use for percentage calculation.
// Horizontal uses MaxWidth, Vertical uses MaxHeight.
Axis Axis
}
PercentNode wraps a child with percentage-based sizing for use in LinearNode. Unlike FlexNode which distributes remaining space, PercentNode calculates a fixed size as a percentage of the parent's available space.
PercentNode is only meaningful as a direct child of LinearNode (Row/Column). The parent uses PercentNode during the first pass (non-flex measurement), where the percentage is resolved to a fixed size based on constraints.
Example:
row := &RowNode{
Children: []LayoutNode{
&PercentNode{Percent: 30, Axis: Horizontal, Child: a}, // 30% of parent width
&PercentNode{Percent: 70, Axis: Horizontal, Child: b}, // 70% of parent width
},
}
func IsPercentNode ¶
func IsPercentNode(node LayoutNode) (*PercentNode, bool)
IsPercentNode returns true if the node is a PercentNode. Used by LinearNode to identify percentage children during layout.
func (*PercentNode) ComputeLayout ¶
func (p *PercentNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout calculates the fixed size from the percentage and applies tight constraints on the main axis while preserving cross-axis constraints.
type PositionedChild ¶
type PositionedChild struct {
// X, Y is the child's border-box position relative to parent's content-area.
X, Y int
// Layout is the child's computed layout (recursive).
Layout ComputedLayout
}
PositionedChild is a child with its computed position.
Coordinate system: X and Y specify the child's border-box position relative to the parent's content-area origin (after padding and border).
The renderer must add the parent's padding and border offsets when translating to screen coordinates. For example:
screenX := parentScreenX + parent.Box.Padding.Left + parent.Box.Border.Left + child.X screenY := parentScreenY + parent.Box.Padding.Top + parent.Box.Border.Top + child.Y
Child margins are already accounted for in X,Y: if a child has Margin.Left=5, its X is offset by 5 from where its margin-box starts. This means X,Y point directly to where the child's visible border-box begins.
type RowNode ¶
type RowNode struct {
// Spacing is the gap between children.
Spacing int
// MainAlign controls horizontal distribution of children.
MainAlign MainAxisAlignment
// CrossAlign controls vertical positioning of children.
CrossAlign CrossAxisAlignment
// Children to lay out.
Children []LayoutNode
// Container's own insets (optional).
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Container's own size constraints (optional, 0 means unconstrained).
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Expand flags force the container to fill available space on that axis.
// Used when the widget's dimension is Flex() to mean "fill available space".
ExpandWidth bool
ExpandHeight bool
// Preserve flags indicate the container should resist stretching on that axis.
// Used when the widget's dimension is explicitly Auto to mean "fit content, don't stretch".
PreserveWidth bool
PreserveHeight bool
}
RowNode lays out children horizontally (left to right). This is a thin wrapper around LinearNode with Axis=Horizontal.
func (*RowNode) ComputeLayout ¶
func (r *RowNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the row layout by delegating to LinearNode.
func (*RowNode) PreservesHeight ¶
PreservesHeight implements SizePreserver.
func (*RowNode) PreservesWidth ¶
PreservesWidth implements SizePreserver.
type ScrollableNode ¶
type ScrollableNode struct {
// Child is the content to scroll.
Child LayoutNode
// ScrollOffsetX is the horizontal scroll offset in cells.
ScrollOffsetX int
// ScrollOffsetY is the vertical scroll offset in cells.
ScrollOffsetY int
// ScrollbarWidth is the space reserved for a vertical scrollbar (default 1).
// Set to 0 to disable vertical scrollbar space reservation.
ScrollbarWidth int
// ScrollbarHeight is the space reserved for a horizontal scrollbar (default 1).
// Set to 0 to disable horizontal scrollbar space reservation.
ScrollbarHeight int
// AlwaysReserveScrollbarSpace forces scrollbar space reservation even when
// content doesn't require scrolling. This prevents layout "jumping" when
// content crosses the scrollable threshold.
AlwaysReserveScrollbarSpace bool
// Container's own insets (optional).
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Container's own size constraints (optional, 0 means unconstrained).
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
}
ScrollableNode wraps a child and applies viewport/scrolling semantics. Unlike DockNode (which partitions space), ScrollableNode clips content that exceeds the viewport and tracks virtual dimensions.
The layout algorithm: 1. Reserve scrollbar space if needed (based on AlwaysReserveScrollbarSpace or content size) 2. Measure child with both bounded and unbounded heights, then choose virtual content size 3. If virtual size exceeds viewport, scrollbar space is reserved 4. Build BoxModel with VirtualHeight/ScrollOffsetY populated
Example:
scrollable := &ScrollableNode{
Child: longContent,
ScrollOffsetY: 10,
ScrollbarWidth: 1,
}
result := scrollable.ComputeLayout(Tight(80, 24))
// result.Box.VirtualHeight may exceed 24
// result.Box.IsScrollableY() returns true if content overflows
func (*ScrollableNode) ComputeLayout ¶
func (s *ScrollableNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the scrollable layout. The child is measured with both viewport-bounded and unbounded height probes to derive a stable virtual size. The viewport is then constrained to the parent constraints, and scroll offsets are applied.
type SizePreserver ¶
type SizePreserver interface {
// PreservesWidth returns true if the node should resist horizontal stretching.
PreservesWidth() bool
// PreservesHeight returns true if the node should resist vertical stretching.
PreservesHeight() bool
}
SizePreserver is implemented by nodes that can resist stretching on specific axes. This is used when a widget has an explicit Auto dimension, meaning "fit content, don't stretch beyond that" rather than the default "no preference, parent decides".
type SplitPaneNode ¶
type SplitPaneNode struct {
First LayoutNode
Second LayoutNode
Axis Axis
Position float64 // 0.0-1.0 along the main axis
DividerSize int
MinPaneSize int
// Container insets and constraints.
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Preserve flags resist cross-axis stretching when Auto is explicitly set.
PreserveWidth bool
PreserveHeight bool
}
SplitPaneNode lays out two children separated by a divider. The container always fills the available space in its constraints.
func (*SplitPaneNode) ComputeLayout ¶
func (n *SplitPaneNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the layout for a split pane container.
func (*SplitPaneNode) PreservesHeight ¶
func (n *SplitPaneNode) PreservesHeight() bool
PreservesHeight indicates whether this node resists vertical stretching.
func (*SplitPaneNode) PreservesWidth ¶
func (n *SplitPaneNode) PreservesWidth() bool
PreservesWidth indicates whether this node resists horizontal stretching.
type StackChild ¶
type StackChild struct {
Node LayoutNode // The child's layout node
IsPositioned bool // True if this child uses edge-based positioning
// Edge offsets for positioned children (nil = not constrained).
// If both Top and Bottom are set, child height = stack height - top - bottom.
// If both Left and Right are set, child width = stack width - left - right.
Top *int
Right *int
Bottom *int
Left *int
}
StackChild represents a child within a Stack, with optional positioning info.
type StackNode ¶
type StackNode struct {
Children []StackChild // Children to overlay
// Default alignment for non-positioned children.
DefaultHAlign HorizontalAlignment
DefaultVAlign VerticalAlignment
// Insets
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Node's own min/max constraints.
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
// Expand flags for flex sizing.
ExpandWidth bool
ExpandHeight bool
}
StackNode overlays children on top of each other. First child is at the bottom, last child is on top.
Stack sizes itself based on the largest non-positioned child. Positioned children do not affect Stack's size (they can overflow).
func (*StackNode) ComputeLayout ¶
func (s *StackNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the StackNode's layout given parent constraints. Unlike other containers, Stack positions children relative to its border-box, not content-box. This allows Positioned children to overlap borders.
type TextNode ¶
type TextNode struct {
// Content is the text to measure and display.
Content string
// Wrap controls how text wraps when it exceeds available width.
// Default is WrapNone (no wrapping).
Wrap WrapMode
// Insets (passed through to BoxNode)
Padding EdgeInsets
Border EdgeInsets
Margin EdgeInsets
// Optional constraints (passed through to BoxNode).
// NOTE: 0 means "no constraint" (unconstrained), not "zero size".
MinWidth int
MaxWidth int
MinHeight int
MaxHeight int
}
TextNode is a leaf node for text content that may wrap. It's a thin wrapper around BoxNode that provides a declarative API for text.
Rather than duplicating BoxNode's constraint/inset handling logic, TextNode creates an internal BoxNode with a MeasureFunc that calls MeasureText. This ensures bug fixes to BoxNode automatically apply to TextNode.
Example:
node := &TextNode{
Content: "Hello, World!",
Wrap: WrapWord,
Padding: EdgeInsets{Top: 1, Right: 2, Bottom: 1, Left: 2},
}
result := node.ComputeLayout(Loose(80, 24))
func (*TextNode) ComputeLayout ¶
func (t *TextNode) ComputeLayout(constraints Constraints) ComputedLayout
ComputeLayout computes the layout for this text node. It delegates to a BoxNode with a MeasureFunc that calls MeasureText.
type VerticalAlignment ¶
type VerticalAlignment int
VerticalAlignment specifies vertical positioning within available space.
const ( // VAlignTop aligns content at the top. VAlignTop VerticalAlignment = iota // VAlignCenter centers content vertically. VAlignCenter // VAlignBottom aligns content at the bottom. VAlignBottom )
type WrapMode ¶
type WrapMode int
WrapMode controls text wrapping behavior.
const ( // WrapNone disables wrapping - text may overflow available width. WrapNone WrapMode = iota // WrapWord wraps at word boundaries (spaces). // Falls back to character breaks for words longer than available width. WrapWord // WrapChar wraps at exact character boundaries. // Useful for CJK text or when precise character-level wrapping is needed. WrapChar )