Layout and Styling
OSUI provides a robust layout and styling system to control the appearance and positioning of your UI elements. This system is built around several key concepts: Transform, Position, Dimension, Style, and Background.
1. Transform: Positioning and Sizing Rules
The Transform component is used to define how a widget should be positioned and sized relative to its parent. It specifies declarative rules rather than absolute pixel values, allowing for flexible and responsive layouts.
You typically create and attach a Transform using the @ component syntax in rsx! or by calling widget.component(Transform::new()...).
use osui::prelude::*;
// Default transform: (0,0) position, content-sized
let default_transform = Transform::new();
// Centered horizontally and vertically, content-sized
let centered_transform = Transform::center();
rsx! {
@Transform::new().padding(2, 1).dimensions(30, 5);
Div { "A fixed-size div with padding." }
@Transform::new().right().margin(5, 0);
Div { "Aligned to the right with a 5-cell horizontal margin." }
@Transform::new().bottom().margin(0, 2);
Div { "Aligned to the bottom with a 2-cell vertical margin." }
}
Transform Fields:
x: Position: Horizontal position relative to the parent.y: Position: Vertical position relative to the parent.mx: i32: Horizontal margin (offset) from the calculatedxposition. Can be negative for overlap.my: i32: Vertical margin (offset) from the calculatedyposition. Can be negative for overlap.px: u16: Horizontal padding (internal spacing) around the content.py: u16: Vertical padding (internal spacing) around the content.width: Dimension: Rule for the widget's width.height: Dimension: Rule for the widget's height.
Chainable Methods for Transform
Transform provides several convenient chainable methods for common layout patterns:
Transform::new(): Creates a default transform at(0,0)withContentdimensions and no padding/margin.Transform::center(): Creates a transform centered both horizontally and vertically.Transform::bottom(self): SetsytoPosition::End.Transform::right(self): SetsxtoPosition::End.Transform::margin(self, x: i32, y: i32): Setsmxandmy.Transform::padding(self, x: u16, y: u16): Setspxandpy.Transform::dimensions(self, width: u16, height: u16): SetswidthandheighttoDimension::Const.
2. Position: Horizontal and Vertical Alignment
Position defines how an element is placed along an axis relative to its parent's boundaries.
pub enum Position {
/// Fixed position in cells from the origin (top-left).
Const(u16),
/// Centered in the parent.
Center,
/// Aligned to the end (right for x, bottom for y) of the parent.
End,
}
Position::Const(value): Sets an exact coordinate from the top-left (0,0). You can also useu16directly, thanks toimpl From<u16> for Position.@Transform { x: 5, y: 10 }; // Same as x: Position::Const(5), y: Position::Const(10)Position::Center: Centers the element within the available space of its parent on that axis.@Transform { x: Position::Center, y: Position::Center };Position::End: Aligns the element to the right (forx) or bottom (fory) edge of its parent.@Transform { x: Position::End, y: Position::Const(0) }; // Top-right aligned
3. Dimension: Sizing Rules
Dimension defines how an element's width or height is determined.
pub enum Dimension {
/// Fills the available space from the parent.
Full,
/// Automatically sized to fit content.
Content,
/// Fixed size in cells.
Const(u16),
}
Dimension::Full: The element will take up 100% of the available space on that axis within its parent.Dimension::Content: The element's size will be determined by its content. For container elements (likeDiv,FlexRow,FlexCol), this means their size will expand to encompass their children. For text elements, it will be the size of the text. This is the default.Dimension::Const(value): The element will have a fixed size in cells. You can also useu16directly, thanks toimpl From<u16> for Dimension.@Transform { width: 20, height: 5 }; // Same as width: Dimension::Const(20), height: Dimension::Const(5)
4. Style: Visual Appearance
The Style component defines the background and foreground colors of a widget.
pub struct Style {
pub background: Background,
pub foreground: Option<u32>,
}
background: Background: Specifies the widget's background appearance.foreground: Option<u32>: Sets the text color.Nonemeans the default terminal foreground color.
You attach Style using the @ component syntax:
use osui::prelude::*;
rsx! {
@Style { background: Background::Solid(0x222222), foreground: Some(0xFFFFFF) };
Div {
"This text is white on a dark gray background."
}
@Style { background: Background::Outline(0x00FF00), foreground: Some(0x00FF00) };
Div {
"This div has a green outline and green text."
}
}
Background: Background Appearance Options
Background defines various ways a widget's background can be drawn. Colors are specified as u32 hex values (e.g., 0xFF0000 for red).
pub enum Background {
/// Transparent / no background.
NoBackground,
/// Draws a basic outline using the given color.
Outline(u32),
/// Draws a rounded outline using the given color.
RoundedOutline(u32),
/// Fills the background with the specified color.
Solid(u32),
}
The transform! Macro
For even more concise Transform definitions, you can use the transform! macro:
use osui::prelude::*;
rsx! {
@transform!{ x: 10, y: Center, width: Full, padding: (1, 1) };
Div {
"This div is at x=10, centered vertically, full width, with 1 unit of padding."
}
}
This macro automatically converts u16 values to Position::Const or Dimension::Const where appropriate.
How Layout and Styling are Applied
When the Screen renders a widget, it performs the following steps (simplified):
- Retrieve Components: It fetches the
TransformandStylecomponents attached to the widget. - Resolve Transform: The
Transform'suse_dimensionsanduse_positionmethods are called. These methods take the parent's resolved size (from theRenderScope) and convert the declarativePositionandDimensionrules into concreteu16values (absolutex,y,width,height) within aRawTransformstructure. - Apply Style: The
Stylecomponent is passed to theRenderScope. - Element Rendering: The widget's
Element::rendermethod is called. This method uses the now-resolvedRawTransformandStylefrom theRenderScopeto queue its drawing instructions (text, rectangles). It might also updateRenderScope's size based on content. - Parent Rendering (
after_render): For container elements (likeDiv,FlexRow,FlexCol), theirElement::after_rendermethod then takes over. They iterate through their children, set theRenderScope's "parent size" to their own resolved size, and recursively trigger the rendering process for each child. - Drawing to Terminal: Finally,
RenderScope::draw()is called, which takes all accumulated drawing instructions and applies them to the terminal usingcrosstermand ANSI escape codes. Backgrounds and outlines are drawn first, then text and other content.
This multi-stage process ensures that layout rules are correctly interpreted from parent to child, allowing for adaptive and well-positioned UI elements.