Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Platform receives input (mouse, keyboard)
  2. Event dispatched to the surface’s root widget
  3. 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
  4. 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