Widgets
Widgets are the building blocks of Guido UIs. Every visual element is a widget, from simple text to complex layouts.
The Widget Trait
All widgets implement the Widget trait:
#![allow(unused)]
fn main() {
pub trait Widget {
fn layout(&mut self, tree: &mut Tree, id: WidgetId, constraints: Constraints) -> Size;
fn paint(&self, tree: &Tree, id: WidgetId, ctx: &mut PaintContext);
fn event(&mut self, tree: &mut Tree, id: WidgetId, event: &Event) -> EventResponse;
}
}
Methods
- layout - Calculate the widget’s size given tree access, widget ID, and constraints
- paint - Draw the widget using tree for child access
- event - Handle input events with tree access
Bounds and Origins
Widget bounds and origins are stored in the Tree, not on individual widgets:
- Use
tree.get_bounds(id)to retrieve a widget’s bounding rectangle for hit testing - Use
tree.set_origin(id, x, y)to position widgets during layout (called by parent layouts)
Built-in Widgets
Container
The primary widget for building UIs. Handles:
- Backgrounds (solid, gradient)
- Borders and corner radius
- Padding and sizing
- Layout of children
- Event handling
- State layers (hover/pressed)
- Transforms
See Container for details.
Text
Renders text content:
#![allow(unused)]
fn main() {
text("Hello, World!")
.font_size(16.0)
.color(Color::WHITE)
.bold()
}
See Text for styling options.
TextInput
Single-line text editing with selection, clipboard, and undo/redo:
#![allow(unused)]
fn main() {
let username = create_signal(String::new());
text_input(username)
.text_color(Color::WHITE)
.on_submit(|text| println!("Submitted: {}", text))
}
See Text Input for details.
Composition
Guido UIs are built through composition - nesting widgets inside containers:
#![allow(unused)]
fn main() {
container()
.layout(Flex::column().spacing(8.0))
.children([
text("Title").font_size(24.0),
container()
.layout(Flex::row().spacing(4.0))
.children([
text("Item 1"),
text("Item 2"),
]),
])
}
This creates:
┌─────────────────┐
│ Title │
│ ┌─────┬───────┐ │
│ │Item1│ Item2 │ │
│ └─────┴───────┘ │
└─────────────────┘
Widget Functions
Guido provides functions that return configured widgets:
#![allow(unused)]
fn main() {
// Creates a Container
container()
// Creates a Text widget
text("content")
}
These use the builder pattern for configuration:
#![allow(unused)]
fn main() {
container()
.padding(16.0) // Returns Container
.background(Color::RED) // Returns Container
.corner_radius(8.0) // Returns Container
}
The impl Widget Pattern
Functions often return impl Widget instead of concrete types:
#![allow(unused)]
fn main() {
fn my_button(label: &str) -> impl Widget {
container()
.padding(12.0)
.background(Color::rgb(0.3, 0.5, 0.8))
.corner_radius(8.0)
.child(text(label).color(Color::WHITE))
}
}
This allows returning any widget type without exposing implementation details.
Constraints and Sizing
During layout, parent widgets pass constraints to children:
#![allow(unused)]
fn main() {
pub struct Constraints {
pub min_width: f32,
pub max_width: f32,
pub min_height: f32,
pub max_height: f32,
}
}
Children choose a size within these constraints. This enables flexible layouts where widgets can expand to fill space or shrink to fit content.
Size Modifiers
Control widget sizing with builder methods:
#![allow(unused)]
fn main() {
container()
.width(100.0) // Fixed width
.height(50.0) // Fixed height
.min_width(50.0) // Minimum width
.max_width(200.0) // Maximum width
}
Event Flow
Events flow from the platform through the widget tree:
- Platform receives input (mouse, keyboard)
- Event dispatched to root widget
- Root checks if event hits its bounds
- If yes, passes to children (innermost first)
- Widget handles event or ignores it
#![allow(unused)]
fn main() {
container()
.on_click(|| println!("Clicked!"))
.child(text("Click me"))
}
The container receives clicks anywhere within its bounds, including over the text.
Next Steps
- Container - Deep dive into the Container widget
- Layout - Learn about flex layout
- Interactivity - Add event handling