v0.1.0 with spark
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
# Elements API Reference
|
||||
|
||||
OSUI provides a set of built-in `Element` implementations that serve as the fundamental visual components for building your terminal user interfaces. This section details their purpose and configurable properties.
|
||||
|
||||
All elements implicitly implement the `Element` trait and can be used within the `rsx!` macro.
|
||||
|
||||
## Basic Elements
|
||||
|
||||
### `String`
|
||||
|
||||
Represents simple plain text.
|
||||
|
||||
```rust
|
||||
impl Element for String
|
||||
```
|
||||
|
||||
* **Properties**: None directly. Content is the string itself.
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
"Hello, World!"
|
||||
}
|
||||
```
|
||||
|
||||
### `(String, u32)` (Colored Text)
|
||||
|
||||
Represents text with a specific 24-bit RGB foreground color.
|
||||
|
||||
```rust
|
||||
impl Element for (String, u32)
|
||||
```
|
||||
|
||||
* **Properties**:
|
||||
* `0`: The `String` content.
|
||||
* `1`: The `u32` RGB color (e.g., `0xFF0000` for red).
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
("This text is red.", 0xFF0000)
|
||||
}
|
||||
```
|
||||
|
||||
## Container Elements
|
||||
|
||||
Container elements manage and render their children. They typically take care of positioning children relative to themselves.
|
||||
|
||||
### `Div`
|
||||
|
||||
A basic rectangular container element. It positions its children at their specified coordinates relative to the div's top-left corner and expands its own `Dimension::Content` size to fit them.
|
||||
|
||||
```rust
|
||||
pub struct Div {
|
||||
// children: Vec<Arc<Widget>>, // Internal
|
||||
// size: (u16, u16), // Internal calculated size
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**: None specific to `Div`. Layout and style are controlled by attached `Transform` and `Style` components.
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
@Transform::new().padding(1, 1);
|
||||
@Style { background: Background::Solid(0x333333) };
|
||||
Div {
|
||||
"Content inside a div."
|
||||
@Transform::new().x(2).y(2);
|
||||
Div { "Nested div offset by (2,2)" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `FlexRow`
|
||||
|
||||
A container element that arranges its children vertically, one after another, like a column. It expands its height to fit children and can apply a uniform `gap` between them.
|
||||
|
||||
```rust
|
||||
pub struct FlexRow {
|
||||
pub gap: u16,
|
||||
// children: Vec<Arc<Widget>>, // Internal
|
||||
// size: (u16, u16), // Internal calculated size
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**:
|
||||
* `gap: u16`: The number of empty cells between each child element. Defaults to `0`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
FlexRow, gap: 1, {
|
||||
"First Item"
|
||||
("Second Item (colored)", 0x00FFFF)
|
||||
Div { "Third Item (a div)" }
|
||||
}
|
||||
}
|
||||
```
|
||||
This will render "First Item", then a 1-cell gap, then "Second Item", then a 1-cell gap, etc., all stacked vertically.
|
||||
|
||||
### `FlexCol`
|
||||
|
||||
A container element that arranges its children horizontally, one after another, like a row. It expands its width to fit children and can apply a uniform `gap` between them.
|
||||
|
||||
```rust
|
||||
pub struct FlexCol {
|
||||
pub gap: u16,
|
||||
// children: Vec<Arc<Widget>>, // Internal
|
||||
// size: (u16, u16), // Internal calculated size
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**:
|
||||
* `gap: u16`: The number of empty cells between each child element. Defaults to `0`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
FlexCol, gap: 2, {
|
||||
"Left Item"
|
||||
("Middle Item (colored)", 0xFFCC00)
|
||||
Div { "Right Item (a div)" }
|
||||
}
|
||||
}
|
||||
```
|
||||
This will render "Left Item", then a 2-cell gap, then "Middle Item", etc., all laid out horizontally.
|
||||
|
||||
### `Paginator`
|
||||
|
||||
A container element that displays only one of its children at a time. It provides built-in keyboard navigation to cycle through its children.
|
||||
|
||||
```rust
|
||||
pub struct Paginator {
|
||||
// children: Vec<Arc<Widget>>, // Internal
|
||||
// size: (u16, u16), // Internal calculated size
|
||||
// index: usize, // Internal current page index
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**: None specific to `Paginator`.
|
||||
* **Internal Behavior**:
|
||||
* Handles `crossterm::event::KeyCode::Tab` to advance to the next child. If at the last child, it wraps to the first.
|
||||
* Handles `crossterm::event::KeyCode::BackTab` (Shift+Tab) to go to the previous child. If at the first child, it wraps to the last.
|
||||
* **Usage**:
|
||||
```rust
|
||||
rsx! {
|
||||
Paginator {
|
||||
Div { "Page 1 Content" }
|
||||
FlexCol { "Page 2: Item A", "Item B" }
|
||||
"Page 3: Just some text."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Form Elements
|
||||
|
||||
### `Input`
|
||||
|
||||
An interactive element that allows users to type text. It manages its own internal state, cursor position, and handles basic text editing key presses.
|
||||
|
||||
```rust
|
||||
pub struct Input {
|
||||
pub state: State<String>, // Reactive state holding the input string
|
||||
// cursor: usize, // Internal cursor position
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**:
|
||||
* `state: State<String>`: A reactive state variable that holds the current text content of the input field. You can pass your own `State<String>` to bind to it, or `Input::new()` creates a default one.
|
||||
* **Internal Behavior**:
|
||||
* Handles `crossterm::event::KeyEvent` for:
|
||||
* `KeyCode::Char`: Inserts character at cursor.
|
||||
* `KeyCode::Backspace`: Deletes character before cursor.
|
||||
* `KeyCode::Delete`: Deletes character at cursor.
|
||||
* `KeyCode::Left`, `KeyCode::Right`: Moves cursor.
|
||||
* **Usage**:
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
let my_input_state = use_state(String::from("Initial Text"));
|
||||
|
||||
rsx! {
|
||||
@Transform::new().dimensions(30, 1).padding(1, 0);
|
||||
@Style { background: Background::Outline(0x888888), foreground: Some(0xFFFFFF) };
|
||||
Input, state: my_input_state, { }
|
||||
}
|
||||
// You can access my_input_state.get_dl() elsewhere to get the current value.
|
||||
```
|
||||
Note that while the `Input` element has a `state` field, it's not declared as a dependency with `%` in `rsx!`. This is because `Input` internally manages its own `State<String>` and triggers its own re-renders when the text changes. You would use `%` if another widget needed to react to changes in `my_input_state`.
|
||||
|
||||
## Display Elements
|
||||
|
||||
### `Heading`
|
||||
|
||||
An element that renders text using FIGlet ASCII art fonts.
|
||||
|
||||
```rust
|
||||
pub struct Heading {
|
||||
pub font: FIGfont, // The FIGlet font to use
|
||||
pub smooth: bool, // Whether to replace '-' with '─' and '|' with '│'
|
||||
// children: Vec<Arc<Widget>>, // Internal: stores text children
|
||||
}
|
||||
```
|
||||
|
||||
* **Properties**:
|
||||
* `font: FIGfont`: The FIGlet font instance to use. You typically use `FIGfont::standard().unwrap()` or load a custom font.
|
||||
* `smooth: bool`: If `true`, replaces standard ASCII box drawing characters with Unicode smooth box drawing characters for a cleaner look. Defaults to `false`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
use figlet_rs::FIGfont; // Import for `FIGfont` type
|
||||
|
||||
rsx! {
|
||||
// Default standard font, not smooth
|
||||
Heading { "OSUI" }
|
||||
|
||||
// Using a custom font and smoothing
|
||||
Heading, font: FIGfont::big().unwrap(), smooth: true, { "Big Title" }
|
||||
}
|
||||
```
|
||||
Note that `Heading` expects `String` children (or `(String, u32)` children) for its text content. It concatenates all string children and renders them as one FIGlet text block.
|
||||
|
||||
These built-in elements provide a solid foundation for constructing diverse and interactive terminal user interfaces with OSUI.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
# Extensions API Reference
|
||||
|
||||
OSUI's extension system provides a powerful mechanism for adding global behaviors, cross-cutting concerns, or custom rendering logic to your application. Extensions are independent units that can hook into the `Screen`'s lifecycle and rendering pipeline.
|
||||
|
||||
## `Extension` Trait
|
||||
|
||||
The core trait that all extensions must implement.
|
||||
|
||||
```rust
|
||||
pub trait Extension {
|
||||
/// Called once when the screen starts running, before the main loop begins.
|
||||
///
|
||||
/// Useful for setting up resources, spawning threads, or initializing global state.
|
||||
fn init(&mut self, _screen: Arc<Screen>) {}
|
||||
|
||||
/// Called when the screen is being closed, after the main loop has exited.
|
||||
///
|
||||
/// Useful for cleanup, restoring terminal state, or saving data.
|
||||
fn on_close(&mut self, _screen: Arc<Screen>) {}
|
||||
|
||||
/// Called for each top-level widget just before its `Element::render` method is invoked.
|
||||
///
|
||||
/// This hook provides an opportunity to inspect or modify the `RenderScope`
|
||||
/// or the widget itself before it draws its content.
|
||||
fn render_widget(&mut self, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
||||
}
|
||||
```
|
||||
|
||||
* `init(&mut self, screen: Arc<Screen>)`: For one-time setup.
|
||||
* `on_close(&mut self, screen: Arc<Screen>)`: For cleanup.
|
||||
* `render_widget(&mut self, scope: &mut RenderScope, widget: &Arc<Widget>)`: Called per widget during each render frame.
|
||||
|
||||
## `Event` Trait
|
||||
|
||||
A marker trait for types that can be dispatched as events within OSUI's system.
|
||||
|
||||
```rust
|
||||
pub trait Event: Send + Sync {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
* `as_any()`: Required for type-erasing the event, allowing `dyn Event` to be downcasted to concrete types.
|
||||
* **Helper Method**: `impl<'a> dyn Event + 'a` has a `get<T: Event + 'static>(&self) -> Option<&T>` method for convenient downcasting.
|
||||
|
||||
## `Handler<E>` Component
|
||||
|
||||
A component that wraps a closure, allowing a `Widget` to subscribe to a specific `Event` type. When an event of type `E` is dispatched to the widget (via `Widget::event`), this handler's closure is called.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Handler<E: Event>(Arc<Mutex<dyn FnMut(&Arc<Widget>, &E) + Send + Sync>>);
|
||||
```
|
||||
|
||||
### `Handler<E>` Methods
|
||||
|
||||
* `new<F: FnMut(&Arc<Widget>, &E) + Send + Sync + 'static>(f: F) -> Handler<E>`: Creates a new handler. The closure receives the `Arc<Widget>` it's attached to and the event.
|
||||
* `call(&self, w: &Arc<Widget>, e: &E)`: Manually calls the wrapped closure. Used internally by `Widget::event`.
|
||||
|
||||
### Usage Example: `Handler`
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
use crossterm::event::{KeyCode, KeyEvent, Event as CrosstermEvent};
|
||||
|
||||
// In your main function or widget:
|
||||
let my_widget = rsx! {
|
||||
@Handler::new({
|
||||
let my_local_data = "some_context".to_string();
|
||||
move |widget_ref, event: &CrosstermEvent| {
|
||||
if let CrosstermEvent::Key(KeyEvent { code: KeyCode::Char('x'), .. }) = event {
|
||||
println!("Widget {:p} received 'x' key. Context: {}", Arc::as_ptr(widget_ref), my_local_data);
|
||||
// You can also get/set components on the widget_ref:
|
||||
// if let Some(mut transform) = widget_ref.get::<Transform>() { ... }
|
||||
}
|
||||
}
|
||||
});
|
||||
Div { "Press 'x'" }
|
||||
}.draw(&screen);
|
||||
```
|
||||
|
||||
## Built-in Extensions
|
||||
|
||||
OSUI provides several pre-implemented extensions for common functionalities:
|
||||
|
||||
### `IdExtension`
|
||||
|
||||
Provides a mechanism to retrieve a widget by a unique ID. It relies on the `Id` component.
|
||||
|
||||
```rust
|
||||
pub struct IdExtension(pub Arc<Screen>);
|
||||
component!(Id(pub usize)); // The component used for identifying widgets
|
||||
|
||||
impl Extension for Arc<IdExtension> {} // Note: Implements for Arc<IdExtension>
|
||||
```
|
||||
|
||||
* `new(screen: Arc<Screen>) -> Arc<Self>`: Creates a new `IdExtension` instance.
|
||||
* `get_element(self: &Arc<IdExtension>, id: usize) -> Option<Arc<Widget>>`: Iterates through all widgets on the screen to find one with the matching `Id` component.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let id_ext = IdExtension::new(screen.clone());
|
||||
screen.extension(id_ext.clone()); // Register the extension
|
||||
|
||||
let my_widget_id = 123;
|
||||
rsx! {
|
||||
@Id(my_widget_id); // Attach the Id component
|
||||
Div { "My Identifiable Div" }
|
||||
}.draw(&screen);
|
||||
|
||||
// Later, retrieve the widget by ID
|
||||
if let Some(widget) = id_ext.get_element(my_widget_id) {
|
||||
// Do something with the widget
|
||||
println!("Found widget with ID {}", my_widget_id);
|
||||
}
|
||||
```
|
||||
|
||||
### `InputExtension`
|
||||
|
||||
Handles raw terminal input using `crossterm` and dispatches `crossterm::event::Event`s to all widgets.
|
||||
|
||||
```rust
|
||||
pub struct InputExtension;
|
||||
impl Extension for InputExtension { /* ... */ }
|
||||
impl crate::extensions::Event for crossterm::event::Event { /* ... */ } // Implemented for crossterm events
|
||||
```
|
||||
|
||||
* **Behavior**: Enables raw mode on `init()`, continuously reads events, and calls `widget.event(&e)` for every widget. Disables raw mode on `on_close()`.
|
||||
* **Usage**: **Essential for any interactive application that needs keyboard input.**
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
screen.extension(InputExtension); // Enable input handling
|
||||
```
|
||||
|
||||
### `TickExtension`
|
||||
|
||||
Dispatches `TickEvent`s at a specified rate (in ticks per second). Useful for animations, timers, or periodic updates.
|
||||
|
||||
```rust
|
||||
pub struct TickExtension(pub u16); // Rate in ticks per second
|
||||
event!(TickEvent(pub u32)); // The event dispatched by this extension
|
||||
|
||||
impl Extension for TickExtension { /* ... */ }
|
||||
```
|
||||
|
||||
* **Constructor**: Takes `u16` representing ticks per second.
|
||||
* **Behavior**: Spawns a thread that sends a `TickEvent(tick_count)` to all widgets at the specified rate.
|
||||
* **Usage**:
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
let screen = Screen::new();
|
||||
screen.extension(TickExtension(30)); // 30 ticks per second (approx 33ms interval)
|
||||
|
||||
// A widget that reacts to ticks
|
||||
let tick_counter = use_state(0);
|
||||
rsx! {
|
||||
@Handler::new({
|
||||
let tick_counter = tick_counter.clone();
|
||||
move |_, e: &TickEvent| {
|
||||
// The `e` is the TickEvent(tick_count)
|
||||
**tick_counter.get() = e.0; // Update state with current tick count
|
||||
}
|
||||
});
|
||||
%tick_counter
|
||||
Div { "Current Tick: {tick_counter}" }
|
||||
}.draw(&screen);
|
||||
```
|
||||
|
||||
### `VelocityExtension`
|
||||
|
||||
Automatically updates the `Transform` of widgets that have a `Velocity` component, simulating movement.
|
||||
|
||||
```rust
|
||||
pub struct VelocityExtension;
|
||||
component!(Velocity(pub i32, pub i32)); // (velocity_x, velocity_y)
|
||||
|
||||
impl Extension for VelocityExtension { /* ... */ }
|
||||
```
|
||||
|
||||
* **Behavior**: Spawns a thread that periodically iterates through all widgets. If a widget has both a `Velocity` and a `Transform` component, it updates the `Transform::x` and `Transform::y` based on the velocity.
|
||||
* **Note**: `VelocityExtension` works by directly modifying `Position::Const` values. If `Transform::x` or `Transform::y` are `Center` or `End`, velocity won't apply to that axis.
|
||||
* **Usage**:
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
let screen = Screen::new();
|
||||
screen.extension(VelocityExtension);
|
||||
|
||||
rsx! {
|
||||
@Transform::new().x(5).y(5); // Initial position
|
||||
@Velocity(10, 0); // Move 10 cells per second horizontally (positive X)
|
||||
Div { "Moving Right" }
|
||||
|
||||
@Transform::new().x(50).y(10);
|
||||
@Velocity(-5, 5); // Move left and down
|
||||
Div { "Moving Left-Down" }
|
||||
}.draw(&screen);
|
||||
```
|
||||
|
||||
Extensions are a powerful way to add modular, global, or cross-cutting features to your OSUI applications, keeping your core UI definitions focused on structure and reactivity.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
# Macros API Reference
|
||||
|
||||
OSUI provides several procedural macros to simplify the definition of common structures like events, components, and especially UI layouts using a declarative syntax.
|
||||
|
||||
## `event!` Macro
|
||||
|
||||
Declares a struct that implements the `Event` trait. This simplifies the creation of custom event types for OSUI's reactive system.
|
||||
|
||||
### Syntax
|
||||
|
||||
```rust
|
||||
event!(Name); // Unit struct
|
||||
event!(Name { field: Type, ... }); // Named struct with fields
|
||||
event!(Name (Type, Type, ...)); // Tuple struct
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
// Defines a unit struct `Clicked` that implements `osui::extensions::Event`.
|
||||
event!(Clicked);
|
||||
|
||||
// Defines a named struct `Resized` with `width` and `height` fields, implements `Event`.
|
||||
event!(Resized { width: u32, height: u32 });
|
||||
|
||||
// Defines a tuple struct `Moved` with two `u32` fields, implements `Event`.
|
||||
event!(Moved(u32, u32));
|
||||
```
|
||||
|
||||
### How it Works
|
||||
|
||||
The macro automatically adds the necessary `#[derive(Debug, Clone)]` and the `impl Event for ...` block, including the `as_any()` method required for type erasure.
|
||||
|
||||
## `component!` Macro
|
||||
|
||||
Declares a struct that implements the `Component` trait. Components allow widgets to extend their behavior or contain additional data. This macro helps avoid boilerplate.
|
||||
|
||||
### Syntax
|
||||
|
||||
```rust
|
||||
component!(Name); // Unit struct
|
||||
component!(Name { field: Type, ... }); // Named struct with fields
|
||||
component!(Name (Type, Type, ...)); // Tuple struct
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
// Defines a unit struct `Focusable` that implements `osui::widget::Component`.
|
||||
component!(Focusable);
|
||||
|
||||
// Defines a named struct `Tooltip` with a `text` field, implements `Component`.
|
||||
component!(Tooltip { text: String });
|
||||
|
||||
// Defines a tuple struct `Size` with two `u32` fields, implements `Component`.
|
||||
component!(Size(u32, u32));
|
||||
```
|
||||
|
||||
### How it Works
|
||||
|
||||
Similar to `event!`, this macro adds `#[derive(Debug, Clone)]` and the `impl Component for ...` block, providing `as_any()` and `as_any_mut()`.
|
||||
|
||||
## `event_handler!` Macro
|
||||
|
||||
Creates an event handler closure that safely calls a method on `self` within a `move` closure, handling the lifetime issues for `Arc` or raw pointers.
|
||||
|
||||
### Syntax
|
||||
|
||||
```rust
|
||||
event_handler!($self_ty:ty, $self:ident, $events:ident, $method:ident)
|
||||
```
|
||||
|
||||
* `$self_ty`: The type of `self` (e.g., `MyStruct`).
|
||||
* `$self`: The instance variable (e.g., `self`).
|
||||
* `$events`: The event source object (e.g., a `Handler::new` call or `widget.on(...)` if such an API existed) where you want to register the handler.
|
||||
* `$method`: The method on `$self` to be called when the event occurs.
|
||||
|
||||
### Example
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use crossterm::event::{KeyCode, KeyEvent, Event as CrosstermEvent};
|
||||
|
||||
struct MyApp {
|
||||
screen: Arc<Screen>,
|
||||
counter: State<u32>,
|
||||
}
|
||||
|
||||
impl MyApp {
|
||||
fn new(screen: Arc<Screen>) -> Self {
|
||||
Self {
|
||||
screen: screen.clone(),
|
||||
counter: use_state(0),
|
||||
}
|
||||
}
|
||||
|
||||
// Method that will handle the event
|
||||
fn handle_key_event(&mut self, _widget: &Arc<Widget>, event: &CrosstermEvent) {
|
||||
if let CrosstermEvent::Key(KeyEvent { code: KeyCode::Char('a'), .. }) = event {
|
||||
**self.counter.get() += 1;
|
||||
println!("'a' pressed! Counter: {}", self.counter.get_dl());
|
||||
}
|
||||
}
|
||||
|
||||
fn build_ui(mut self: Arc<Self>) -> Rsx { // Self must be Arc<Self> here for cloning
|
||||
let counter_dep = self.counter.clone();
|
||||
rsx! {
|
||||
// Attach a Handler component to the root widget
|
||||
@Handler::new({
|
||||
let self_ref = Arc::downgrade(&self); // Use Weak for self-referential closures if needed for more complex scenarios, or clone Arc directly.
|
||||
// For direct method calls, a raw pointer cast is used by the macro,
|
||||
// but generally it's safer to clone Arcs for closures or use Weak.
|
||||
// The macro's internal implementation uses unsafe raw pointer:
|
||||
// let self_ptr = &*self as *const Self as *mut Self;
|
||||
// move |widget, event| unsafe { (*self_ptr).handle_key_event(widget, event) }
|
||||
// Let's use a safe Arc clone here for demonstration
|
||||
let app_clone = self.clone();
|
||||
move |widget, event| {
|
||||
app_clone.clone().handle_key_event(widget, event);
|
||||
}
|
||||
});
|
||||
%counter_dep
|
||||
Div {
|
||||
"Press 'a' to increment: {counter_dep}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The `event_handler!` macro as provided in the source code uses `unsafe` raw pointers.
|
||||
// In real-world code, using `Arc::clone` or `Arc::downgrade` (for weak references)
|
||||
// and then `upgrade()` within the closure is generally safer and idiomatic for
|
||||
// closures that need to refer back to their owning struct.
|
||||
// The provided macro is a low-level utility and care must be taken regarding lifetimes.
|
||||
```
|
||||
|
||||
### Safety Considerations
|
||||
|
||||
The provided `event_handler!` macro internally uses `unsafe` code to cast `self` to a raw mutable pointer and dereference it within the closure. **This is inherently unsafe** because it bypasses Rust's borrow checker. You *must* ensure that the instance referred to by the raw pointer outlives the closure. In complex scenarios (e.g., where the event handler could outlive the original struct), this can lead to use-after-free or data races. For safer patterns, consider:
|
||||
* Cloning `Arc`s for each capture in the closure.
|
||||
* Using `Arc::downgrade` for weak references if the closure might outlive the original `Arc`.
|
||||
|
||||
## `transform!` Macro
|
||||
|
||||
Creates a `Transform` struct with specified field values. This offers a more concise syntax for defining transforms compared to `Transform::new().field(...).field(...)`.
|
||||
|
||||
### Syntax
|
||||
|
||||
```rust
|
||||
transform!{ field: value, ... }
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
// Creates a Transform with x=10, y=20, and default values for others.
|
||||
let t1 = transform!{ x: 10, y: 20 };
|
||||
|
||||
// Creates a Transform centered horizontally, with full width and 2 units of padding.
|
||||
let t2 = transform!{ x: Center, width: Full, padding: (2, 2) };
|
||||
|
||||
rsx! {
|
||||
@transform!{ x: 5, y: 5, dimensions: (30, 10) };
|
||||
Div { "A fixed-size div at (5,5)" }
|
||||
}
|
||||
```
|
||||
|
||||
### How it Works
|
||||
|
||||
The macro expands to a `Transform::new()` call followed by setting each specified field using its `into()` method (which allows `u16` to convert to `Position::Const` or `Dimension::Const`).
|
||||
|
||||
## `rsx!` Macro
|
||||
|
||||
The primary macro for declaratively building UI element trees in OSUI. It supports static elements, dynamic elements with state dependencies, and component attachment.
|
||||
|
||||
### Syntax (Simplified Key Patterns)
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
// Text literal
|
||||
"Some text"
|
||||
|
||||
// Text literal with color
|
||||
("Some colored text", 0xFF0000)
|
||||
|
||||
// Static element: `static` keyword
|
||||
// ElementType, prop1: val1, ... { children }
|
||||
static MyElement, some_prop: true, { "Static child" }
|
||||
|
||||
// Static element (no properties):
|
||||
static MyElement { "Static child" }
|
||||
|
||||
// Dynamic element (default, no `static` keyword):
|
||||
// %dependency1 %dependency2 ... ElementType, prop1: val1, ... { children }
|
||||
%my_state
|
||||
MyDynamicElement, another_prop: "value", { "Dynamic child: {my_state}" }
|
||||
|
||||
// Dynamic element (no properties):
|
||||
%my_state
|
||||
MyDynamicElement { "Dynamic child: {my_state}" }
|
||||
|
||||
// Attaching components: `@ComponentType;`
|
||||
@Transform::new().center();
|
||||
@Style { background: Background::Solid(0x111111) };
|
||||
Div { "Div with Transform and Style" }
|
||||
|
||||
// Expanding another Rsx block: `function_call => (args)`
|
||||
my_sub_rsx_function => (arg1, arg2)
|
||||
}
|
||||
```
|
||||
|
||||
### How it Works (High-Level)
|
||||
|
||||
The `rsx!` macro recursively calls an internal `rsx_inner!` macro. It parses the declarative syntax and constructs a tree of `RsxElement` enums (either `RsxElement::Element` for static or `RsxElement::DynElement` for dynamic widgets). This `Rsx` tree is then used by `Rsx::draw` or `Rsx::draw_parent` to create the actual `Widget` instances on the `Screen`.
|
||||
|
||||
* **`static`**: Creates a `StaticWidget` for the root `Element`.
|
||||
* **No `static`**: Creates a `DynWidget` whose element-creation closure will be re-evaluated on dependency changes.
|
||||
* **`%dependency`**: Automatically clones the `Arc<State<T>>` (or other `DependencyHandler`) and registers it with the `DynWidget`.
|
||||
* **`@Component`**: Attaches the specified component to the `Widget` being created.
|
||||
* **`properties: value`**: Sets public fields on the `Element` struct during its construction.
|
||||
* **`{ children }`**: Recursively processes nested `rsx!` blocks to create child elements.
|
||||
|
||||
The `rsx!` macro is the most idiomatic way to build user interfaces in OSUI, offering a concise and powerful syntax for defining complex UI hierarchies and reactivity.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# `RenderScope` API Reference
|
||||
|
||||
The `RenderScope` is a crucial internal component of OSUI's rendering engine. It acts as a drawing canvas and context for individual widgets, accumulating drawing instructions (text, shapes, colors) and managing transformation states. Widgets use `RenderScope` to define what and where they want to draw, and the `Screen` then flushes these instructions to the terminal.
|
||||
|
||||
## `RenderScope` Struct
|
||||
|
||||
```rust
|
||||
pub struct RenderScope {
|
||||
transform: RawTransform,
|
||||
render_stack: Vec<RenderMethod>, // Internal list of drawing commands
|
||||
parent_width: u16,
|
||||
parent_height: u16,
|
||||
style: Style,
|
||||
}
|
||||
```
|
||||
|
||||
* `transform`: The `RawTransform` representing the current widget's absolute position and size. This is resolved from a `Transform` component.
|
||||
* `render_stack`: A queue of `RenderMethod` enums (internal) that define individual draw operations.
|
||||
* `parent_width`, `parent_height`: The dimensions of the *parent* container, used for resolving `Position::Center`, `Position::End`, and `Dimension::Full`.
|
||||
* `style`: The `Style` component currently applied to this scope.
|
||||
|
||||
## `RenderScope` Methods
|
||||
|
||||
### `RenderScope::new()`
|
||||
|
||||
Creates a new, empty `RenderScope` with default transform and style.
|
||||
|
||||
```rust
|
||||
pub fn new() -> RenderScope
|
||||
```
|
||||
|
||||
* **Returns**: A new `RenderScope` instance.
|
||||
* **Usage**: Called internally by the `Screen` for each widget's render pass.
|
||||
|
||||
### `set_transform_raw(&mut self, transform: RawTransform)`
|
||||
|
||||
Directly sets the raw (absolute) transform for this scope. This bypasses the declarative `Transform` component rules.
|
||||
|
||||
```rust
|
||||
pub fn set_transform_raw(&mut self, transform: RawTransform)
|
||||
```
|
||||
|
||||
* `transform`: The `RawTransform` to apply.
|
||||
* **Usage**: Rarely used directly by application developers; primarily for internal layout calculations or advanced custom elements.
|
||||
|
||||
### `set_transform(&mut self, transform: &Transform)`
|
||||
|
||||
Applies a declarative `Transform` component to this scope, resolving its position and dimensions into concrete values based on the `parent_width` and `parent_height`.
|
||||
|
||||
```rust
|
||||
pub fn set_transform(&mut self, transform: &Transform)
|
||||
```
|
||||
|
||||
* `transform`: A reference to the `Transform` component.
|
||||
* **Usage**: Called by the `Screen` or a parent element before a child's `render` method to set up its coordinate system.
|
||||
|
||||
### `draw_text(&mut self, x: u16, y: u16, text: &str)`
|
||||
|
||||
Adds a plain text drawing instruction to the render stack. The text will be rendered relative to the `RenderScope`'s `x` and `y` coordinates, and will use the `RenderScope`'s current `style.foreground` if set.
|
||||
|
||||
```rust
|
||||
pub fn draw_text(&mut self, x: u16, y: u16, text: &str)
|
||||
```
|
||||
|
||||
* `x`, `y`: Relative coordinates within the current `RenderScope`'s content area.
|
||||
* `text`: The string to draw.
|
||||
* **Side Effect**: Updates the `RenderScope`'s `transform.width` and `transform.height` to encompass the drawn text if it's larger than the current dimensions. This is how `Dimension::Content` works.
|
||||
|
||||
### `draw_text_inverted(&mut self, x: u16, y: u16, text: &str)`
|
||||
|
||||
Adds a text drawing instruction where the background and foreground colors are swapped.
|
||||
|
||||
```rust
|
||||
pub fn draw_text_inverted(&mut self, x: u16, y: u16, text: &str)
|
||||
```
|
||||
|
||||
* `x`, `y`, `text`: Same as `draw_text`.
|
||||
* **Usage**: Useful for creating highlighted text, like a cursor in an input field.
|
||||
|
||||
### `draw_text_colored(&mut self, x: u16, y: u16, text: &str, color: u32)`
|
||||
|
||||
Adds a text drawing instruction with a specific 24-bit RGB foreground color. This color overrides the `RenderScope`'s `style.foreground` for this specific text.
|
||||
|
||||
```rust
|
||||
pub fn draw_text_colored(&mut self, x: u16, y: u16, text: &str, color: u32)
|
||||
```
|
||||
|
||||
* `x`, `y`, `text`: Same as `draw_text`.
|
||||
* `color`: The 24-bit RGB color (e.g., `0xFF00FF`).
|
||||
|
||||
### `draw_rect(&mut self, x: u16, y: u16, width: u16, height: u16, color: u32)`
|
||||
|
||||
Adds a filled rectangle drawing instruction to the render stack.
|
||||
|
||||
```rust
|
||||
pub fn draw_rect(&mut self, x: u16, y: u16, width: u16, height: u16, color: u32)
|
||||
```
|
||||
|
||||
* `x`, `y`: Relative top-left coordinates.
|
||||
* `width`, `height`: Dimensions of the rectangle.
|
||||
* `color`: The 24-bit RGB fill color.
|
||||
* **Side Effect**: Updates the `RenderScope`'s `transform.width` and `transform.height` to encompass the drawn rectangle if it's larger.
|
||||
|
||||
### `use_area(&mut self, width: u16, height: u16)`
|
||||
|
||||
Manually ensures that the `RenderScope`'s internal `transform.width` and `transform.height` are at least the specified values.
|
||||
|
||||
```rust
|
||||
pub fn use_area(&mut self, width: u16, height: u16)
|
||||
```
|
||||
|
||||
* `width`, `height`: Minimum width and height to ensure.
|
||||
* **Usage**: For elements that might not draw content but have a conceptual size (e.g., a spacer, or a container that needs a minimum dimension).
|
||||
|
||||
### `draw(&self)`
|
||||
|
||||
Executes all accumulated drawing instructions in the `render_stack` and flushes them to the terminal. This also draws the `RenderScope`'s background style (`Style::Background`).
|
||||
|
||||
```rust
|
||||
pub fn draw(&self)
|
||||
```
|
||||
|
||||
* **Behavior**:
|
||||
1. Applies the `RenderScope`'s `style.background` (Solid, Outline, RoundedOutline).
|
||||
2. Iterates through `render_stack`, applying text colors (if `style.foreground` is `Some`) or specific `draw_text_colored` colors, and drawing rectangles.
|
||||
3. Uses `utils::print_liner` for efficient output.
|
||||
* **Usage**: Called internally by the `Screen` or parent elements after `render` and `after_render` for a child is complete.
|
||||
|
||||
### `clear(&mut self)`
|
||||
|
||||
Clears all accumulated drawing instructions, resets the `transform` to default (all zeros), and resets the `style` to `Style::new()`.
|
||||
|
||||
```rust
|
||||
pub fn clear(&mut self)
|
||||
```
|
||||
|
||||
* **Usage**: Called by the `Screen` before rendering each top-level widget, and by container elements before rendering each of their children, to provide a clean drawing context.
|
||||
|
||||
### `get_size(&self) -> (u16, u16)`
|
||||
|
||||
Returns the current width and height of the `RenderScope` as determined by its `transform.width` and `transform.height`.
|
||||
|
||||
```rust
|
||||
pub fn get_size(&self) -> (u16, u16)
|
||||
```
|
||||
|
||||
### `get_size_or(&self, width: u16, height: u16) -> (u16, u16)`
|
||||
|
||||
Returns the current width and height, or falls back to the provided `width` and `height` if the current dimensions are zero.
|
||||
|
||||
```rust
|
||||
pub fn get_size_or(&self, width: u16, height: u16) -> (u16, u16)
|
||||
```
|
||||
|
||||
### `get_size_or_parent(&self) -> (u16, u16)`
|
||||
|
||||
Returns the current width and height, or falls back to the parent's dimensions (`parent_width`, `parent_height`) if the current dimensions are zero.
|
||||
|
||||
```rust
|
||||
pub fn get_size_or_parent(&self) -> (u16, u16)
|
||||
```
|
||||
|
||||
### `get_parent_size(&self) -> (u16, u16)`
|
||||
|
||||
Returns the width and height of the `RenderScope`'s parent container.
|
||||
|
||||
```rust
|
||||
pub fn get_parent_size(&self) -> (u16, u16)
|
||||
```
|
||||
|
||||
### `set_parent_size(&mut self, width: u16, height: u16)`
|
||||
|
||||
Sets the dimensions of the parent container for this `RenderScope`. This is crucial for children to correctly resolve `Dimension::Full`, `Position::Center`, and `Position::End`.
|
||||
|
||||
```rust
|
||||
pub fn set_parent_size(&mut self, width: u16, height: u16)
|
||||
```
|
||||
|
||||
* **Usage**: Primarily used by container elements in their `after_render` method before rendering their children.
|
||||
|
||||
### `get_transform_mut(&mut self) -> &mut RawTransform`
|
||||
|
||||
Returns a mutable reference to the `RenderScope`'s internal `RawTransform`.
|
||||
|
||||
```rust
|
||||
pub fn get_transform_mut(&mut self) -> &mut RawTransform
|
||||
```
|
||||
|
||||
* **Usage**: Allows elements or extensions to directly manipulate the resolved position and size.
|
||||
|
||||
### `get_transform(&self) -> &RawTransform`
|
||||
|
||||
Returns an immutable reference to the `RenderScope`'s internal `RawTransform`.
|
||||
|
||||
```rust
|
||||
pub fn get_transform(&self) -> &RawTransform
|
||||
```
|
||||
|
||||
### `set_style(&mut self, style: Style)`
|
||||
|
||||
Sets the `Style` for the current render scope. This style applies to subsequent drawing instructions unless overridden by a colored text instruction.
|
||||
|
||||
```rust
|
||||
pub fn set_style(&mut self, style: Style)
|
||||
```
|
||||
|
||||
* `style`: The `Style` to apply.
|
||||
* **Usage**: Called by the `Screen` or a parent element before a child's `render` method to set up its visual appearance.
|
||||
|
||||
### `get_style(&mut self) -> &mut Style`
|
||||
|
||||
Gets a mutable reference to the current `Style` in the scope.
|
||||
|
||||
```rust
|
||||
pub fn get_style(&mut self) -> &mut Style
|
||||
```
|
||||
|
||||
`RenderScope` is the bridge between your declarative UI definitions and the actual terminal output. Understanding its methods is key to creating custom elements and mastering OSUI's rendering pipeline.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# `Screen` API Reference
|
||||
|
||||
The `Screen` struct is the central orchestrator of an OSUI application. It manages the UI tree, handles the rendering loop, and coordinates with extensions.
|
||||
|
||||
## `Screen` Struct
|
||||
|
||||
```rust
|
||||
pub struct Screen {
|
||||
pub widgets: Mutex<Vec<Arc<Widget>>>,
|
||||
// Internal fields for extensions and running state
|
||||
// extensions: Mutex<Vec<Arc<Mutex<Box<dyn Extension + Send + Sync>>>>>,
|
||||
// running: Mutex<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
* `widgets`: A `Mutex`-protected vector holding all top-level `Arc<Widget>` instances managed by this screen. These are the root elements of your UI tree.
|
||||
|
||||
## `Screen` Methods
|
||||
|
||||
### `Screen::new()`
|
||||
|
||||
Creates a new `Screen` instance, wrapped in an `Arc` for shared ownership.
|
||||
|
||||
```rust
|
||||
pub fn new() -> Arc<Self>
|
||||
```
|
||||
|
||||
* **Returns**: An `Arc<Screen>`.
|
||||
* **Usage**: The primary way to initialize your OSUI environment.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
```
|
||||
|
||||
### `Screen::draw<E: Element + 'static + Send + Sync>(self: &Arc<Self>, element: E) -> Arc<Widget>`
|
||||
|
||||
Draws a static element onto the screen. This creates a new `StaticWidget` internally and adds it to the screen's widget list.
|
||||
|
||||
```rust
|
||||
pub fn draw<E: Element + 'static + Send + Sync>(self: &Arc<Self>, element: E) -> Arc<Widget>
|
||||
```
|
||||
|
||||
* `element`: The element to draw. This can be any type that implements the `Element` trait, such as `String`, `Div`, `Input`, etc.
|
||||
* **Returns**: An `Arc<Widget>` representing the newly created static widget.
|
||||
* **Usage**: Ideal for simple, non-reactive elements. Often used as the final step after defining your UI with `rsx!`.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let my_widget = screen.draw(String::from("Hello, OSUI!"));
|
||||
// Equivalent to: rsx! { "Hello, OSUI!" }.draw(&screen);
|
||||
```
|
||||
|
||||
### `Screen::draw_box(self: &Arc<Self>, element: BoxedElement) -> Arc<Widget>`
|
||||
|
||||
Draws a `BoxedElement` (a boxed trait object implementing `Element`) as a static widget.
|
||||
|
||||
```rust
|
||||
pub fn draw_box(self: &Arc<Self>, element: BoxedElement) -> Arc<Widget>
|
||||
```
|
||||
|
||||
* `element`: A `Box<dyn Element + Send + Sync>`.
|
||||
* **Returns**: An `Arc<Widget>`.
|
||||
* **Usage**: Useful when you dynamically create a boxed element that you want to add as static content.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let my_boxed_element: BoxedElement = Box::new(Div::new());
|
||||
screen.draw_box(my_boxed_element);
|
||||
```
|
||||
|
||||
### `Screen::draw_widget(self: &Arc<Self>, widget: Arc<Widget>)`
|
||||
|
||||
Adds an already existing `Arc<Widget>` to the screen's list of top-level widgets.
|
||||
|
||||
```rust
|
||||
pub fn draw_widget(self: &Arc<Self>, widget: Arc<Widget>)
|
||||
```
|
||||
|
||||
* `widget`: The `Arc<Widget>` to add.
|
||||
* **Usage**: When you've manually constructed an `Arc<Widget>` (e.g., a `StaticWidget` or `DynWidget`) and want to display it. `Rsx::draw_parent` uses this internally for non-`NoRenderRoot` widgets.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let my_static_widget = Arc::new(Widget::Static(StaticWidget::new(Box::new(String::from("Manually created widget")))));
|
||||
screen.draw_widget(my_static_widget);
|
||||
```
|
||||
|
||||
### `Screen::draw_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(self: &Arc<Self>, element: F) -> Arc<Widget>`
|
||||
|
||||
Draws a dynamic element onto the screen. This element will be re-evaluated when its dependencies change.
|
||||
|
||||
```rust
|
||||
pub fn draw_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(self: &Arc<Self>, element: F) -> Arc<Widget>
|
||||
```
|
||||
|
||||
* `element`: A closure that returns a `WidgetLoad`. This closure encapsulates the logic for creating the widget's initial state and element.
|
||||
* **Returns**: An `Arc<Widget>` representing the newly created dynamic widget.
|
||||
* **Usage**: For reactive components whose content changes based on `State` or other dynamic factors.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let count = use_state(0);
|
||||
let my_dyn_widget = screen.draw_dyn({
|
||||
let count = count.clone();
|
||||
move || WidgetLoad::new(format!("Count: {}", *count.get()))
|
||||
});
|
||||
my_dyn_widget.dependency(count); // Explicitly declare dependency if not using rsx!
|
||||
```
|
||||
|
||||
### `Screen::draw_box_dyn(self: &Arc<Self>, element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>) -> Arc<Widget>`
|
||||
|
||||
Draws a dynamic element from a boxed closure. Similar to `draw_dyn` but takes a `Box<dyn FnMut() -> WidgetLoad>`.
|
||||
|
||||
```rust
|
||||
pub fn draw_box_dyn(self: &Arc<Self>, element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>) -> Arc<Widget>
|
||||
```
|
||||
|
||||
* `element`: A boxed closure.
|
||||
* **Returns**: An `Arc<Widget>`.
|
||||
* **Usage**: Less common for direct use, as `draw_dyn` covers most cases. Used internally by `Rsx::create_element`.
|
||||
|
||||
### `Screen::extension<E: Extension + Send + Sync + 'static>(self: &Arc<Self>, ext: E)`
|
||||
|
||||
Registers an extension with the screen. Extensions receive lifecycle events and can influence rendering.
|
||||
|
||||
```rust
|
||||
pub fn extension<E: Extension + Send + Sync + 'static>(self: &Arc<Self>, ext: E)
|
||||
```
|
||||
|
||||
* `ext`: An instance of a type that implements the `Extension` trait.
|
||||
* **Usage**: Essential for adding global functionalities like input handling or periodic updates.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
screen.extension(InputExtension); // Enable keyboard input
|
||||
screen.extension(TickExtension(100)); // Enable 100ms ticks
|
||||
```
|
||||
|
||||
### `Screen::run(self: &Arc<Self>) -> std::io::Result<()>`
|
||||
|
||||
Starts the main rendering loop of the application. This method blocks the current thread until `screen.close()` is called.
|
||||
|
||||
```rust
|
||||
pub fn run(self: &Arc<Self>) -> std::io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: `Ok(())` on successful exit, or an `Err` if a terminal operation fails.
|
||||
* **Behavior**:
|
||||
* Calls `init()` on all registered extensions.
|
||||
* Hides the terminal cursor.
|
||||
* Enters a loop that repeatedly calls `render()` and sleeps for 28ms (approx. 36 FPS).
|
||||
* When the loop exits (due to `close()` being called), it shows the cursor, clears the screen, and calls `on_close()` on all extensions.
|
||||
* **Usage**: The last call in your `main` function.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
// ... setup widgets and extensions ...
|
||||
screen.run()?; // Note the '?' for error propagation
|
||||
```
|
||||
|
||||
### `Screen::render(self: &Arc<Self>) -> std::io::Result<()>`
|
||||
|
||||
Performs a single rendering pass of all widgets on the screen.
|
||||
|
||||
```rust
|
||||
pub fn render(self: &Arc<Self>) -> std::io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: `Ok(())` or an `Err` on terminal failure.
|
||||
* **Behavior**:
|
||||
* Clears the terminal.
|
||||
* Initializes a new `RenderScope` with the current terminal dimensions.
|
||||
* Iterates through all widgets in `screen.widgets`:
|
||||
* If a widget has `NoRender` or `NoRenderRoot` components, it's skipped for direct rendering by the screen (its parent is responsible for rendering it).
|
||||
* Otherwise, it applies the widget's `Style` and `Transform` to the `RenderScope`.
|
||||
* Calls `Extension::render_widget` for each extension.
|
||||
* Calls `Element::render` on the widget's root element.
|
||||
* Draws the `RenderScope` to the terminal.
|
||||
* Calls `Element::after_render` on the widget's root element.
|
||||
* Calls `Widget::auto_refresh()` on dynamic widgets to check for state changes.
|
||||
* **Usage**: Primarily called internally by `Screen::run()`. You typically don't need to call this directly unless implementing a custom rendering loop.
|
||||
|
||||
### `Screen::close(self: &Arc<Self>)`
|
||||
|
||||
Signals the main rendering loop to terminate.
|
||||
|
||||
```rust
|
||||
pub fn close(self: &Arc<Self>)
|
||||
```
|
||||
|
||||
* **Behavior**: Sets an internal flag that causes the `Screen::run()` loop to exit on its next iteration. It also performs cleanup: shows the cursor, clears the terminal, and calls `on_close()` on all registered extensions.
|
||||
* **Usage**: Call this from an event handler or other logic to gracefully shut down your application.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
use crossterm::event::{KeyCode, KeyEvent, Event as CrosstermEvent};
|
||||
|
||||
let screen = Screen::new();
|
||||
screen.extension(InputExtension);
|
||||
rsx! {
|
||||
@Handler::new({
|
||||
let screen = screen.clone();
|
||||
move |_, e: &CrosstermEvent| {
|
||||
if let CrosstermEvent::Key(KeyEvent { code: KeyCode::Char('q'), .. }) = e {
|
||||
screen.close(); // Exit application on 'q'
|
||||
}
|
||||
}
|
||||
});
|
||||
"Press 'q' to quit"
|
||||
}.draw(&screen);
|
||||
screen.run()?;
|
||||
```
|
||||
@@ -0,0 +1,184 @@
|
||||
# `State` API Reference
|
||||
|
||||
OSUI provides a reactive state management system built around the `State<T>` struct and the `DependencyHandler` trait. This system allows your UI to automatically re-render when underlying data changes, eliminating the need for manual update calls in most cases.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### `DependencyHandler` Trait
|
||||
|
||||
This trait is implemented by types that can act as dependencies for dynamic widgets.
|
||||
|
||||
```rust
|
||||
pub trait DependencyHandler: std::fmt::Debug + Send + Sync {
|
||||
/// Called when a dependent (e.g., a `DynWidget`) is registered with this handler.
|
||||
fn add(&self);
|
||||
/// Returns `true` if the state has changed since the last check.
|
||||
fn check(&self) -> bool;
|
||||
}
|
||||
```
|
||||
|
||||
* `add()`: Increments an internal counter, indicating that another `DynWidget` is listening to this state.
|
||||
* `check()`: Decrements an internal counter and returns `true` if the state was marked as changed *and* there are still dependents that haven't processed the change.
|
||||
|
||||
## `State<T>` Struct
|
||||
|
||||
`State<T>` is the primary type for managing reactive data in OSUI. It wraps your data `T` in an `Arc<Mutex<Inner<T>>>`, allowing for shared, thread-safe access and change 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/handlers listening
|
||||
changed: usize, // Number of dependents that need to be notified of a change
|
||||
}
|
||||
```
|
||||
|
||||
### `State` Creation
|
||||
|
||||
#### `use_state<T>(v: T) -> State<T>`
|
||||
|
||||
A convenience function to create a new `State` instance.
|
||||
|
||||
```rust
|
||||
pub fn use_state<T>(v: T) -> State<T>
|
||||
```
|
||||
|
||||
* `v`: The initial value for the state.
|
||||
* **Returns**: A new `State<T>`.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let counter = use_state(0);
|
||||
let name = use_state(String::from("Alice"));
|
||||
```
|
||||
|
||||
### `State<T>` Methods
|
||||
|
||||
#### `get_dl(&self) -> T` (Clone required for `T`)
|
||||
|
||||
Gets a **cloned** value of the inner data. This is recommended to avoid deadlocks when accessing the state from multiple threads or within complex UI logic, as it doesn't hold the `Mutex` lock.
|
||||
|
||||
```rust
|
||||
pub fn get_dl(&self) -> T
|
||||
```
|
||||
|
||||
* **Requires**: `T: Clone`.
|
||||
* **Returns**: A cloned instance of the inner `value`.
|
||||
|
||||
```rust
|
||||
let count_val = counter.get_dl();
|
||||
println!("Current count: {}", count_val);
|
||||
```
|
||||
|
||||
#### `get(&self) -> MutexGuard<'_, Inner<T>>`
|
||||
|
||||
Obtains a `MutexGuard` for the `Inner<T>` struct. This allows direct read and write access to the underlying `value`. When the `MutexGuard` is dropped (or explicitly dereferenced mutably), the state is marked as changed.
|
||||
|
||||
```rust
|
||||
pub fn get(&self) -> MutexGuard<'_, Inner<T>>
|
||||
```
|
||||
|
||||
* **Returns**: A `MutexGuard` that dereferences to `&Inner<T>`.
|
||||
* **Usage**: For both reading and mutating the state.
|
||||
|
||||
```rust
|
||||
// Reading
|
||||
let inner_state = counter.get();
|
||||
println!("Count from guard: {}", inner_state.value);
|
||||
|
||||
// Mutating (will mark state as changed)
|
||||
let mut inner_state = counter.get();
|
||||
inner_state.value += 1; // Direct mutation
|
||||
*inner_state = 5; // Replaces the entire Inner struct (less common for value)
|
||||
```
|
||||
|
||||
#### `set(&self, v: T)`
|
||||
|
||||
Sets the inner value directly and unconditionally marks the state as changed, notifying all listening dependents.
|
||||
|
||||
```rust
|
||||
pub fn set(&self, v: T)
|
||||
```
|
||||
|
||||
* `v`: The new value for the state.
|
||||
|
||||
```rust
|
||||
counter.set(10); // Sets count to 10 and triggers a refresh for dependents
|
||||
```
|
||||
|
||||
#### `update(&self)`
|
||||
|
||||
Manually marks the state as changed without modifying its value. This is useful if you mutate the inner value directly through `get()` and then want to explicitly trigger a refresh *after* the `MutexGuard` has been dropped, or if the change is internal to `T` and not visible via `DerefMut`.
|
||||
|
||||
```rust
|
||||
pub fn update(&self)
|
||||
```
|
||||
|
||||
```rust
|
||||
// Example where `update` might be useful (less common with DerefMut):
|
||||
let mut inner_data = counter.get();
|
||||
// Perform complex operations on inner_data.value
|
||||
// ...
|
||||
// Dropping inner_data will mark as changed, so update() might be redundant here
|
||||
// But if you had a situation where `DerefMut` didn't cover the change:
|
||||
// inner_data.some_internal_list.push(item);
|
||||
// drop(inner_data); // Dropping the guard normally triggers update
|
||||
// counter.update(); // Manual update if needed for some reason (e.g., if you only had an immutable guard before)
|
||||
```
|
||||
|
||||
### `State<T>` and `DependencyHandler` Implementation
|
||||
|
||||
`State<T>` implements `DependencyHandler`, enabling it to participate in OSUI's reactive system:
|
||||
|
||||
* `add()`: When a `DynWidget` declares a dependency on a `State<T>` (e.g., using `%my_state` in `rsx!`), this method is called. It increments `inner.dependencies`.
|
||||
* `check()`: During `DynWidget::auto_refresh`, this method is called. It checks if `inner.changed > 0`. If true, it decrements `inner.changed` and returns `true`, indicating the widget needs to be rebuilt.
|
||||
|
||||
### `Deref` and `DerefMut` for `Inner<T>`
|
||||
|
||||
The `Inner<T>` struct implements `Deref` and `DerefMut` to its inner `value: T`. This allows you to directly access and modify the `T` value through the `MutexGuard`.
|
||||
|
||||
* `impl Deref for Inner<T>`: Allows `*inner_state` to yield `&T`.
|
||||
* `impl DerefMut for Inner<T>`: Allows `*inner_state = ...` or `inner_state.mutate_field = ...` to yield `&mut T`. **Crucially, when `deref_mut` is called, it sets `inner.changed = inner.dependencies`, ensuring all registered dependents are notified.**
|
||||
|
||||
```rust
|
||||
let my_state = use_state(0);
|
||||
|
||||
// Directly read via Deref:
|
||||
let guard = my_state.get();
|
||||
println!("{}", *guard); // Prints the value of the integer
|
||||
|
||||
// Directly mutate via DerefMut:
|
||||
let mut guard = my_state.get();
|
||||
*guard += 1; // Mutates the integer, triggers 'changed' flag
|
||||
drop(guard); // The guard is dropped here, releasing the mutex
|
||||
|
||||
// The DynWidget dependent on `my_state` will now refresh on the next auto_refresh cycle.
|
||||
```
|
||||
|
||||
### `Display` for `State<T>`
|
||||
|
||||
`State<T>` also implements `Display` if `T` implements `Display`. This is very convenient for embedding `State` values directly into strings in `rsx!` for display.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
let my_str_state = use_state(String::from("World"));
|
||||
|
||||
rsx! {
|
||||
%my_str_state
|
||||
Div {
|
||||
// This works because State<String> implements Display
|
||||
"Hello, {my_str_state}!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `State` system, combined with `DynWidget` and the `rsx!` macro, forms the backbone of OSUI's reactive programming model, allowing for efficient and automatic UI updates in response to data changes.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# Style API Reference
|
||||
|
||||
The `style` module defines the fundamental structures for managing rendering geometry, positioning, sizing, and visual appearance of OSUI widgets. These types are essential for controlling how your UI elements are laid out and drawn.
|
||||
|
||||
## `RawTransform` Struct
|
||||
|
||||
`RawTransform` holds the concrete, resolved layout information for a widget *after* layout calculations have been performed. It contains absolute pixel values.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawTransform {
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
pub px: u16, // Padding X
|
||||
pub py: u16, // Padding Y
|
||||
}
|
||||
```
|
||||
|
||||
* `x`, `y`: Absolute top-left coordinate of the widget in terminal cells.
|
||||
* `width`, `height`: Absolute dimensions of the widget in terminal cells.
|
||||
* `px`, `py`: Resolved horizontal and vertical padding applied *around* the content area.
|
||||
|
||||
### `RawTransform::new()`
|
||||
|
||||
Creates a new `RawTransform` with all fields set to 0.
|
||||
|
||||
```rust
|
||||
pub fn new() -> RawTransform
|
||||
```
|
||||
|
||||
## `Position` Enum
|
||||
|
||||
`Position` defines horizontal or vertical positioning rules relative to a parent container.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Position {
|
||||
/// Fixed position in cells from the origin.
|
||||
Const(u16),
|
||||
/// Centered in the parent.
|
||||
Center,
|
||||
/// Aligned to the end (right or bottom) of the parent.
|
||||
End,
|
||||
}
|
||||
```
|
||||
|
||||
### `Position` Implementations
|
||||
|
||||
* `impl From<u16> for Position`: Allows `u16` values to be directly used where `Position` is expected (e.g., `Transform { x: 10 }`).
|
||||
* `use_position(&self, size: u16, parent: u16, m: i32, r: &mut u16)`: An internal method used by `Transform` to resolve the position based on the element's size, parent's size, and margin.
|
||||
|
||||
## `Dimension` Enum
|
||||
|
||||
`Dimension` defines sizing rules for width or height.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Dimension {
|
||||
/// Fills the available space from the parent.
|
||||
Full,
|
||||
/// Automatically sized to fit content.
|
||||
Content,
|
||||
/// Fixed size in cells.
|
||||
Const(u16),
|
||||
}
|
||||
```
|
||||
|
||||
### `Dimension` Implementations
|
||||
|
||||
* `impl From<u16> for Dimension`: Allows `u16` values to be directly used where `Dimension` is expected (e.g., `Transform { width: 50 }`).
|
||||
* `use_dimension(&self, parent: u16, r: &mut u16)`: An internal method used by `Transform` to resolve the dimension based on the parent's size.
|
||||
|
||||
## `Background` Enum
|
||||
|
||||
`Background` defines various visual appearances for a widget's background. Colors are 24-bit RGB values represented as `u32` (e.g., `0xFF0000` for red).
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Background {
|
||||
/// Transparent / no background.
|
||||
NoBackground,
|
||||
/// Draws a basic outline using the given color.
|
||||
Outline(u32),
|
||||
/// Draws a rounded outline using the given color.
|
||||
RoundedOutline(u32),
|
||||
/// Fills the background with the specified color.
|
||||
Solid(u32),
|
||||
}
|
||||
```
|
||||
|
||||
## `Transform` Component
|
||||
|
||||
The `Transform` component is the primary way to define a widget's desired layout properties. It is typically attached to a `Widget` via `rsx!` or `widget.component()`.
|
||||
|
||||
```rust
|
||||
component!(Transform {
|
||||
pub x: Position,
|
||||
pub y: Position,
|
||||
pub mx: i32, // Margin X
|
||||
pub my: i32, // Margin Y
|
||||
pub px: u16, // Padding X
|
||||
pub py: u16, // Padding Y
|
||||
pub width: Dimension,
|
||||
pub height: Dimension,
|
||||
});
|
||||
```
|
||||
|
||||
### `Transform` Methods
|
||||
|
||||
* `Transform::new() -> Transform`: Creates a default transform with top-left alignment (`Position::Const(0)`) and content sizing (`Dimension::Content`), with no margins or padding.
|
||||
* `Transform::center() -> Transform`: Shortcut for centering both horizontally and vertically (`Position::Center`) with content sizing.
|
||||
* `bottom(mut self) -> Self`: Sets `y` to `Position::End`.
|
||||
* `right(mut self) -> Self`: Sets `x` to `Position::End`.
|
||||
* `margin(mut self, x: i32, y: i32) -> Self`: Adds margin (offset) from parent edge. `x` is `mx`, `y` is `my`.
|
||||
* `padding(mut self, x: u16, y: u16) -> Self`: Adds internal spacing (padding) around content. `x` is `px`, `y` is `py`.
|
||||
* `dimensions(mut self, width: u16, height: u16) -> Self`: Sets constant dimensions (`Dimension::Const`) for `width` and `height`.
|
||||
* `use_dimensions(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform)`: Internal method that resolves `Dimension` rules into absolute values (`raw.width`, `raw.height`) based on parent size.
|
||||
* `use_position(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform)`: Internal method that resolves `Position` rules into absolute coordinates (`raw.x`, `raw.y`) based on parent size and the resolved widget size.
|
||||
|
||||
### Usage Example: `Transform`
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// A div that is 20x5 cells, centered, with 1 unit of padding
|
||||
@Transform::new().dimensions(20, 5).center().padding(1, 1);
|
||||
Div { "Centered Box" }
|
||||
|
||||
// A div aligned to the bottom-right, with 2 cells margin from edges
|
||||
@Transform::new().bottom().right().margin(2, 2);
|
||||
Div { "Bottom Right" }
|
||||
|
||||
// A div that fills the parent's width, is content-height, and starts at (0, 3)
|
||||
@Transform { x: 0, y: 3, width: Full, height: Content };
|
||||
Div { "Full Width Container" }
|
||||
}
|
||||
```
|
||||
|
||||
## `Style` Component
|
||||
|
||||
The `Style` component defines the visual appearance of a widget, primarily its background and foreground colors.
|
||||
|
||||
```rust
|
||||
component!(Style {
|
||||
pub background: Background,
|
||||
pub foreground: Option<u32>,
|
||||
});
|
||||
```
|
||||
|
||||
### `Style` Methods
|
||||
|
||||
* `Style::new() -> Self`: Creates a default style with `Background::NoBackground` and no foreground color (`None`).
|
||||
|
||||
### Usage Example: `Style`
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// A solid red background with white text
|
||||
@Style { background: Background::Solid(0xFF0000), foreground: Some(0xFFFFFF) };
|
||||
Div { "Red Box" }
|
||||
|
||||
// A green rounded outline with default foreground
|
||||
@Style { background: Background::RoundedOutline(0x00FF00), foreground: None };
|
||||
Div { "Green Rounded Outline" }
|
||||
}
|
||||
```
|
||||
|
||||
By combining `Transform` and `Style` components, developers have fine-grained control over the layout and aesthetics of every element in their OSUI applications.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# Utilities (`utils` module) API Reference
|
||||
|
||||
The `utils` module provides a collection of helper functions primarily for low-level terminal manipulation (like clearing the screen or hiding the cursor) and string measurements. These functions are often used internally by OSUI's rendering engine but can also be helpful for application developers directly.
|
||||
|
||||
## Functions
|
||||
|
||||
### `clear()`
|
||||
|
||||
Clears the entire terminal screen and moves the cursor to the top-left corner (1,1).
|
||||
|
||||
```rust
|
||||
pub fn clear() -> io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: A `std::io::Result<()>` indicating success or an I/O error.
|
||||
* **Behavior**: Sends ANSI escape codes `\x1B[2J` (clear screen) and `\x1B[H` (cursor home).
|
||||
* **Usage**: Used by `Screen::render` before drawing a new frame. You might use it yourself for custom full-screen updates outside of OSUI's main loop.
|
||||
|
||||
```rust
|
||||
use osui::utils;
|
||||
// ...
|
||||
utils::clear().unwrap();
|
||||
```
|
||||
|
||||
### `hide_cursor()`
|
||||
|
||||
Hides the terminal cursor.
|
||||
|
||||
```rust
|
||||
pub fn hide_cursor() -> io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: A `std::io::Result<()>` indicating success or an I/O error.
|
||||
* **Behavior**: Sends ANSI escape code `\x1b[?25l`.
|
||||
* **Usage**: Called by `Screen::run` at application startup for a cleaner TUI experience. You should not need to call this manually in most OSUI applications.
|
||||
|
||||
### `show_cursor()`
|
||||
|
||||
Shows the terminal cursor.
|
||||
|
||||
```rust
|
||||
pub fn show_cursor() -> io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: A `std::io::Result<()>` indicating success or an I/O error.
|
||||
* **Behavior**: Sends ANSI escape code `\x1B[?25h`.
|
||||
* **Usage**: Called by `Screen::close` at application shutdown to restore the terminal state. You should not need to call this manually.
|
||||
|
||||
### `flush()`
|
||||
|
||||
Flushes the standard output buffer. This ensures that any `print!` or `println!` macros or direct writes to `stdout` are immediately displayed on the terminal.
|
||||
|
||||
```rust
|
||||
pub fn flush() -> io::Result<()>
|
||||
```
|
||||
|
||||
* **Returns**: A `std::io::Result<()>` indicating success or an I/O error.
|
||||
* **Usage**: Used internally by OSUI's print functions to ensure immediate rendering. You might use it after a series of prints if you're not using OSUI's `RenderScope` for drawing.
|
||||
|
||||
```rust
|
||||
use std::io::{self, Write};
|
||||
use osui::utils;
|
||||
|
||||
print!("Loading...");
|
||||
utils::flush()?; // Ensure "Loading..." is visible
|
||||
// ... long operation ...
|
||||
println!("Done.");
|
||||
```
|
||||
|
||||
### `str_size(s: &str) -> (u16, u16)`
|
||||
|
||||
Calculates the width (maximum line length) and height (number of lines) of a string, assuming it's rendered in a monospaced terminal environment. It handles newline characters (`\n`).
|
||||
|
||||
```rust
|
||||
pub fn str_size(s: &str) -> (u16, u16)
|
||||
```
|
||||
|
||||
* `s`: The input string.
|
||||
* **Returns**: A tuple `(width, height)` where `width` is the maximum line width and `height` is the number of lines.
|
||||
* **Usage**: Used by `RenderScope` to determine content-based element sizes. Also useful for custom elements needing to know the dimensions of text.
|
||||
|
||||
```rust
|
||||
use osui::utils;
|
||||
let (width, height) = utils::str_size("Hello\nWorld");
|
||||
assert_eq!((5, 2), (width, height)); // "World" is 5 chars, 2 lines
|
||||
```
|
||||
|
||||
### `hex_ansi(hex: u32) -> String`
|
||||
|
||||
Converts a 24-bit RGB hex color (e.g., `0xFF00FF`) into an ANSI escape sequence for setting the **foreground** color.
|
||||
|
||||
```rust
|
||||
pub fn hex_ansi(hex: u32) -> String
|
||||
```
|
||||
|
||||
* `hex`: A `u32` representing the RGB color (e.g., `0xAABBCC`).
|
||||
* **Returns**: A `String` containing the ANSI escape code (e.g., `"\x1b[38;2;R;G;Bm"`).
|
||||
* **Usage**: Used internally for coloring text.
|
||||
|
||||
### `hex_ansi_bg(hex: u32) -> String`
|
||||
|
||||
Converts a 24-bit RGB hex color (e.g., `0xFF00FF`) into an ANSI escape sequence for setting the **background** color.
|
||||
|
||||
```rust
|
||||
pub fn hex_ansi_bg(hex: u32) -> String
|
||||
```
|
||||
|
||||
* `hex`: A `u32` representing the RGB color.
|
||||
* **Returns**: A `String` containing the ANSI escape code (e.g., `"\x1b[48;2;R;G;Bm"`).
|
||||
* **Usage**: Used internally for coloring backgrounds.
|
||||
|
||||
### `print(x: u16, y: u16, text: &str)` (crate-internal)
|
||||
|
||||
Prints text directly to the terminal at a specific 0-indexed `(x, y)` coordinate (converted to 1-indexed for ANSI). It resets the terminal style (`\x1b[0m`) after printing.
|
||||
|
||||
```rust
|
||||
pub(crate) fn print(x: u16, y: u16, text: &str)
|
||||
```
|
||||
|
||||
* `x`, `y`: 0-indexed coordinates for the top-left corner of the text.
|
||||
* `text`: The string to print.
|
||||
* **Usage**: Internal helper for `RenderScope`.
|
||||
|
||||
### `print_liner(x: u16, y: u16, liner: &str, text: &str)` (crate-internal)
|
||||
|
||||
Prints text to the terminal at `(x, y)` coordinates, prepending each line with a specified `liner` string (typically an ANSI color code). It resets the terminal style after printing.
|
||||
|
||||
```rust
|
||||
pub(crate) fn print_liner(x: u16, y: u16, liner: &str, text: &str)
|
||||
```
|
||||
|
||||
* `x`, `y`: 0-indexed coordinates.
|
||||
* `liner`: A string (e.g., an ANSI color code) to prepend to each line.
|
||||
* `text`: The string to print.
|
||||
* **Usage**: Internal helper for `RenderScope` to apply styles to printed output.
|
||||
|
||||
These utility functions provide the low-level terminal interaction necessary for OSUI's rendering, but some can be useful for direct debugging or custom terminal output when not managed by OSUI's drawing pipeline.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# Widget System API Reference
|
||||
|
||||
OSUI's UI is built upon a flexible widget system that combines `Element`s (renderable units) with `Component`s (data/behavior). The `Widget` enum serves as the primary container for these UI entities.
|
||||
|
||||
## Core Traits
|
||||
|
||||
### `Element` Trait
|
||||
|
||||
The fundamental building block for anything that can be rendered or participate in the UI tree.
|
||||
|
||||
```rust
|
||||
pub trait Element: Send + Sync {
|
||||
fn render(&mut self, scope: &mut RenderScope);
|
||||
fn after_render(&mut self, scope: &mut RenderScope);
|
||||
fn draw_child(&mut self, element: &Arc<Widget>);
|
||||
fn event(&mut self, event: &dyn Event);
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
* `render(&mut self, scope: &mut RenderScope)`: Called to perform the element's direct rendering (e.g., drawing text, shapes).
|
||||
* `after_render(&mut self, scope: &mut RenderScope)`: Called after the element's `render` and its children's `render` methods. Used by container elements (`Div`, `FlexRow`, `FlexCol`) to process and render their children.
|
||||
* `draw_child(&mut self, element: &Arc<Widget>)`: Called by the `rsx!` macro or parent elements when a child widget is added to this element. Container elements must implement this to store their children.
|
||||
* `event(&mut self, event: &dyn Event)`: Called when an event is dispatched to this widget.
|
||||
* `as_any(&self) -> &dyn Any`: Required for downcasting.
|
||||
* `as_any_mut(&mut self) -> &mut dyn Any`: Required for mutable downcasting.
|
||||
|
||||
### `Component` Trait
|
||||
|
||||
An optional trait for state or metadata attached to widgets. Components are key-value pairs (`TypeId` to `Box<dyn Component>`) stored alongside the element.
|
||||
|
||||
```rust
|
||||
pub trait Component: Send + Sync {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
* `as_any(&self) -> &dyn Any`: Required for downcasting.
|
||||
* `as_any_mut(&mut self) -> &mut dyn Any`: Required for mutable downcasting.
|
||||
* **Note**: Components usually implement `Clone` as well, so they can be retrieved (`get()`) by value.
|
||||
|
||||
## Widget Containers
|
||||
|
||||
### `WidgetLoad` Struct
|
||||
|
||||
A temporary container used during the initial construction of a widget, particularly by `rsx!`.
|
||||
|
||||
```rust
|
||||
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
|
||||
```
|
||||
|
||||
* `new<E: Element + 'static>(e: E) -> Self`: Creates a new `WidgetLoad` with a root element.
|
||||
* `component<C: Component + 'static>(mut self, c: C) -> Self`: Attaches a component if one of its type doesn't already exist. Chainable.
|
||||
* `set_component<C: Component + 'static>(mut self, c: C) -> Self`: Replaces any existing component of the same type. Chainable.
|
||||
* `get<C: Component + 'static + Clone>(&self) -> Option<C>`: Attempts to retrieve a cloned component of the given type.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let wl = WidgetLoad::new(String::from("My Text"))
|
||||
.component(Transform::new().center())
|
||||
.set_component(Style { background: Background::Solid(0x000000), foreground: Some(0xFFFFFF) });
|
||||
|
||||
if let Some(t) = wl.get::<Transform>() {
|
||||
// ... use t ...
|
||||
}
|
||||
```
|
||||
|
||||
### `StaticWidget` Struct
|
||||
|
||||
Represents a widget with fixed content and no dynamic rebuilding behavior.
|
||||
|
||||
```rust
|
||||
pub struct StaticWidget(Mutex<BoxedElement>, Mutex<HashMap<TypeId, BoxedComponent>>);
|
||||
```
|
||||
|
||||
* `new(e: BoxedElement) -> Self`: Creates a new `StaticWidget`. Used internally by `Screen::draw`.
|
||||
* `component<C: Component + 'static>(&self, c: C)`: Attaches a component if type doesn't exist.
|
||||
* `set_component<C: Component + 'static>(&self, c: C)`: Replaces a component.
|
||||
* `get<C: Component + 'static + Clone>(&self) -> Option<C>`: Retrieves a cloned component.
|
||||
* **Note**: Access to the element and components is via `Mutex`es for thread safety.
|
||||
|
||||
### `DynWidget` Struct
|
||||
|
||||
Represents a widget with dynamic content, supporting reactive updates and rebuilding.
|
||||
|
||||
```rust
|
||||
pub struct DynWidget(
|
||||
Mutex<BoxedElement>,
|
||||
Mutex<HashMap<TypeId, BoxedComponent>>,
|
||||
Mutex<Box<dyn FnMut() -> WidgetLoad + Send + Sync>>, // The rebuild function
|
||||
Mutex<Vec<Box<dyn DependencyHandler>>>, // Registered dependencies
|
||||
Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>, // Inject function
|
||||
);
|
||||
```
|
||||
|
||||
* `new<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self`: Creates a new `DynWidget` from a build closure.
|
||||
* `inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(&self, f: F)`: Provides a callback that can modify the `WidgetLoad` during refresh. Useful for adding components dynamically.
|
||||
* `refresh(&self)`: Forces the widget to rebuild its content by re-evaluating its creation function.
|
||||
* `auto_refresh(&self)`: Rebuilds the widget only if any of its registered dependencies (`DependencyHandler`) have changed. Called by `Screen` during `render`.
|
||||
* `dependency<D: DependencyHandler + 'static>(&self, d: D)`: Adds a dependency.
|
||||
* `dependency_box(&self, d: Box<dyn DependencyHandler>)`: Adds a boxed dependency.
|
||||
* `component<C: Component + 'static>(&self, c: C)`: Attaches a component if type doesn't exist.
|
||||
* `set_component<C: Component + 'static>(&self, c: C)`: Replaces a component.
|
||||
* `get<C: Component + 'static + Clone>(&self) -> Option<C>`: Retrieves a cloned component.
|
||||
|
||||
## `Widget` Enum
|
||||
|
||||
The main reference-counted container for either a `StaticWidget` or `DynWidget`. This is the standard way to pass widgets around.
|
||||
|
||||
```rust
|
||||
pub enum Widget {
|
||||
Static(StaticWidget),
|
||||
Dynamic(DynWidget),
|
||||
}
|
||||
```
|
||||
|
||||
* `new_static(e: BoxedElement) -> Self`: Creates a static `Widget`.
|
||||
* `new_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self`: Creates a dynamic `Widget`.
|
||||
|
||||
### Common `Widget` Methods (Delegated)
|
||||
|
||||
These methods delegate to the underlying `StaticWidget` or `DynWidget` variant.
|
||||
|
||||
* `get_elem(&self) -> MutexGuard<BoxedElement>`: Gets a mutable lock to the widget's root `Element`. Use `*widget.get_elem()` to access the `Element`.
|
||||
* `after_render(&self)`: Calls `Element::after_render` on the root element.
|
||||
* `component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self>`: Attaches a component. Returns `self` for chaining.
|
||||
* `set_component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self>`: Replaces a component. Returns `self` for chaining.
|
||||
* `get<C: Component + 'static + Clone>(&self) -> Option<C>`: Retrieves a cloned component. Returns `None` if not found or type mismatch.
|
||||
* `inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(self: &Arc<Self>, mut f: F)`: For dynamic widgets, sets a callback to modify `WidgetLoad` on refresh. For static, it injects components from the `WidgetLoad` returned by `f`.
|
||||
* `refresh(self: &Arc<Self>)`: Forces a rebuild for `DynWidget`s. Does nothing for `StaticWidget`s.
|
||||
* `auto_refresh(self: &Arc<Self>)`: Triggers rebuild for `DynWidget`s if dependencies have changed. Does nothing for `StaticWidget`s.
|
||||
* `dependency<D: DependencyHandler + 'static>(self: &Arc<Self>, d: D) -> &Arc<Self>`: Adds a dependency for `DynWidget`s. Does nothing for `StaticWidget`s.
|
||||
* `dependency_box(self: &Arc<Self>, d: Box<dyn DependencyHandler>) -> &Arc<Self>`: Adds a boxed dependency for `DynWidget`s. Does nothing for `StaticWidget`s.
|
||||
* `event<E: Event + Clone + 'static>(self: &Arc<Self>, e: &E)`: Dispatches an event. It first checks for an attached `Handler<E>` component and calls it, then calls `Element::event` on the root element.
|
||||
|
||||
## Special Components
|
||||
|
||||
OSUI uses a few internal components to control rendering behavior:
|
||||
|
||||
* `NoRender`: If a widget has this component, the `Screen`'s main rendering loop will skip rendering it directly. This is typically used for widgets that are managed and rendered by their parent `Element::after_render` method.
|
||||
* `NoRenderRoot`: Similar to `NoRender`, but specifically signals that the widget is a child being managed by a parent element, preventing the `Screen` from considering it a top-level root widget for direct rendering.
|
||||
* `Handler<E>`: (Described in [Handling Input](../guides/handling_input.md) and [Extensions API](../reference/extensions_api.md)) Enables widgets to subscribe to specific event types.
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
When working with widgets, you'll commonly:
|
||||
|
||||
1. Create them using `rsx!` or `Screen::draw`/`draw_dyn`.
|
||||
2. Attach `Transform` and `Style` components for layout and appearance.
|
||||
3. Attach other custom components for data or behavior.
|
||||
4. For dynamic widgets, declare `State` dependencies using `%` in `rsx!`.
|
||||
5. Access elements and components using `get_elem()`, `get()`, `set_component()`.
|
||||
|
||||
Understanding the interplay between `Element`, `Component`, and `Widget` is crucial for building complex and interactive OSUI applications.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user