Version 0.1.1

This commit is contained in:
2025-08-10 14:14:04 -05:00
parent 54b9cba486
commit b4891d4795
39 changed files with 4587 additions and 0 deletions
@@ -0,0 +1,168 @@
# Declarative UI and the `rsx!` Macro
OSUI embraces a declarative approach to UI development, heavily inspired by modern web frameworks. This is primarily facilitated by the `rsx!` macro, which allows you to describe your UI's structure and behavior in a clear, nested, and expressive way.
## Why Declarative UI?
Traditional imperative UI development involves manually creating, positioning, and updating UI elements based on state changes. This can lead to complex, hard-to-maintain code, especially for dynamic UIs.
Declarative UI, in contrast:
* **Focuses on "What"**: You describe the desired UI state for a given data state, rather than the steps to get there.
* **Simplicity**: The code is often more readable and easier to reason about, as it mirrors the visual structure of the UI.
* **Reactivity**: When the underlying data changes, the framework (OSUI, in this case) efficiently updates the UI to reflect the new state, minimizing manual DOM manipulation.
* **Composition**: Encourages breaking down complex UIs into smaller, reusable components.
## The `rsx!` Macro
The `rsx!` macro is the cornerstone of OSUI's declarative syntax. It transforms a nested, JSX-like structure into a tree of `RsxElement`s, which are then used to build `Widget`s on the `Screen`.
### Basic Syntax
```rust
use osui::prelude::*;
fn main() -> std::io::Result<()> {
let screen = Screen::new();
screen.extension(InputExtension); // Needed for basic interaction
rsx! {
// Simple text string as an Element
"Hello, World!"
// Static element: No dynamic dependencies or state
static Div {
// Nested content
"This is a static div."
}
// Dynamic element: Reacts to state changes
%my_state // This widget depends on `my_state`
Div {
// Content can be interpolated from state
"Current value: {my_state}"
}
}.draw(&screen);
screen.run()?;
Ok(())
}
```
### Key Features of `rsx!` Syntax
1. **Direct Text**:
Any string literal within `rsx!` is treated as a `String` element.
```rust
rsx! {
"Just some text."
format!("Dynamic text: {}", 123) // You can also use format!
}
```
2. **Element Declaration**:
Elements are typically declared by their struct name (e.g., `Div`, `FlexRow`, `Input`).
```rust
rsx! {
Div {} // A simple Div element
}
```
3. **Properties (Fields)**:
You can set public fields on the element struct directly using comma-separated key-value pairs, similar to struct instantiation.
```rust
rsx! {
Heading, smooth: true, { "Important Title" } // Sets the `smooth` field on Heading
}
```
This expands to:
```rust
let mut elem = Heading::new();
elem.smooth = true;
// ... then `elem` is wrapped in a Widget
```
4. **Children**:
Content nested within curly braces `{}` after an element forms its children. These children are then drawn by the parent element's `after_render` method.
```rust
rsx! {
Div {
"First child"
"Second child"
FlexRow { "Nested FlexRow" }
}
}
```
5. **Static vs. Dynamic Widgets**:
* **`static` keyword**: Prefix an element with `static` to explicitly declare it as a `StaticWidget`. This means its content and components won't change unless manually modified elsewhere. It incurs no dependency tracking overhead.
```rust
rsx! {
static Div { "Always the same" }
}
```
* **Dynamic (Reactive)**: By default, if an element has dependencies (see below) or is just a string literal without `static`, it becomes a `DynWidget`. This allows it to refresh when its dependencies change.
6. **Dependencies (`%dep_name`)**:
Prefixing an element with `%variable_name` registers `variable_name` (which must implement `DependencyHandler`, like `State<T>`) as a dependency for that widget. If `variable_name` signals a change, the widget will automatically rebuild and re-render.
```rust
let counter = use_state(0);
rsx! {
%counter // This Div depends on `counter`
Div {
// `counter` can be used directly in its content
"Count: {counter}"
}
}
```
Multiple dependencies can be listed: `%dep1 %dep2 Element {}`.
7. **Components (`@ComponentType`)**:
Attach components to an element using the `@` symbol followed by the component's type and its constructor or a value.
```rust
use osui::prelude::*;
rsx! {
// Attach a Transform component with specific dimensions
@Transform::new().dimensions(10, 5);
// Attach a Style component with a solid red background
@Style { background: Background::Solid(0xFF0000) };
Div {
"A red box"
}
// Attach a custom Handler component for events
@Handler::new(|_, e: &MyEvent| { /* ... */ });
Div { "Click me!" }
}
```
You can attach multiple components to the same element, each on its own `@` line.
8. **Macro Expansion (`$expand => ($args)`)**:
You can include the output of another `Rsx`-generating function or macro using `macro_name => (args)`. This is useful for creating reusable UI fragments.
```rust
fn my_button(text: &str) -> Rsx {
rsx! {
Div { format!("Button: {}", text) }
}
}
rsx! {
my_button => ("Click Me")
my_button => ("Another Button")
}
```
### How `rsx!` Works Internally
The `rsx!` macro recursively expands into a series of `Rsx::create_element` or `Rsx::create_element_static` calls. Each `RsxElement` stores either a `StaticWidget` directly or a closure that produces a `WidgetLoad` (for dynamic widgets), along with its dependencies and child `Rsx` tree.
When `Rsx::draw` or `Rsx::draw_parent` is called on the root `Rsx` object:
1. It iterates through its `RsxElement`s.
2. For `RsxElement::DynElement`, it calls the stored closure to generate a `WidgetLoad`, then creates a `DynWidget` via `screen.draw_box_dyn`. It registers all specified dependencies with this new `DynWidget`.
3. For `RsxElement::Element`, it directly creates a `StaticWidget` via `screen.draw_widget`.
4. If a parent widget is provided (for nested elements), it calls `parent.get_elem().draw_child(&new_widget)`. This informs the parent `Element` about its new child.
5. It then recursively calls `draw_parent` on the child `Rsx` tree, passing the newly created widget as the `parent`.
This process builds the complete `Arc<Widget>` tree managed by the `Screen`, setting up the initial hierarchy and reactive dependencies. The declarative `rsx!` syntax simplifies this complex creation process into an intuitive structure.
@@ -0,0 +1,107 @@
# Layout and Styling System
OSUI employs a two-phase approach to layout and rendering, driven by `Transform` and `RawTransform` structures, and provides expressive tools for styling elements using `Style`.
## The Two-Phase Transform Model
OSUI's layout system resolves abstract positioning and sizing rules into concrete pixel coordinates and dimensions. This is managed by two primary structures:
1. **`Transform` (Configured Layout)**:
* This is the high-level struct that developers interact with.
* It defines abstract rules for position (`Position` enum) and dimension (`Dimension` enum), along with explicit margins (`mx`, `my`) and padding (`px`, `py`).
* `Transform` instances are typically attached to widgets as components using the `Transform` component or via the `transform!` macro.
* Examples: `Position::Center`, `Dimension::Full`, `Position::Const(10)`.
```rust
// Example Transform definition
component!(Transform {
pub x: Position,
pub y: Position,
pub mx: i32,
pub my: i32,
pub px: u16,
pub py: u16,
pub width: Dimension,
pub height: Dimension,
});
```
* Methods like `center()`, `bottom()`, `right()`, `margin()`, `padding()`, and `dimensions()` provide a fluent API for configuration.
2. **`RawTransform` (Resolved Layout)**:
* This struct holds the *concrete, absolute* `u16` values for `x`, `y`, `width`, `height`, `px`, and `py` after layout calculations have occurred.
* It represents the final calculated bounds and offsets for a widget on the virtual screen.
* Developers generally don't manipulate `RawTransform` directly; it's used internally by the `RenderScope` during the rendering phase.
```rust
// Example RawTransform definition
#[derive(Debug, Clone)]
pub struct RawTransform {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
pub px: u16,
pub py: u16,
}
```
### How Resolution Works (`use_dimensions`, `use_position`)
When a widget is rendered, its `Transform` component is used by the `RenderScope` to resolve its abstract rules into a concrete `RawTransform`.
* **`Transform::use_dimensions(parent_width, parent_height, raw_transform)`**:
* This method takes the `parent_width` and `parent_height` (the available space from the parent container) and updates the `raw_transform.width` and `raw_transform.height` based on the `Dimension` rules.
* `Dimension::Full` will set the raw dimension to the parent's available size.
* `Dimension::Const(n)` will set it to `n`.
* `Dimension::Content` means the dimension will be determined by the content drawn by the element itself (e.g., text length or explicit `use_area` calls).
* **`Transform::use_position(parent_width, parent_height, raw_transform)`**:
* This method uses the widget's *resolved* `raw_transform.width` and `raw_transform.height` (after `use_dimensions`) along with `parent_width` and `parent_height` to determine the absolute `raw_transform.x` and `raw_transform.y`.
* `Position::Const(n)` sets the coordinate to `n`.
* `Position::Center` calculates the coordinate to center the widget within the parent.
* `Position::End` calculates the coordinate to align the widget to the end (right/bottom) of the parent.
* Margins (`mx`, `my`) are applied as offsets after the base position is calculated.
This separation allows for a clear definition of layout rules at design time (`Transform`) and their efficient resolution into precise screen coordinates at runtime (`RawTransform`).
## Styling with `Style`
The `Style` component defines the visual appearance of a widget. It primarily controls background and foreground colors.
```rust
component!(Style {
pub background: Background,
pub foreground: Option<u32>,
});
pub enum Background {
NoBackground,
Outline(u32),
RoundedOutline(u32),
Solid(u32),
}
```
* **`background`**: Defines how the widget's background is rendered.
* `NoBackground`: The widget's area is transparent.
* `Outline(color)`: Draws a simple rectangular outline using the specified 24-bit RGB color.
* `RoundedOutline(color)`: Draws a rounded rectangular outline.
* `Solid(color)`: Fills the entire widget area with the specified 24-bit RGB color.
* **`foreground`**: An `Option<u32>` representing the 24-bit RGB color for any text drawn within the widget. If `None`, default terminal foreground color is used.
**Usage:**
```rust
use osui::prelude::*;
// Example: A div with blue background and white text
rsx! {
@Transform::new().dimensions(20, 5).padding(1,1);
@Style { background: Background::Solid(0x0000FF), foreground: Some(0xFFFFFF) };
Div {
"This is a blue box with white text."
}
}
```
By combining `Transform` and `Style` components, developers have precise control over the visual presentation and spatial arrangement of UI elements.
@@ -0,0 +1,42 @@
# Rendering Pipeline and `RenderScope`
OSUI's rendering process is a structured sequence of operations managed by the `RenderScope`. This module centralizes the accumulation of drawing commands and their eventual flush to the terminal.
## The Role of `RenderScope`
`RenderScope` is a mutable context passed through the rendering process for each widget. It serves several key purposes:
* **Layout Context**: It holds the current `RawTransform` (resolved position and dimensions) for the widget being rendered, derived from its `Transform` component and its parent's layout.
* **Drawing Command Accumulation**: It acts as a temporary buffer for `RenderMethod` instructions (text, rectangles). Widgets and extensions add commands to this stack.
* **Parent Size Tracking**: It keeps track of the available dimensions from the current widget's parent, crucial for resolving `Dimension::Full` and `Position::Center`/`End` rules.
* **Style Application**: It carries the `Style` component for the current widget, influencing how drawing commands are eventually rendered (e.g., background fill, foreground color).
## The Rendering Sequence
When `Screen::render_widget` is called for a given `Arc<Widget>`, the following general pipeline is executed:
1. **Clear Scope**: The `RenderScope` is cleared of any previous draw instructions, ensuring a clean slate for the current widget.
2. **Apply Style (if present)**: If the widget has a `Style` component, it's applied to the `RenderScope`.
3. **Apply Transform (Dimensions First)**: If the widget has a `Transform` component, its `use_dimensions` method is called. This resolves `Dimension` rules (`Full`, `Const`, `Content`) into the `RawTransform` based on the parent's size.
4. **Element `render` Call**: The widget's `Element::render` method is invoked.
* Here, the `Element` issues its direct drawing commands (e.g., `scope.draw_text(...)`, `scope.draw_rect(...)`).
* Crucially, if the `Dimension` was `Content`, the `Element` is responsible for updating the `RenderScope`'s `RawTransform` width/height to enclose its drawn content (e.g., `scope.use_area`).
5. **Extension `render_widget` Calls**: Any registered extensions have their `render_widget` hook called. Extensions can observe or modify the `RenderScope` or widget state at this point.
6. **Re-Apply Transform (Positions Second)**: After the `Element` and extensions have had a chance to determine the *content size* (for `Dimension::Content`), the widget's `Transform::use_position` is called. This resolves `Position` rules (`Const`, `Center`, `End`) into the `RawTransform` using the now-finalized dimensions. Margins are also applied here.
7. **`ElementRenderer::before_draw` Call**: An `ElementRenderer` trait hook is called. This is specifically used by "ghost" elements (like `Div` or `FlexRow`) to adjust the `RenderScope`'s transform *for their children*. For example, a `FlexRow` would update the `x`/`y` of the `RenderScope`'s internal `RawTransform` so that the next child starts at the correct position within the row.
8. **`RenderScope::draw`**: The accumulated `RenderMethod` instructions within the `RenderScope`'s `render_stack` are flushed to the terminal. This involves:
* Drawing the background (if `Style::background` is `Solid`, `Outline`, or `RoundedOutline`).
* Iterating through `render_stack` and printing text or drawing rectangles at their resolved coordinates, applying foreground colors from `Style` or specific `TextColored` commands.
9. **Element `after_render` Call**: The widget's `Element::after_render` method is invoked.
* This is typically where container elements (`Div`, `FlexRow`, `Paginator`) recursively trigger `scope.render_widget` for their children. They pass a *new* or modified `RenderScope` to their children, setting the `parent_width` and `parent_height` appropriately (e.g., the parent's own *content area*).
10. **Extension `after_render_widget` Calls**: Any registered extensions have their `after_render_widget` hook called. This allows extensions to perform post-rendering logic, such as updating internal state based on the final rendered transform (e.g., `RelativeFocusExtension` records widget positions).
11. **`widget.auto_refresh()`**: For `DynWidget`s, this checks if any registered dependencies have changed and, if so, triggers a `refresh()` to rebuild the widget for the next frame.
## Key Concepts in `RenderScope`
* **`RenderMethod`**: An internal enum representing a single primitive drawing operation (Text, TextInverted, TextColored, Rectangle).
* **`draw_text`, `draw_rect`, etc.**: Methods on `RenderScope` to add `RenderMethod`s to the `render_stack`. These methods also update the `RenderScope`'s internal `RawTransform` to account for the content's size, enabling `Dimension::Content` to work.
* **`use_area(width, height)`**: Allows an `Element` to explicitly declare a minimum required width and height, which is useful when the content itself doesn't automatically dictate a size (e.g., a `Div` that just reserves space).
* **Coordinate System**: All coordinates `(x, y)` are relative to the *current* `RenderScope`'s top-left corner. When `RenderScope::draw` is called, these relative coordinates are offset by the `RenderScope`'s absolute `transform.x` and `transform.y`. Padding (`px`, `py`) is also applied as an offset for content rendering.
This pipeline ensures that layout calculations are performed efficiently, drawing commands are batched, and elements are rendered within their correctly determined bounds, respecting both parent constraints and self-determined content sizes.
@@ -0,0 +1,109 @@
# State Management and Reactivity
OSUI provides a built-in, lightweight state management system that enables reactive updates to your UI. This system is centered around the `State<T>` struct and the `DependencyHandler` trait, allowing `DynWidget`s to automatically re-render when their associated data changes.
## `State<T>`: Your Reactive Data Container
The `State<T>` struct is a wrapper around your data `T` that facilitates dependency tracking.
```rust
#[derive(Debug, Clone)]
pub struct State<T> {
inner: Arc<Mutex<Inner<T>>>,
}
#[derive(Debug)]
pub struct Inner<T> {
value: T,
dependencies: usize, // Number of widgets depending on this state
changed: usize, // Counter for changes waiting to be processed by dependents
}
```
* **`Arc<Mutex<Inner<T>>>`**: The core of `State<T>` is its use of `Arc` and `Mutex`.
* `Arc` allows `State<T>` instances to be shared across multiple widgets and threads without needing to clone the underlying data `T` itself, which is crucial for `DynWidget`s that track multiple dependencies.
* `Mutex` ensures safe concurrent access to the `value` and metadata (`dependencies`, `changed`), preventing data races.
### Creating State (`use_state`)
You create a new `State<T>` instance using the `use_state` helper function:
```rust
pub fn use_state<T>(v: T) -> State<T> { /* ... */ }
```
**Example:**
```rust
use osui::prelude::*;
fn main() -> std::io::Result<()> {
let screen = Screen::new();
screen.extension(InputExtension);
screen.extension(RelativeFocusExtension::new());
// Create a new state variable for a counter
let count = use_state(0);
// Spawn a thread to increment the counter every second
std::thread::spawn({
let count = count.clone(); // Clone the Arc<State<T>> for the new thread
move || loop {
// Get a mutable lock on the Inner<T> to modify the value
// DerefMut implementation on Inner<T> automatically marks it as changed
*count.get() += 1;
std::thread::sleep(std::time::Duration::from_secs(1));
}
});
rsx! {
// Declare the widget as dependent on `count`
%count
Div {
// Access the value using Deref on Inner<T>
format!("This number increments: {}", count.get())
}
}.draw(&screen);
screen.run()?;
Ok(())
}
```
### Accessing and Modifying State
* **`State::get()`**: Returns a `MutexGuard<'_, Inner<T>>`. This provides mutable access to the underlying `value` within `Inner<T>`.
* `Inner<T>` implements `Deref` and `DerefMut` for `T`. This means you can treat `count.get()` like a direct reference to `T`.
* Crucially, when you use `DerefMut` (e.g., `*count.get() += 1`), the `changed` counter within `Inner<T>` is automatically incremented. This is how OSUI knows the state has been modified and needs to trigger a re-render.
* **`State::get_dl()`**: (Short for "get, don't lock") Returns a cloned copy of the value. This is useful when you only need to read the value and want to avoid holding the `MutexGuard` for longer than necessary, which can prevent deadlocks in complex scenarios. However, it doesn't mark the state as changed.
* **`State::set(v: T)`**: Replaces the entire value and marks the state as changed.
* **`State::update()`**: Explicitly marks the state as changed *without* modifying its value. Useful if internal parts of `T` are modified outside of direct `DerefMut` access.
## `DependencyHandler` Trait
The `DependencyHandler` trait is the interface through which `DynWidget`s observe changes in their dependencies. `State<T>` implements this trait.
```rust
pub trait DependencyHandler: std::fmt::Debug + Send + Sync {
fn add(&self);
fn check(&self) -> bool;
}
```
* **`add()`**: Called when a `DynWidget` registers itself as a dependent of this `State<T>`. It increments the `dependencies` counter within `Inner<T>`.
* **`check()`**: Called by `DynWidget`s (specifically by `DynWidget::auto_refresh()`) to determine if the state has changed since the last check.
* It decrements the `changed` counter if it's greater than zero, signifying that a change has been "consumed" by a dependent.
* It returns `true` if `changed` was greater than zero, indicating a fresh update.
### How Reactivity Works
1. **Widget Creation**: When `rsx!` creates a `DynWidget` with a `%state_var` dependency, `state_var.add()` is called, incrementing `state_var.inner.dependencies`.
2. **State Modification**: When `*state_var.get() = new_value` or `state_var.set(new_value)` is called, the `state_var.inner.changed` counter is set to `state_var.inner.dependencies`. This means *all* widgets currently depending on this state are marked for a refresh.
3. **Automatic Refresh**: In each rendering frame, `DynWidget::auto_refresh()` is called.
* It iterates through its registered `DependencyHandler`s.
* For each dependency, it calls `dependency.check()`.
* If `check()` returns `true` (meaning the state has changed and hasn't been consumed yet by this widget), the `DynWidget`'s internal `load` closure is re-executed (`self.refresh()`). This rebuilds the widget's `Element` and `Component`s, picking up the new state value.
* The `check()` method decrements the `changed` counter, ensuring that a single modification to the state triggers exactly one rebuild for each dependent widget.
4. **Re-render**: The rebuilt widget is then rendered on the next frame, reflecting the updated state.
This system provides a robust and efficient way to manage dynamic UI elements, abstracting away the complexities of manual DOM updates and allowing developers to focus on defining their UI's desired state.
@@ -0,0 +1,100 @@
# Core Widget Model
OSUI's user interface is built upon a flexible and extensible widget model. At its heart, this model separates rendering logic from data and behavior using `Element`s and `Component`s, all encapsulated within `Widget`s.
## Elements: The Renderable Unit
The `Element` trait is the fundamental building block for anything that can be rendered on the screen.
```rust
pub trait Element: Send + Sync {
fn render(&mut self, scope: &mut RenderScope, render_context: &RenderContext);
fn after_render(&mut self, scope: &mut RenderScope, render_context: &RenderContext);
fn draw_child(&mut self, element: &Arc<Widget>);
fn event(&mut self, event: &dyn Event);
fn is_ghost(&mut self) -> bool;
// ... Any and AnyMut methods for downcasting
}
```
* **`render`**: This is where the element defines *what* it draws. It uses the provided `RenderScope` to issue drawing commands (e.g., `draw_text`, `draw_rect`). It *does not* handle child rendering; that's done by the system or a parent element's `after_render`.
* **`after_render`**: Called after the element's `render` method and any extensions have processed it. This is typically where container elements (like `Div` or `FlexRow`) would recursively trigger the rendering of their children, using the `RenderScope` for layout calculations.
* **`draw_child`**: Used by the `rsx!` macro and `Rsx` structure to register a child widget with a parent `Element`.
* **`event`**: Allows the element to react to various system events (e.g., keyboard input, custom events).
* **`is_ghost`**: A "ghost" element is one that primarily serves as a layout or logical container and does not draw itself, but manages the rendering of its children. Examples include `Div` and `FlexRow`. They receive a `RenderScope` but might not add anything to its `render_stack` directly.
By default, simple text (`String`) is also an `Element`, allowing you to embed strings directly in `rsx!`.
## Components: Attaching Behavior and Data
The `Component` trait is an optional marker trait used for attaching arbitrary data or behavior to a `Widget`.
```rust
pub trait Component: Send + Sync {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
```
Components are stored in a `HashMap` within a `Widget`, keyed by `TypeId`. This allows a widget to dynamically acquire and retrieve different functionalities or data.
**Why Components?**
* **Separation of Concerns**: Keep common behaviors (like styling, focus, velocity) separate from the core rendering logic of an `Element`.
* **Flexibility**: Widgets can gain new capabilities at runtime by adding or removing components without modifying their fundamental `Element` implementation.
* **Extensibility**: Extensions often operate by attaching or querying specific components (e.g., `Transform` for layout, `Style` for appearance, `Focused` for focus management).
Examples of built-in components include `Transform`, `Style`, `Velocity`, `Focused`, `Handler<E>`, etc. You can easily define your own using the `component!` macro.
## Widgets: The Container for Elements and Components
The `Widget` enum wraps an `Element` and its associated `Component`s. It's the primary way to interact with UI nodes in the OSUI tree.
```rust
pub enum Widget {
Static(StaticWidget),
Dynamic(DynWidget),
}
```
`Widget` provides a unified interface to access its underlying `Element` and `Component`s, regardless of whether it's static or dynamic. Most interactions with the UI tree, such as drawing children or querying properties, are done via an `Arc<Widget>`.
### `WidgetLoad`: Building Widgets
`WidgetLoad` is a temporary struct used during the initial construction of a widget. It encapsulates the root `BoxedElement` and a `HashMap` of `BoxedComponent`s.
```rust
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
impl WidgetLoad {
pub fn new<E: Element + 'static>(e: E) -> Self { /* ... */ }
pub fn component<C: Component + 'static>(mut self, c: C) -> Self { /* ... */ }
pub fn set_component<C: Component + 'static>(mut self, c: C) -> Self { /* ... */ }
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> { /* ... */ }
}
```
It acts as a builder pattern for setting up a widget's initial state and attaching components, often used by the `rsx!` macro.
### `StaticWidget` vs. `DynWidget`
OSUI differentiates between two types of widgets to optimize for different use cases:
* **`StaticWidget`**:
* **Purpose**: Represents UI elements whose content and components do not change after initial creation.
* **When to Use**: Ideal for static text, immutable labels, or simple decorative elements that don't react to state changes.
* **Performance**: More efficient as they don't carry the overhead of dependency tracking or re-evaluation logic.
* **Creation**: Typically created directly via `Widget::new_static` or `Screen::draw` for direct `Element`s, or by `rsx!` when no dependencies are specified.
* **`DynWidget`**:
* **Purpose**: Represents UI elements whose content can change reactively based on external state.
* **When to Use**: Essential for displaying dynamic data, user input fields, or any widget that needs to update its appearance or children when underlying data changes (e.g., a counter, a list that filters based on input).
* **Mechanism**: Stores a closure (`FnMut() -> WidgetLoad`) that rebuilds its `Element` and `Component`s. It tracks dependencies (via `DependencyHandler`) and automatically `refresh`es itself when those dependencies signal a change.
* **Performance**: Carries a small overhead for dependency checking and re-evaluation.
* **Creation**: Created via `Widget::new_dyn` or `Screen::draw_dyn`, or by `rsx!` when state dependencies (`%state_var`) are provided.
The distinction allows OSUI to efficiently render static parts of the UI while providing powerful reactivity for dynamic sections. When you use the `rsx!` macro, OSUI automatically determines whether to create a `StaticWidget` or `DynWidget` based on the presence of dependencies.
### Dependency Tracking
`DynWidget`s are at the core of OSUI's reactivity. They listen for changes in their registered dependencies. When a dependency changes, the widget's internal `load` closure is re-executed, effectively rebuilding its `Element` and `Component`s, leading to a re-render. This mechanism is explained in detail in [State Management](../concepts/state-management.md).