gpui-markup
A declarative markup DSL for building GPUI applications.
Installation
Usage
use *;
use ui;
Syntax
Elements
All elements require braces {}. Attributes go before braces with @[...]:
// Empty div
ui!
// -> div()
// Div with attributes
ui!
// -> div().flex().flex_col()
// Div with children
ui!
// -> gpui::ParentElement::child(div(), "content")
// Full form: attributes before braces, children inside
ui!
// -> gpui::ParentElement::child(div().flex(), "content")
Attributes
Attributes use @[...] before braces, comma-separated:
// Flag attributes (no value)
ui!
// -> div().flex().flex_col()
// Key-value attributes
ui!
// -> div().w(px(200.0)).h(px(100.0))
// Multi-value attributes (use tuples)
ui!
// -> div().when(condition, |d| d.bg(red()))
Children
Children go inside {...}, comma-separated:
ui!
// -> gpui::ParentElement::child(
// gpui::ParentElement::child(
// gpui::ParentElement::child(div(), "First"),
// "Second"
// ),
// gpui::ParentElement::child(div().bold(), "Nested")
// )
Deferred
The deferred element wraps content for deferred rendering:
ui!
// -> deferred(gpui::IntoElement::into_any_element(gpui::ParentElement::child(div(), "Deferred content")))
Spread Children
Use ..expr to spread an iterable as children:
let items: = vec!;
ui!
// -> gpui::ParentElement::children(div(), items)
// Can be mixed with regular children
ui!
// -> gpui::ParentElement::child(
// gpui::ParentElement::children(
// gpui::ParentElement::child(div(), "Header"),
// items
// ),
// "Footer"
// )
Method Chains
Use .method(args) to insert method calls at any position:
ui!
Comments
Use standard Rust comments inside ui!:
ui!
// -> gpui::ParentElement::child(div(), "Visible content")
Components
Components are any non-native element names. They automatically call ::new():
// Simple component
ui!
// -> Header::new()
// Component with attributes
ui!
// -> Button::new().style(Primary)
// Component with children
ui!
// -> gpui::ParentElement::child(gpui::ParentElement::child(Container::new(), "Content"), Footer::new())
Expression Elements
Any expression can be used as an element at the top level (braces required):
// Custom constructor
ui!
// -> Button::with_label("Click")
// Expression with attributes
ui!
// -> Button::with_label("Click").style(Primary)
// Builder pattern expression
ui!
// -> gpui::ParentElement::child(div().flex().flex_col(), "Content")
// Parentheses for complex expressions (braces optional)
ui!
// -> a + b
Why braces are required at top level?
The ui! macro builds a GPUI component tree. At the top level, {} declares "this is a UI element":
- Marks this as a node in the component tree, not just an expression
- For components,
{}triggers the implicit::new()call - Provides a place for attributes
@[...]and children
// Clear: defining a UI element, Header::new() is called
ui!
// As a child, context already indicates it's part of the tree
div
Nested Structures
ui!
How It Works
The ui! macro transforms the markup syntax into GPUI's builder pattern at compile time:
| Markup | Generated Code |
|---|---|
div {} |
div() |
div @[flex] {} |
div().flex() |
div @[w: x] {} |
div().w(x) |
div @[when: (a, b)] {} |
div().when(a, b) |
div { a, b } |
gpui::ParentElement::child(gpui::ParentElement::child(div(), a), b) |
div { ..items } |
gpui::ParentElement::children(div(), items) |
div { .a().b() } |
div().a().b() |
deferred { e } |
deferred(gpui::IntoElement::into_any_element(e)) |
Header {} |
Header::new() |
Header @[a] {} |
Header::new().a() |
expr {} |
expr |
expr @[a] {} |
expr.a() |
(expr) |
expr |