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
Transformand the dimensions of its immediate parent. - Two-Phase Calculation:
- Dimension Resolution: First,
Dimensionrules (Full,Content,Const) are applied to determine the concrete width and height of an element. - Position Resolution: Second,
Positionrules (Const,Center,End) are applied to determine the concretexandycoordinates, 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 clearedRenderScopeis prepared. Itsparent_widthandparent_heightare set to the available space (either terminal size or the parent element's resolved size). -
Apply
Transform(Phase 1: Dimensions):- The
Transformcomponent attached to the current widget is accessed. Transform::use_dimensions()is called. This method takes theRenderScope'sparent_widthandparent_heightand resolves thewidthandheightDimensionrules into concreteu16values.- If
Dimension::Full, it takes theparent_width/height. - If
Dimension::Const(n), it takesn. - If
Dimension::Content, it's initially set to0or left unchanged; its final value will be determined by theElement::rendermethod (based on text size) or byElement::after_render(based on children's size).
- If
- The
-
Element Renders Content (
Element::render):- The
Element::rendermethod is called. It usesRenderScope::draw_text(),draw_rect(), etc., to queue drawing commands. - Crucially, these
draw_*methods automatically update theRenderScope's internalRawTransform.widthandheightto be at least the size of the drawn content. This is howDimension::Contentgets its actual size. Elements can also explicitly usescope.use_area(w, h)to hint their minimum desired size.
- The
-
Apply
Transform(Phase 2: Position):- After the
Element::renderhas potentially updated theRawTransform'swidthandheight(forContentdimensions),Transform::use_position()is called. - This method takes the now-resolved
RawTransform.widthandheight, theRenderScope'sparent_width/height, and theTransform'smx/my(margins) to calculate the finalRawTransform.xandy.Position::Const(n): Setsxoryton.Position::Center: Calculates(parent_size - element_size) / 2.Position::End: Calculatesparent_size - element_size.mx,myare then added or subtracted to these calculated base positions.
- After the
-
Child Rendering (
Element::after_renderfor containers):- For container elements (
Div,FlexRow,FlexCol), theirafter_rendermethod then steps in. - Before rendering each child, the parent container performs a critical step: it sets the
RenderScope'sparent_widthandparent_heightto its own newly resolvedRawTransform.widthandheight. This creates a new layout context for the child. - The parent also shifts the
RawTransform.xandyof 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.widthandheightbased on the maximum extent of its children, especially if itsDimensionwasContent.
- 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'syposition is implicitly determined by the previous child's height plus thegap. Itsxposition is usually0relative to theFlexRow.FlexCol(Row-like): Children are laid out horizontally. Each child'sxposition is implicitly determined by the previous child's width plus thegap. Itsyposition is usually0relative 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.