Macros Module API Reference
The osui-macros crate provides the procedural macros that enhance OSUI's ergonomics and enable its declarative UI syntax. These macros transform your Rust code into the necessary OSUI component and rendering structures.
#[component] Attribute Macro
#[proc_macro_attribute]
pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream { /* ... */ }
The #[component] attribute macro transforms a standard Rust function into a fully-fledged OSUI component.
Purpose:
- Prop Generation: It automatically parses the function's parameters (after the initial
cx: &Arc<Context>) and generates astructwith matching fields. These fields become the component's "props". ComponentImplImplementation: It implements theComponentImpltrait for the generated struct, making it a valid OSUI component that can be rendered. Thecallmethod of this trait simply invokes your original function.- Ergonomics: Simplifies component definition by allowing you to write components as regular functions with clear parameters, without manually defining structs and
ComponentImplboilerplate.
Usage:
use osui::prelude::*; // For Context and View types
use std::sync::Arc;
#[component]
pub fn MyComponent(cx: &Arc<Context>, message: &str, count: &usize) -> View {
// Your component logic, accessing `message` and `count` directly
rsx! {
format!("Message: {}, Count: {}", message, count)
}.view(&cx)
}
// How `MyComponent` would be used in RSX:
// rsx! {
// MyComponent { message: "Hello", count: 123 }
// }
Generated Code (Simplified):
pub struct MyComponent {
pub message: String, // Note: `&str` becomes `String`
pub count: usize, // Note: `&usize` becomes `usize`
}
impl MyComponent {
pub fn component(
cx: &Arc<Context>,
message: &str, // Original function signature as `component` method
count: &usize,
) -> View {
// Original body of the function
rsx! {
format!("Message: {}, Count: {}", message, count)
}.view(&cx)
}
}
impl ComponentImpl for MyComponent {
fn call(&self, cx: &Arc<Context>) -> View {
Self::component(
cx,
&self.message, // Passes stored props as references
&self.count,
)
}
}
Requirements:
- The function must take
cx: &Arc<Context>as its first parameter. - The function must return
View. - Prop parameters are typically references (e.g.,
&str,&i32). The macro automatically converts them to their owned types (e.g.,String,i32) in the generated struct.
rsx! Procedural Macro
#[proc_macro]
pub fn rsx(input: TokenStream) -> TokenStream { /* ... */ }
The rsx! macro provides a declarative, React-like syntax for building UI component hierarchies directly in your Rust code. It parses the input and transforms it into calls to the osui::frontend::Rsx builder methods.
Purpose:
- Declarative UI: Allows you to describe what your UI should look like, rather than imperatively writing drawing commands.
- Component Composition: Enables easy nesting and passing of props/children to other components.
- Reactive Flow: Integrates with OSUI's state management to define dynamic UI segments.
Syntax Overview:
The rsx! macro supports several types of nodes:
-
Text Literals:
rsx! {
"Hello World"
"Another line of text"
}
// Generates:
// r.static_scope(move |scope| {
// scope.view(Arc::new(move |ctx| {
// ctx.draw_text(Point { x: 0, y: 0 }, &format!("Hello World"))
// }));
// // ... for another line
// }); -
Rust Expressions (
@{expr}):let name = "Alice";
rsx! {
@{format!("Hello, {}!", name)}
@{1 + 2} // Any `Display` impl
}
// Generates:
// r.child(format!("Hello, {}!", name));
// r.child(1 + 2);- The
exprmust evaluate to a type that implementsosui::frontend::ToRsx.
- The
-
Component Instantiation (
Component { prop: value, ... children }):#[component] fn MyDiv(cx: &Arc<Context>, content: &str) -> View { /* ... */ }
rsx! {
MyDiv {
content: "Some text", // Prop
rsx! { "Child content" } // Children (if `children: &Rsx` is a prop)
}
}
// Generates:
// r.static_scope(move |scope| {
// scope.child(
// MyDiv {
// content: "Some text".to_string(), // Owned type for struct field
// children: osui::frontend::Rsx(/* ... */)
// },
// None
// );
// });path: The path to the component struct (e.g.,MyDiv,my_module::MyComponent).props:key: valuepairs for component properties.children: Anyrsx!content directly inside the braces after props. This is collected into the specialchildren: &Rsxprop if the component defines it.
-
Conditional Rendering (
@if condition { ... } [else { ... }]):let show = true;
rsx! {
%show @if show {
"Content shown if 'show' is true"
} else {
"Content shown if 'show' is false"
}
}
// Generates:
// r.dynamic_scope(move |scope| {
// if show {
// // ... rsx for true branch
// } else {
// // ... rsx for false branch
// }
// }, vec![Arc::new(show) as Arc<dyn HookDependency>]);%dep1, dep2: Optional dependency list. Theifblock will re-evaluate when any of these dependencies (which must implementHookDependency, likeState<T>) change.condition: A Rust expression evaluating tobool.{ ... }: Anrsx!fragment rendered ifconditionis true.else { ... }: Optionalrsx!fragment rendered ifconditionis false.
-
Loop Rendering (
@for pattern in expr { ... }):let items = vec!["A", "B", "C"];
rsx! {
%items @for item in items {
format!("Item: {}", item)
}
}
// Generates:
// r.dynamic_scope(move |scope| {
// for item in items {
// // ... rsx for each item
// }
// }, vec![Arc::new(items) as Arc<dyn HookDependency>]);%dep1, dep2: Optional dependency list. Theforloop will re-evaluate when any of these dependencies change.pattern: A standard Rustforloop pattern (e.g.,item,(idx, item)).expr: A Rust expression evaluating to anIntoIterator.{ ... }: Anrsx!fragment rendered for each iteration.
-
Mount Hook (
!mount_hook_instance):let my_manual_mount = use_mount_manual();
rsx! {
!my_manual_mount
}
// Generates:
// my_manual_mount.mount();- Calls the
.mount()method on the providedMountinstance. This is typically used withuse_mount_manualto trigger effects at a specific point in the render tree.
- Calls the
The rsx! macro is a powerful tool for declarative UI construction, abstracting away the underlying Rsx object manipulation and Scope creation logic.
Next: Explore the detailed Render API.