Layout System
OSUI's layout system is designed for flexibility and responsiveness in a grid-based terminal environment. It abstracts away absolute pixel calculations by allowing developers to define layout rules declaratively using Transform
, Position
, and Dimension
components. The system then resolves these rules into concrete coordinates and sizes during the rendering phase.
Core Principles
- Parent-Child Relationship: Layout is always relative to the parent container. A child element's position and size are determined by its own
Transform
and the dimensions of its immediate parent. - Two-Phase Calculation:
- Dimension Resolution: First,
Dimension
rules (Full
,Content
,Const
) are applied to determine the concrete width and height of an element. - Position Resolution: Second,
Position
rules (Const
,Center
,End
) are applied to determine the concretex
andy
coordinates, using the newly resolved dimensions.
- Dimension Resolution: First,
- Content-Based Sizing: Elements can automatically size themselves to fit their content (
Dimension::Content
), allowing for dynamic UI. - Padding and Margin: Distinct concepts for internal spacing (padding) and external offset (margin).
Key Components of Layout
Transform
The central component for defining layout rules. It encapsulates all the properties that influence an element's position and size.
x: Position
,y: Position
: Define alignment along horizontal and vertical axes.width: Dimension
,height: Dimension
: Define sizing along horizontal and vertical axes.px: u16
,py: u16
: Padding - internal space between the element's border and its content/children. This increases the overall size of the element.mx: i32
,my: i32
: Margin - an offset applied after the element's position is calculated. This creates space around the element relative to its parent's edges. Can be negative for overlap.
(See Reference: Style API - Transform for full details)
Position
Enum
Determines the x
or y
coordinate.
Const(u16)
: Absolute coordinate from the top-left (0,0).Center
: Centers the element within the parent's available space on that axis.End
: Aligns the element to the right or bottom edge of the parent.
(See Reference: Style API - Position for full details)
Dimension
Enum
Determines the width
or height
.
Full
: Takes up all available space from the parent on that axis.Content
: Sizes itself to fit its content (text) or children. This is dynamic.Const(u16)
: Fixed size in terminal cells.
(See Reference: Style API - Dimension for full details)
RawTransform
Struct
This is the internal, resolved representation of a Transform
. After all calculations, a Transform
's declarative rules are converted into a RawTransform
with concrete u16
values for x
, y
, width
, height
, px
, py
. This RawTransform
is then used by the RenderScope
for actual drawing.
(See Reference: Style API - RawTransform for full details)
Layout Calculation Flow (Simplified)
The layout process happens during the Screen::render()
cycle, primarily managed by the RenderScope
and parent Element::after_render
methods.
-
Initialize
RenderScope
: For each top-level widget (or for each child within a container element), a new or clearedRenderScope
is prepared. Itsparent_width
andparent_height
are set to the available space (either terminal size or the parent element's resolved size). -
Apply
Transform
(Phase 1: Dimensions):- The
Transform
component attached to the current widget is accessed. Transform::use_dimensions()
is called. This method takes theRenderScope
'sparent_width
andparent_height
and resolves thewidth
andheight
Dimension
rules into concreteu16
values.- If
Dimension::Full
, it takes theparent_width
/height
. - If
Dimension::Const(n)
, it takesn
. - If
Dimension::Content
, it's initially set to0
or left unchanged; its final value will be determined by theElement::render
method (based on text size) or byElement::after_render
(based on children's size).
- If
- The
-
Element Renders Content (
Element::render
):- The
Element::render
method is called. It usesRenderScope::draw_text()
,draw_rect()
, etc., to queue drawing commands. - Crucially, these
draw_*
methods automatically update theRenderScope
's internalRawTransform.width
andheight
to be at least the size of the drawn content. This is howDimension::Content
gets its actual size. Element
s can also explicitly usescope.use_area(w, h)
to hint their minimum desired size.
- The
-
Apply
Transform
(Phase 2: Position):- After the
Element::render
has potentially updated theRawTransform
'swidth
andheight
(forContent
dimensions),Transform::use_position()
is called. - This method takes the now-resolved
RawTransform.width
andheight
, theRenderScope
'sparent_width
/height
, and theTransform
'smx
/my
(margins) to calculate the finalRawTransform.x
andy
.Position::Const(n)
: Setsx
ory
ton
.Position::Center
: Calculates(parent_size - element_size) / 2
.Position::End
: Calculatesparent_size - element_size
.mx
,my
are then added or subtracted to these calculated base positions.
- After the
-
Child Rendering (
Element::after_render
for containers):- For container elements (
Div
,FlexRow
,FlexCol
), theirafter_render
method then steps in. - Before rendering each child, the parent container performs a critical step: it sets the
RenderScope
'sparent_width
andparent_height
to its own newly resolvedRawTransform.width
andheight
. This creates a new layout context for the child. - The parent also shifts the
RawTransform.x
andy
of the child by its own resolvedx
,y
, andpx
,py
(padding), ensuring children are drawn relative to the parent's padded content area. - The entire process (steps 2-5) recursively repeats for each child.
- After all children are rendered, the parent element might update its own
RawTransform.width
andheight
based on the maximum extent of its children, especially if itsDimension
wasContent
.
- For container elements (
-
Final Draw (
RenderScope::draw
): Once all elements and their children have queued their commands and positions are finalized,RenderScope::draw()
translates theseRawTransform
-based instructions into actual terminal ANSI escape codes and prints them.
Flex Layouts (FlexRow
, FlexCol
)
FlexRow
and FlexCol
elements implement a simpler sequential layout model on top of the core Transform
system.
FlexRow
(Column-like): Children are stacked vertically. Each child'sy
position is implicitly determined by the previous child's height plus thegap
. Itsx
position is usually0
relative to theFlexRow
.FlexCol
(Row-like): Children are laid out horizontally. Each child'sx
position is implicitly determined by the previous child's width plus thegap
. Itsy
position is usually0
relative to theFlexCol
.
These elements internally manage the cumulative position (v
variable in source) for their children to ensure correct sequential placement.
By understanding this hierarchical and two-phase layout resolution, you can effectively predict and control how your OSUI elements will appear on the terminal.