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:
// not compiled: a signature listing — these declarations have no bodies.
pub trait Widget {
fn layout(&mut self, ctx: &mut LayoutCtx, constraints: Constraints) -> Size;
fn paint(&self, 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:
extern crate guido;
use guido::prelude::*;
fn main() {
container().child(text("Hello, World!").font_size(16.0).color(Color::WHITE))
;
}
See Text for styling options.
TextInput
Single-line text editing with selection, clipboard, and undo/redo:
extern crate guido;
use guido::prelude::*;
fn main() {
let username = create_signal(String::new());
container().child(text_input(username).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:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.layout(Flex::column().spacing(8.0))
.children([
container().child(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:
extern crate guido;
use guido::prelude::*;
fn main() {
// Creates a Container
container();
// Creates a Text widget
text("content")
;
}
These use the builder pattern for configuration:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.padding(16.0) // Returns Container
.background(Color::RED) // Returns Container
.corners(8.0) // Returns Container
;
}
The impl Widget Pattern
Functions often return impl Widget instead of concrete types:
extern crate guido;
use guido::prelude::*;
fn my_button(label: &str) -> impl Widget {
container()
.padding(12.0)
.background(Color::rgb(0.3, 0.5, 0.8))
.corners(8.0)
.child(container().child(text(label).color(Color::WHITE)))
}
fn main() {}
This allows returning any widget type without exposing implementation details.
Constraints and Sizing
During layout, parent widgets pass constraints to children:
// not compiled: a listing of a type the crate owns — a copy of it declared
// here would compile without checking the original.
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:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.width(100.0) // Fixed width
.height(50.0) // Fixed height
.width(at_least(50.0).at_most(200.0)) // Bounded instead
;
}
Event Flow
Events flow from the platform through the widget tree, and each one goes only where it can matter:
- Platform receives input (mouse, keyboard)
- Event dispatched to the surface’s root widget
- A pointer event is passed down to the widgets that can be under its point, and to those it still owes one — the widget the pointer just left, the one a press landed on. A key is passed down to the focused widget and, if it does not take it, to the containers listening for keys
- Each widget handles the event or ignores it; the innermost hears it first
Event Routing has the whole of it.
extern crate guido;
use guido::prelude::*;
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