Compose an interface
Build rows, columns, text, and widgets as a typed Rust tree.
Elements describe the view
Element::row and Element::column accept child elements. Builder methods apply typed layout, paint, semantics, and interaction. Rendering rebuilds a cheap description; Argui reconciles it against the retained tree.
let actions = Element::row([
Button::new("save", "Save", theme.button()).build(),
Button::new("cancel", "Cancel", theme.ghost_button()).build(),
])
.flex_wrap(FlexWrap::Wrap)
.gap(8.0);
Element::column([
Element::text("Account settings"),
form,
actions,
])
.width(percent(1.0))
.max_width(length(720.0))
.padding(Sides::length(24.0))
.gap(20.0)Use stable keys where identity matters
Interactive controls already require a stable key. Add keyed identities to siblings that can reorder, appear, or disappear so focus, animation, and retained state follow the logical item instead of its old position.
Element::column(items.iter().map(|item| {
row(item).keyed(format!("item-{}", item.id))
}))Extract plain view functions
Start with ordinary functions returning Element. Extract a model only when a section needs state, lifecycle, subscriptions, tasks, or an independently cached presentation. This keeps the tree readable without creating a component abstraction for every wrapper.
Reference files
These are the implementation and guide files used for this chapter.
crates/argui/examples/layout.rscrates/argui-ui/src/element.rscrates/argui-widget-gallery/src/pages/buttons.rsComposition with real Argui buttons
The Rust file below is imported verbatim by this page and compiled into the WebAssembly application running underneath it.
use argui::{
paint::{Border, CornerRadii},
runtime::{Context, Render},
text::TextStyle,
ui::{AlignItems, Element, FlexWrap, JustifyContent, Sides, length, percent},
widgets::default_theme,
};
pub struct Example;
impl Render for Example {
fn render(&mut self, cx: &mut Context<Self>) -> Element {
let themes = default_theme(cx.environment());
let theme = themes.resolve(cx.environment().color_scheme);
let item = |label: &'static str| {
Element::text(label)
.text_style(TextStyle {
color: theme.primary_foreground,
weight: 700,
..TextStyle::default()
})
.width(length(56.0))
.height(length(40.0))
.align_items(AlignItems::CENTER)
.justify_content(JustifyContent::CENTER)
.background(theme.primary)
.radius(CornerRadii::all(8.0))
};
let panel = |title: &'static str, detail: &'static str, composition: Element| {
Element::column([
Element::text(title).text_style(TextStyle {
color: theme.foreground,
weight: 700,
..TextStyle::default()
}),
Element::text(detail).text_style(TextStyle {
color: theme.muted_foreground,
font_size: 13.0,
..TextStyle::default()
}),
Element::container([composition])
.height(length(150.0))
.padding(Sides::length(14.0))
.align_items(AlignItems::CENTER)
.justify_content(JustifyContent::CENTER)
.background(theme.muted)
.radius(CornerRadii::all(9.0)),
])
.width(length(280.0))
.grow(1.0)
.padding(Sides::length(18.0))
.gap(10.0)
.background(theme.card)
.border(Border::all(1.0, theme.border))
.radius(CornerRadii::all(12.0))
};
let horizontal = Element::row([item("A"), item("B"), item("C")]).gap(10.0);
let vertical = Element::column([item("A"), item("B"), item("C")]).gap(8.0);
Element::row([
panel(
"Element::row",
"Children flow from left to right.",
horizontal,
),
panel(
"Element::column",
"Children flow from top to bottom.",
vertical,
),
])
.width(percent(1.0))
.height(percent(1.0))
.padding(Sides::length(24.0))
.gap(12.0)
.flex_wrap(FlexWrap::Wrap)
.background(theme.background)
}
}
Starting this lesson’s WebAssembly module…