Updated to 0.2.0
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 0
|
||||
title: Crate Structure
|
||||
---
|
||||
|
||||
# OSUI Crate Structure
|
||||
|
||||
The `osui` library is organized into several modules, each responsible for a distinct aspect of TUI development. Understanding this structure helps in navigating the codebase and locating relevant functionalities.
|
||||
|
||||
## Top-Level Modules
|
||||
|
||||
The `osui` crate is composed of the following main modules:
|
||||
|
||||
* **`component`**: Defines the core component system, including `Context` (for component state and lifecycle), `Scope` (for child management), and the `ComponentImpl` trait.
|
||||
* **`engine`**: Provides the rendering engine trait (`Engine`), command execution system (`CommandExecutor`), and concrete engine implementations like `Console` (for `crossterm`) and `Benchmark`.
|
||||
* **`frontend`**: Implements the RSX (React-like Syntax) system, including the `Rsx` struct and `ToRsx` trait, which bridges the `rsx!` macro output to the rendering pipeline.
|
||||
* **`render`**: Contains low-level rendering primitives such as `DrawContext`, `DrawInstruction`, `Point`, `Area`, and `Size`. This module defines *what* gets drawn.
|
||||
* **`state`**: Offers React-like hooks for managing component state (`use_state`), side effects (`use_effect`), and component lifecycle (`use_mount`, `use_mount_manual`).
|
||||
|
||||
## `prelude` Module
|
||||
|
||||
The `osui::prelude` module re-exports the most commonly used items from all sub-modules. It's recommended to `use osui::prelude::*;` in your application to easily access essential types and macros without verbose imports.
|
||||
|
||||
```rust
|
||||
pub mod prelude {
|
||||
pub use crate::component::{context::*, scope::*, *};
|
||||
pub use crate::engine::*;
|
||||
pub use crate::frontend::*;
|
||||
pub use crate::render::*;
|
||||
pub use crate::state::*;
|
||||
pub use crate::{sleep, Error, Result, View, ViewWrapper};
|
||||
pub use crossterm;
|
||||
pub use osui_macros::{component, rsx};
|
||||
pub use std::sync::{Arc, Mutex};
|
||||
}
|
||||
```
|
||||
|
||||
## OSUI Core Types
|
||||
|
||||
Beyond the modules, `lib.rs` also defines some fundamental type aliases and error handling:
|
||||
|
||||
* **`View`**: `Arc<dyn Fn(&mut DrawContext) + Send + Sync>`. Represents a renderable unit, essentially a closure that takes a `DrawContext` and adds drawing instructions.
|
||||
* **`ViewWrapper`**: `Arc<dyn Fn(&mut DrawContext, View) + Send + Sync>`. A higher-order view that can wrap and modify how another `View` is rendered (e.g., for applying layout or styling).
|
||||
* **`Result<T>`**: `std::result::Result<T, Error>`. The standard result type for OSUI operations.
|
||||
* **`Error`**: An enum defining OSUI-specific errors, currently including `PoisonError` for mutex poisoning.
|
||||
* **`sleep(delay_ms: u64)`**: A utility function for pausing execution for a specified duration.
|
||||
|
||||
This structured approach helps keep the library organized and maintainable, allowing developers to quickly understand where to find the tools they need.
|
||||
|
||||
**Next:** Dive into the details of the [Component API](./component-api.md).
|
||||
@@ -0,0 +1,118 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: Component API
|
||||
---
|
||||
|
||||
# Component Module API Reference
|
||||
|
||||
The `component` module is the heart of OSUI's UI system, defining how reusable UI units are structured, manage their state, and interact.
|
||||
|
||||
## `ComponentImpl` Trait
|
||||
|
||||
```rust
|
||||
pub trait ComponentImpl: Send + Sync {
|
||||
fn call(&self, cx: &Arc<Context>) -> View;
|
||||
}
|
||||
```
|
||||
|
||||
The `ComponentImpl` trait is implemented by all types that can act as an OSUI component.
|
||||
* **`call(&self, cx: &Arc<Context>) -> View`**: The core method that renders the component. It takes a reference to the component itself (`self`) and the component's `Context` (`cx`), and returns a `View` which contains the drawing instructions.
|
||||
|
||||
**Implementations:**
|
||||
* **`View`**: A bare `View` (an `Arc<dyn Fn(&mut DrawContext) + Send + Sync>`) can itself be a `ComponentImpl`, simply returning itself.
|
||||
* **`Fn(&Arc<Context>) -> View`**: Any closure with this signature can also be a `ComponentImpl`.
|
||||
* **`#[component]` macro**: The `#[component]` macro automatically generates a struct and implements `ComponentImpl` for it, wrapping your component function.
|
||||
|
||||
## `Component` Type Alias
|
||||
|
||||
```rust
|
||||
pub type Component = Arc<dyn ComponentImpl>;
|
||||
```
|
||||
|
||||
A convenience type alias for a `ComponentImpl` wrapped in an `Arc`, allowing for shared, thread-safe ownership.
|
||||
|
||||
## `EventHandler` Type Alias
|
||||
|
||||
```rust
|
||||
pub type EventHandler = Arc<Mutex<dyn FnMut(&Arc<Context>, &dyn Any) + Send + Sync>>;
|
||||
```
|
||||
|
||||
A type alias for a mutex-protected, thread-safe, mutable closure that handles events. Event handlers receive the component's `Context` and a reference to the event data (as `&dyn Any`).
|
||||
|
||||
## `context` Module
|
||||
|
||||
Contains the `Context` struct, which is central to each component instance.
|
||||
|
||||
### `Context` Struct
|
||||
|
||||
```rust
|
||||
pub struct Context {
|
||||
component: AccessCell<Component>,
|
||||
view: AccessCell<View>,
|
||||
event_handlers: AccessCell<HashMap<TypeId, Vec<EventHandler>>>,
|
||||
pub(crate) scopes: Mutex<Vec<Arc<Scope>>>,
|
||||
executor: Arc<dyn CommandExecutor>,
|
||||
}
|
||||
```
|
||||
|
||||
The `Context` holds the runtime state and behavior for a specific component instance. It manages the component's `View`, its registered event handlers, and its child `Scope`s.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new<F: ComponentImpl + 'static>(component: F, executor: Arc<dyn CommandExecutor>) -> Arc<Self>`**
|
||||
* Creates a new `Arc` wrapped `Context` for the given component and command executor.
|
||||
* **`fn refresh(self: &Arc<Self>)`**
|
||||
* Re-renders the component by clearing existing event handlers and calling the component's `call` method to produce a new `View`. This is typically called automatically by the engine or `Rsx`.
|
||||
* **`fn refresh_sync(self: &Arc<Self>)`**
|
||||
* Synchronously re-renders the component, blocking until the view closure has finished executing.
|
||||
* **`fn get_view(self: &Arc<Self>) -> View`**
|
||||
* Returns a clone of the component's current `View`.
|
||||
* **`fn on_event<T: Any + 'static, F: Fn(&Arc<Self>, &T) + Send + Sync + 'static>(self: &Arc<Self>, handler: F)`**
|
||||
* Registers an event handler `F` for events of type `T`. When an event of type `T` is `emit`ted to this `Context`, the handler `F` will be invoked.
|
||||
* **`fn emit_event<E: Send + Sync + Any + 'static>(self: &Arc<Self>, event: E)`**
|
||||
* Emits an event `E`. All registered handlers for type `E` on this `Context` are called synchronously, and then the event is propagated to all child components.
|
||||
* **`fn emit_event_threaded<E: Any + Send + Sync + Clone + 'static>(self: &Arc<Self>, event: &E)`**
|
||||
* Emits an event `E`. Each registered handler for type `E` on this `Context` is called in a *newly spawned thread*. The event is then propagated to all child components (also using `emit_event_threaded`). Requires `E` to be `Clone`.
|
||||
* **`fn scope(self: &Arc<Self>) -> Arc<Scope>`**
|
||||
* Creates a new, empty child `Scope` and adds it to this `Context`. Returns the new `Scope`.
|
||||
* **`fn dyn_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(self: &Arc<Self>, drawer: F, dependencies: &[&dyn HookDependency]) -> Arc<Scope>`**
|
||||
* Creates a new *dynamic* child `Scope` that re-renders (by calling `drawer`) whenever any of its `dependencies` change. `drawer` is also called immediately. Returns the new `Scope`.
|
||||
* **`fn add_scope(self: &Arc<Self>, scope: Arc<Scope>)`**
|
||||
* Adds an already constructed `Scope` as a child to this `Context`.
|
||||
* **`fn draw_children(self: &Arc<Self>, ctx: &mut DrawContext)`**
|
||||
* Iterates through all child `Scope`s and their components, rendering them into the provided `DrawContext`. Handles `ViewWrapper`s if present.
|
||||
* **`fn get_executor(self: &Arc<Self>) -> Arc<dyn CommandExecutor>`**
|
||||
* Returns a clone of the `CommandExecutor` associated with this `Context`.
|
||||
* **`fn execute<T: Command + 'static>(self: &Arc<Self>, command: T) -> crate::Result<()>`**
|
||||
* Executes a given `Command` using the associated `CommandExecutor`.
|
||||
* **`fn stop(self: &Arc<Self>) -> crate::Result<()>`**
|
||||
* A convenience method to execute the `Stop` command, terminating the application.
|
||||
|
||||
## `scope` Module
|
||||
|
||||
Contains the `Scope` struct, which organizes child components within a `Context`.
|
||||
|
||||
### `Scope` Struct
|
||||
|
||||
```rust
|
||||
pub struct Scope {
|
||||
pub children: Mutex<Vec<(Arc<Context>, Option<ViewWrapper>)>>,
|
||||
executor: Arc<dyn CommandExecutor>,
|
||||
}
|
||||
```
|
||||
|
||||
A `Scope` groups child components. Each entry in `children` consists of a child `Context` and an optional `ViewWrapper` that can modify its rendering.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new(executor: Arc<dyn CommandExecutor>) -> Arc<Self>`**
|
||||
* Creates a new `Arc` wrapped `Scope` with the given `CommandExecutor`.
|
||||
* **`fn child<F: ComponentImpl + 'static>(self: &Arc<Self>, child: F, view_wrapper: Option<ViewWrapper>)`**
|
||||
* Creates a new `Context` for the provided `child` component, refreshes it, and adds it to this `Scope`'s children, optionally with a `ViewWrapper`.
|
||||
* **`fn view(self: &Arc<Self>, view: View)`**
|
||||
* Creates a new `Context` directly from a `View` (without an explicit `ComponentImpl`), refreshes it, and adds it to this `Scope`'s children.
|
||||
|
||||
This module forms the backbone of how component trees are constructed and managed in OSUI.
|
||||
|
||||
**Next:** Explore the [Engine API](./engine-api.md).
|
||||
@@ -0,0 +1,164 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: Engine API
|
||||
---
|
||||
|
||||
# Engine Module API Reference
|
||||
|
||||
The `engine` module defines the core interfaces for how OSUI applications run, render, and execute commands. It provides abstractions for different rendering backends and includes a default console implementation.
|
||||
|
||||
## `Engine` Trait
|
||||
|
||||
```rust
|
||||
pub trait Engine<Output = ()> {
|
||||
fn run<C: ComponentImpl + 'static>(&self, component: C) -> crate::Result<Output>;
|
||||
fn init<C: ComponentImpl + 'static>(&self, component: C) -> Arc<Context>;
|
||||
fn render(&self, cx: &Arc<Context>);
|
||||
fn render_delay(&self);
|
||||
fn render_view(&self, area: &Area, view: &View) -> DrawContext;
|
||||
fn draw_context(&self, ctx: &DrawContext);
|
||||
fn executor(&self) -> Arc<dyn CommandExecutor>;
|
||||
}
|
||||
```
|
||||
|
||||
The `Engine` trait is the primary interface for running an OSUI application. It abstracts away the specifics of how components are initialized, rendered, and how the application loop is managed.
|
||||
|
||||
#### Associated Types / Generics:
|
||||
|
||||
* **`Output`**: A generic type that allows `run` to return different results. By default, it's `()`, but for specialized engines (like `Benchmark`), it can return custom data.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn run<C: ComponentImpl + 'static>(&self, component: C) -> crate::Result<Output>`**
|
||||
* The main entry point to start the OSUI application loop. It takes the root component `C`, initializes it, and continuously renders until a stop command is issued. Returns `Ok(())` by default, or a custom `Output` for specialized engines.
|
||||
* **`fn init<C: ComponentImpl + 'static>(&self, component: C) -> Arc<Context>`**
|
||||
* Initializes the rendering environment and creates the root `Context` for the application's top-level component. This is typically called by `run`.
|
||||
* **`fn render(&self, cx: &Arc<Context>)`**
|
||||
* Performs a full render cycle for the given `Context`. This usually involves clearing the screen, calling `render_view` for the root, and then `draw_context`.
|
||||
* **`fn render_delay(&self)`**
|
||||
* A hook for introducing a delay between render frames. The default implementation calls `crate::sleep(16)` for approximately 60 frames per second.
|
||||
* **`fn render_view(&self, area: &Area, view: &View) -> DrawContext`**
|
||||
* Takes a `View` and the `Area` it should render within, executing the `View`'s closure to produce a `DrawContext` filled with `DrawInstruction`s.
|
||||
* **`fn draw_context(&self, ctx: &DrawContext)`**
|
||||
* Executes the drawing instructions contained within a `DrawContext` to actually draw content to the rendering target (e.g., the terminal).
|
||||
* **`fn executor(&self) -> Arc<dyn CommandExecutor>`**
|
||||
* Returns the `CommandExecutor` instance used by this engine.
|
||||
|
||||
## `Command` Trait
|
||||
|
||||
```rust
|
||||
pub trait Command {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
The `Command` trait is implemented by types that represent actions or instructions that can be executed by the `CommandExecutor`. This trait enables a type-safe way to send commands between components and the engine.
|
||||
|
||||
* **`fn as_any(&self) -> &dyn Any`**: Allows downcasting the command to its concrete type for pattern matching and execution.
|
||||
|
||||
## `CommandExecutor` Trait
|
||||
|
||||
```rust
|
||||
pub trait CommandExecutor: Send + Sync {
|
||||
fn execute_command(&self, command: &Arc<dyn Command>) -> crate::Result<()>;
|
||||
}
|
||||
```
|
||||
|
||||
The `CommandExecutor` trait defines how commands are processed within the OSUI application. Engines provide their own implementations of this trait to handle system-level operations.
|
||||
|
||||
* **`fn execute_command(&self, command: &Arc<dyn Command>) -> crate::Result<()>`**: Takes an `Arc` wrapped `Command` and executes it. Implementations typically use `command.as_any().downcast_ref()` to identify and process specific commands.
|
||||
|
||||
## `console` Module: `Console` Engine and `ConsoleExecutor`
|
||||
|
||||
The `console` module provides OSUI's default, `crossterm`-based rendering engine.
|
||||
|
||||
### `Console` Struct
|
||||
|
||||
```rust
|
||||
pub struct Console {
|
||||
threads: Mutex<Vec<Arc<dyn Fn(Arc<Context>) + Send + Sync>>>,
|
||||
executor: Arc<ConsoleExecutor>,
|
||||
}
|
||||
```
|
||||
|
||||
The `Console` struct implements the `Engine` trait, specifically designed to render to a terminal using `crossterm`.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new() -> Self`**: Creates a new `Console` engine.
|
||||
* **`fn thread<F: Fn(Arc<Context>) + Send + Sync + 'static>(&self, run: F)`**: Registers a closure to be run in a separate thread when the engine initializes. This is useful for background tasks or input polling that needs access to the main `Context`.
|
||||
|
||||
#### `Engine` Trait Implementation:
|
||||
The `Console` implements all methods of the `Engine` trait, handling terminal setup (raw mode, cursor hiding), screen clearing, `crossterm` cursor movements, and text output.
|
||||
|
||||
### `ConsoleExecutor` Struct
|
||||
|
||||
```rust
|
||||
pub struct ConsoleExecutor {
|
||||
running: Mutex<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
The `ConsoleExecutor` implements the `CommandExecutor` trait for the `Console` engine. It manages the `running` state of the application.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn is_running(self: &Arc<ConsoleExecutor>) -> bool`**: Checks if the application is currently running.
|
||||
* **`fn stop(&self) -> crate::Result<()>`**: Sets the internal `running` flag to `false`, signaling the `Console` engine to terminate its `run` loop.
|
||||
|
||||
#### `CommandExecutor` Trait Implementation:
|
||||
The `ConsoleExecutor` currently supports handling the `commands::Stop` command.
|
||||
|
||||
## `commands` Module: Built-in Commands
|
||||
|
||||
The `commands` module defines simple, built-in commands for the engine.
|
||||
|
||||
### `Stop` Command
|
||||
|
||||
```rust
|
||||
pub struct Stop;
|
||||
|
||||
impl Command for Stop {
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
}
|
||||
```
|
||||
|
||||
A basic command used to signal the `Engine` to stop its main loop and exit the application.
|
||||
|
||||
## `benchmark` Module: `Benchmark` Engine
|
||||
|
||||
The `benchmark` module provides a wrapper engine for performance testing.
|
||||
|
||||
### `BenchmarkResult` Struct
|
||||
|
||||
```rust
|
||||
pub struct BenchmarkResult {
|
||||
pub average: u128,
|
||||
pub min: u128,
|
||||
pub max: u128,
|
||||
pub total_render: u128,
|
||||
pub total: u128,
|
||||
}
|
||||
```
|
||||
|
||||
Holds the statistical results of a benchmark run, including average, minimum, maximum, total render time, and total overall time in microseconds.
|
||||
|
||||
### `Benchmark<T: Engine>` Struct
|
||||
|
||||
```rust
|
||||
pub struct Benchmark<T: Engine>(T);
|
||||
```
|
||||
|
||||
A wrapper around any other `Engine` that measures its rendering performance.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new(engine: T) -> Self`**: Creates a new `Benchmark` wrapper around an existing `Engine` instance.
|
||||
|
||||
#### `Engine<BenchmarkResult>` Trait Implementation:
|
||||
The `Benchmark` engine implements the `Engine` trait, but its `run` method performs multiple render cycles (e.g., 40 times), measures the duration of each, and returns a `BenchmarkResult` instead of `()`. It delegates all other `Engine` methods to the wrapped engine.
|
||||
|
||||
This comprehensive set of traits and implementations allows OSUI to be flexible regarding its rendering backend and extensible with custom command handling.
|
||||
|
||||
**Next:** Explore the [Frontend API](./frontend-api.md).
|
||||
@@ -0,0 +1,78 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: Frontend API
|
||||
---
|
||||
|
||||
# Frontend Module API Reference
|
||||
|
||||
The `frontend` module is responsible for bridging the declarative `rsx!` macro syntax to the dynamic component rendering system. It defines how component hierarchies are constructed and managed before being translated into `View`s.
|
||||
|
||||
## `ToRsx` Trait
|
||||
|
||||
```rust
|
||||
pub trait ToRsx {
|
||||
fn to_rsx(&self) -> Rsx;
|
||||
}
|
||||
```
|
||||
|
||||
The `ToRsx` trait is implemented by any type that can be converted into an `Rsx` object. This is crucial for embedding various types (like strings, numbers, or other `Rsx` instances) directly into the `rsx!` macro output using the `@{expr}` syntax.
|
||||
|
||||
**Implementations:**
|
||||
* **`&Rsx`**: Converts a reference to an `Rsx` into an owned `Rsx` by cloning its internal structure.
|
||||
* **`T: std::fmt::Display`**: Any type that implements `std::fmt::Display` (e.g., `String`, `&str`, `i32`, `f64`, etc.) automatically implements `ToRsx`. It converts the displayable value into a `Rsx` containing a static scope that draws the text.
|
||||
|
||||
## `RsxScope` Enum
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub enum RsxScope {
|
||||
Static(Arc<dyn Fn(&Arc<Scope>) + Send + Sync>),
|
||||
Dynamic(
|
||||
Arc<dyn Fn(&Arc<Scope>) + Send + Sync>,
|
||||
Vec<Arc<dyn HookDependency>>,
|
||||
),
|
||||
Child(Rsx),
|
||||
}
|
||||
```
|
||||
|
||||
`RsxScope` represents the different kinds of renderable units that can be part of an `Rsx` hierarchy. These scopes dictate how and when their content is processed and updated.
|
||||
|
||||
* **`Static(Arc<dyn Fn(&Arc<Scope>) + Send + Sync>)`**:
|
||||
* Represents content that is processed only once. This is typically used for simple text literals or components that don't depend on reactive state within their `rsx!`.
|
||||
* The contained closure is executed once to set up children within a new `Scope`.
|
||||
* **`Dynamic(Arc<dyn Fn(&Arc<Scope>) + Send + Sync>, Vec<Arc<dyn HookDependency>>)`**:
|
||||
* Represents content that needs to be re-evaluated and potentially re-rendered when certain `dependencies` change. This is used for `rsx!` blocks with `@if` and `@for` that declare dependencies (`%dep`).
|
||||
* The closure (`drawer`) is executed initially and then whenever any of the `HookDependency` instances in `dependencies` notify an update.
|
||||
* **`Child(Rsx)`**:
|
||||
* Represents a nested `Rsx` structure. This is used when an `Rsx` object is embedded directly into another `rsx!` block (e.g., via `@{other_rsx}` or when passing `children` to a component).
|
||||
|
||||
## `Rsx` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Rsx(Vec<RsxScope>);
|
||||
```
|
||||
|
||||
The `Rsx` struct is a collection of `RsxScope`s, representing a declarative UI tree fragment. It's the primary output of the `rsx!` macro.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new() -> Self`**
|
||||
* Creates a new empty `Rsx` instance.
|
||||
* **`fn static_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(&mut self, scope: F)`**
|
||||
* Adds a `Static` `RsxScope` to the collection. The `scope` closure will be executed once to build up the content within a dedicated `Scope`.
|
||||
* **`fn dynamic_scope<F: Fn(&Arc<Scope>) + Send + Sync + 'static>(&mut self, drawer: F, dependencies: Vec<Arc<dyn HookDependency>>)`**
|
||||
* Adds a `Dynamic` `RsxScope` to the collection. The `drawer` closure will be executed initially and then on subsequent updates of the specified `dependencies`.
|
||||
* **`fn child<R: ToRsx>(&mut self, child: R)`**
|
||||
* Adds a `Child` `RsxScope` to the collection, converting the input `R` (which must implement `ToRsx`) into a nested `Rsx`.
|
||||
* **`fn generate_children(&self, context: &Arc<Context>)`**
|
||||
* Processes the internal `Vec<RsxScope>`, converting each scope into actual component `Context`s and `Scope`s within the provided parent `context`. This method recursively builds the component tree.
|
||||
* **`fn view(&self, context: &Arc<Context>) -> View`**
|
||||
* The primary method used by components to turn their `rsx!` output into a renderable `View`.
|
||||
* It first calls `generate_children` to build the component tree within the given `context`.
|
||||
* It then returns a `View` closure that, when executed, will instruct the `context` to `draw_children`.
|
||||
|
||||
The `frontend` module, through `Rsx` and `RsxScope`, provides the declarative interface and the necessary translation layer to OSUI's imperative rendering core.
|
||||
|
||||
**Next:** Explore the [Render API](./render-api.md).
|
||||
@@ -0,0 +1,212 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 6
|
||||
title: Macros API
|
||||
---
|
||||
|
||||
# Macros Module API Reference
|
||||
|
||||
The `osui-macros` crate provides the procedural macros that enhance OSUI's ergonomics and enable its declarative UI syntax. These macros transform your Rust code into the necessary OSUI component and rendering structures.
|
||||
|
||||
## `#[component]` Attribute Macro
|
||||
|
||||
```rust
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream { /* ... */ }
|
||||
```
|
||||
|
||||
The `#[component]` attribute macro transforms a standard Rust function into a fully-fledged OSUI component.
|
||||
|
||||
#### Purpose:
|
||||
|
||||
* **Prop Generation**: It automatically parses the function's parameters (after the initial `cx: &Arc<Context>`) and generates a `struct` with matching fields. These fields become the component's "props".
|
||||
* **`ComponentImpl` Implementation**: It implements the `ComponentImpl` trait for the generated struct, making it a valid OSUI component that can be rendered. The `call` method of this trait simply invokes your original function.
|
||||
* **Ergonomics**: Simplifies component definition by allowing you to write components as regular functions with clear parameters, without manually defining structs and `ComponentImpl` boilerplate.
|
||||
|
||||
#### Usage:
|
||||
|
||||
```rust
|
||||
use osui::prelude::*; // For Context and View types
|
||||
use std::sync::Arc;
|
||||
|
||||
#[component]
|
||||
pub fn MyComponent(cx: &Arc<Context>, message: &str, count: &usize) -> View {
|
||||
// Your component logic, accessing `message` and `count` directly
|
||||
rsx! {
|
||||
format!("Message: {}, Count: {}", message, count)
|
||||
}.view(&cx)
|
||||
}
|
||||
|
||||
// How `MyComponent` would be used in RSX:
|
||||
// rsx! {
|
||||
// MyComponent { message: "Hello", count: 123 }
|
||||
// }
|
||||
```
|
||||
|
||||
#### Generated Code (Simplified):
|
||||
|
||||
```rust
|
||||
pub struct MyComponent {
|
||||
pub message: String, // Note: `&str` becomes `String`
|
||||
pub count: usize, // Note: `&usize` becomes `usize`
|
||||
}
|
||||
|
||||
impl MyComponent {
|
||||
pub fn component(
|
||||
cx: &Arc<Context>,
|
||||
message: &str, // Original function signature as `component` method
|
||||
count: &usize,
|
||||
) -> View {
|
||||
// Original body of the function
|
||||
rsx! {
|
||||
format!("Message: {}, Count: {}", message, count)
|
||||
}.view(&cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentImpl for MyComponent {
|
||||
fn call(&self, cx: &Arc<Context>) -> View {
|
||||
Self::component(
|
||||
cx,
|
||||
&self.message, // Passes stored props as references
|
||||
&self.count,
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Requirements:
|
||||
|
||||
* The function must take `cx: &Arc<Context>` as its first parameter.
|
||||
* The function must return `View`.
|
||||
* Prop parameters are typically references (e.g., `&str`, `&i32`). The macro automatically converts them to their owned types (e.g., `String`, `i32`) in the generated struct.
|
||||
|
||||
## `rsx!` Procedural Macro
|
||||
|
||||
```rust
|
||||
#[proc_macro]
|
||||
pub fn rsx(input: TokenStream) -> TokenStream { /* ... */ }
|
||||
```
|
||||
|
||||
The `rsx!` macro provides a declarative, React-like syntax for building UI component hierarchies directly in your Rust code. It parses the input and transforms it into calls to the `osui::frontend::Rsx` builder methods.
|
||||
|
||||
#### Purpose:
|
||||
|
||||
* **Declarative UI**: Allows you to describe *what* your UI should look like, rather than imperatively writing drawing commands.
|
||||
* **Component Composition**: Enables easy nesting and passing of props/children to other components.
|
||||
* **Reactive Flow**: Integrates with OSUI's state management to define dynamic UI segments.
|
||||
|
||||
#### Syntax Overview:
|
||||
|
||||
The `rsx!` macro supports several types of nodes:
|
||||
|
||||
1. **Text Literals**:
|
||||
```rust
|
||||
rsx! {
|
||||
"Hello World"
|
||||
"Another line of text"
|
||||
}
|
||||
// Generates:
|
||||
// r.static_scope(move |scope| {
|
||||
// scope.view(Arc::new(move |ctx| {
|
||||
// ctx.draw_text(Point { x: 0, y: 0 }, &format!("Hello World"))
|
||||
// }));
|
||||
// // ... for another line
|
||||
// });
|
||||
```
|
||||
|
||||
2. **Rust Expressions (`@{expr}`)**:
|
||||
```rust
|
||||
let name = "Alice";
|
||||
rsx! {
|
||||
@{format!("Hello, {}!", name)}
|
||||
@{1 + 2} // Any `Display` impl
|
||||
}
|
||||
// Generates:
|
||||
// r.child(format!("Hello, {}!", name));
|
||||
// r.child(1 + 2);
|
||||
```
|
||||
* The `expr` must evaluate to a type that implements `osui::frontend::ToRsx`.
|
||||
|
||||
3. **Component Instantiation (`Component { prop: value, ... children }`)**:
|
||||
```rust
|
||||
#[component] fn MyDiv(cx: &Arc<Context>, content: &str) -> View { /* ... */ }
|
||||
rsx! {
|
||||
MyDiv {
|
||||
content: "Some text", // Prop
|
||||
rsx! { "Child content" } // Children (if `children: &Rsx` is a prop)
|
||||
}
|
||||
}
|
||||
// Generates:
|
||||
// r.static_scope(move |scope| {
|
||||
// scope.child(
|
||||
// MyDiv {
|
||||
// content: "Some text".to_string(), // Owned type for struct field
|
||||
// children: osui::frontend::Rsx(/* ... */)
|
||||
// },
|
||||
// None
|
||||
// );
|
||||
// });
|
||||
```
|
||||
* `path`: The path to the component struct (e.g., `MyDiv`, `my_module::MyComponent`).
|
||||
* `props`: `key: value` pairs for component properties.
|
||||
* `children`: Any `rsx!` content directly inside the braces after props. This is collected into the special `children: &Rsx` prop if the component defines it.
|
||||
|
||||
4. **Conditional Rendering (`@if condition { ... } [else { ... }]`)**:
|
||||
```rust
|
||||
let show = true;
|
||||
rsx! {
|
||||
%show @if show {
|
||||
"Content shown if 'show' is true"
|
||||
} else {
|
||||
"Content shown if 'show' is false"
|
||||
}
|
||||
}
|
||||
// Generates:
|
||||
// r.dynamic_scope(move |scope| {
|
||||
// if show {
|
||||
// // ... rsx for true branch
|
||||
// } else {
|
||||
// // ... rsx for false branch
|
||||
// }
|
||||
// }, vec![Arc::new(show) as Arc<dyn HookDependency>]);
|
||||
```
|
||||
* `%dep1, dep2`: Optional dependency list. The `if` block will re-evaluate when any of these dependencies (which must implement `HookDependency`, like `State<T>`) change.
|
||||
* `condition`: A Rust expression evaluating to `bool`.
|
||||
* `{ ... }`: An `rsx!` fragment rendered if `condition` is true.
|
||||
* `else { ... }`: Optional `rsx!` fragment rendered if `condition` is false.
|
||||
|
||||
5. **Loop Rendering (`@for pattern in expr { ... }`)**:
|
||||
```rust
|
||||
let items = vec!["A", "B", "C"];
|
||||
rsx! {
|
||||
%items @for item in items {
|
||||
format!("Item: {}", item)
|
||||
}
|
||||
}
|
||||
// Generates:
|
||||
// r.dynamic_scope(move |scope| {
|
||||
// for item in items {
|
||||
// // ... rsx for each item
|
||||
// }
|
||||
// }, vec![Arc::new(items) as Arc<dyn HookDependency>]);
|
||||
```
|
||||
* `%dep1, dep2`: Optional dependency list. The `for` loop will re-evaluate when any of these dependencies change.
|
||||
* `pattern`: A standard Rust `for` loop pattern (e.g., `item`, `(idx, item)`).
|
||||
* `expr`: A Rust expression evaluating to an `IntoIterator`.
|
||||
* `{ ... }`: An `rsx!` fragment rendered for each iteration.
|
||||
|
||||
6. **Mount Hook (`!mount_hook_instance`)**:
|
||||
```rust
|
||||
let my_manual_mount = use_mount_manual();
|
||||
rsx! {
|
||||
!my_manual_mount
|
||||
}
|
||||
// Generates:
|
||||
// my_manual_mount.mount();
|
||||
```
|
||||
* Calls the `.mount()` method on the provided `Mount` instance. This is typically used with `use_mount_manual` to trigger effects at a specific point in the render tree.
|
||||
|
||||
The `rsx!` macro is a powerful tool for declarative UI construction, abstracting away the underlying `Rsx` object manipulation and `Scope` creation logic.
|
||||
|
||||
**Next:** Explore the detailed [Render API](./render-api.md).
|
||||
@@ -0,0 +1,100 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: Render API
|
||||
---
|
||||
|
||||
# Render Module API Reference
|
||||
|
||||
The `render` module provides the foundational primitives for drawing content to the terminal. It defines basic geometric types and the `DrawContext` for accumulating drawing instructions, abstracting away the specifics of the underlying terminal backend.
|
||||
|
||||
## Geometric Primitives
|
||||
|
||||
These structs define positions and dimensions within the terminal grid.
|
||||
|
||||
### `Point` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Point {
|
||||
pub x: u16, // X coordinate (column)
|
||||
pub y: u16, // Y coordinate (row)
|
||||
}
|
||||
```
|
||||
|
||||
Represents a specific coordinate in a 2D grid.
|
||||
|
||||
### `Size` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Size {
|
||||
pub width: u16, // Width in terminal columns
|
||||
pub pub height: u16, // Height in terminal rows
|
||||
}
|
||||
```
|
||||
|
||||
Represents the dimensions of a rectangular area.
|
||||
|
||||
### `Area` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Area {
|
||||
pub x: u16, // X coordinate (column) of the top-left corner
|
||||
pub y: u16, // Y coordinate (row) of the top-left corner
|
||||
pub width: u16, // Width in terminal columns
|
||||
pub height: u16, // Height in terminal rows
|
||||
}
|
||||
```
|
||||
|
||||
Represents a rectangular region defined by its top-left corner (`x`, `y`) and its `width` and `height`.
|
||||
|
||||
## `DrawInstruction` Enum
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub enum DrawInstruction {
|
||||
Text(Point, String),
|
||||
View(Area, View),
|
||||
Child(Point, DrawContext), // For embedding child DrawContexts at an offset
|
||||
}
|
||||
```
|
||||
|
||||
`DrawInstruction` enumerates the different types of atomic drawing operations that the rendering engine can perform.
|
||||
|
||||
* **`Text(Point, String)`**: Instructs the engine to draw a given `String` at a specific `Point`.
|
||||
* **`View(Area, View)`**: Instructs the engine to render a nested `View` within a specified `Area`. This is how child components are rendered.
|
||||
* **`Child(Point, DrawContext)`**: Instructs the engine to render a child `DrawContext` at a given offset `Point`. This is typically used internally when drawing recursively.
|
||||
|
||||
## `DrawContext` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct DrawContext {
|
||||
pub area: Area, // The total area available for drawing to this context
|
||||
pub allocated: Area, // The union of all allocated sub-areas within this context
|
||||
pub drawing: Vec<DrawInstruction>, // Accumulated drawing instructions
|
||||
}
|
||||
```
|
||||
|
||||
The `DrawContext` is the primary interface for components to issue drawing commands. Each component's `View` receives a `DrawContext` that represents its allocated drawing space. It accumulates `DrawInstruction`s which are then processed by the `Engine`.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new(area: Area) -> Self`**
|
||||
* Creates a new `DrawContext` with the specified `area` as its total available space. Initializes `allocated` to an "empty" area (max `u16` for x/y, 0 for width/height).
|
||||
* **`fn allocate(&mut self, x: u16, y: u16, width: u16, height: u16) -> Area`**
|
||||
* Marks a sub-region within the `DrawContext`'s `area` as "allocated".
|
||||
* Updates the `self.allocated` field to grow to encompass this new allocation.
|
||||
* Returns the `Area` representing the newly allocated space. Coordinates (`x`, `y`) are relative to `self.area`'s top-left corner.
|
||||
* **`fn draw(&mut self, inst: DrawInstruction)`**
|
||||
* Adds a raw `DrawInstruction` to the `drawing` vector.
|
||||
* **`fn draw_text(&mut self, point: Point, text: &str)`**
|
||||
* A convenience method to add a `DrawInstruction::Text` to the context. `point` is relative to `self.area`.
|
||||
* **`fn draw_view(&mut self, area: Area, view: View)`**
|
||||
* A convenience method to add a `DrawInstruction::View` to the context. `area` is relative to `self.area`.
|
||||
|
||||
This module lays the groundwork for all visual output in OSUI, providing the necessary abstractions for components to describe what they want to render without knowing the specifics of the terminal backend.
|
||||
|
||||
**Next:** Explore the [State API](./state-api.md).
|
||||
@@ -0,0 +1,166 @@
|
||||
markdown
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: State API
|
||||
---
|
||||
|
||||
# State Module API Reference
|
||||
|
||||
The `state` module provides OSUI's powerful, React-like hook system for managing component state and side effects. These hooks enable reactivity, allowing your UI to automatically update in response to data changes.
|
||||
|
||||
## `State<T>` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
pub struct State<T> {
|
||||
value: Arc<Mutex<T>>,
|
||||
dependents: Arc<Mutex<Vec<HookEffect>>>,
|
||||
}
|
||||
```
|
||||
|
||||
`State<T>` is the primary type for holding reactive, component-local state. It wraps a value `T` in an `Arc<Mutex<T>>` for thread-safe access and includes a list of `HookEffect`s that should be triggered when its value changes.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn get(&self) -> Inner<'_, T>`**
|
||||
* Acquires a lock on the internal `Mutex` and returns an `Inner<'_, T>` guard. This guard provides mutable (`DerefMut`) access to the state value. When the `Inner` guard is dropped, if the value was mutated, all `dependents` (registered `HookEffect`s) are notified.
|
||||
* **`fn get_dl(&self) -> T`**
|
||||
* "Deadlock-less" getter. Acquires a lock, clones the internal value `T`, releases the lock, and returns the cloned value. Useful for reading the state when cloning `T` is cheap and you don't need mutable access, preventing potential deadlocks from holding a `MutexGuard` across `await` points or other blocking operations. Requires `T: Clone`.
|
||||
* **`fn set(&self, v: T)`**
|
||||
* Replaces the current state value with `v` and then explicitly notifies all `dependents`.
|
||||
* **`fn update(&self)`**
|
||||
* Manually triggers all registered `dependents`. Useful if you've modified the internal value without using `get()` (e.g., via `Arc::get_mut` if the `Arc` is uniquely owned, which is rare for `State<T>`).
|
||||
* **`fn clone(&self) -> Self`**
|
||||
* Clones the `State<T>` handle (not the internal value). This creates a new `Arc` reference to the same underlying `value` and `dependents`. Essential for moving `State<T>` into closures or passing to child components without moving the actual state.
|
||||
|
||||
#### `Display` Implementation:
|
||||
If `T` implements `std::fmt::Display`, `State<T>` also implements `std::fmt::Display`, allowing it to be directly formatted (e.g., in `format!`) by implicitly calling `get_dl()`.
|
||||
|
||||
### `Inner<'a, T>` Struct
|
||||
|
||||
```rust
|
||||
pub struct Inner<'a, T> {
|
||||
value: MutexGuard<'a, T>,
|
||||
dependents: Arc<Mutex<Vec<HookEffect>>>,
|
||||
updated: bool,
|
||||
}
|
||||
```
|
||||
|
||||
A guard type returned by `State<T>::get()`. It provides scoped, mutable access to the internal state value.
|
||||
|
||||
#### `Deref` and `DerefMut` Implementations:
|
||||
* Allows `Inner<'a, T>` to be treated as a `&T` or `&mut T`, giving direct access to the underlying state.
|
||||
* The `DerefMut` implementation sets an internal `updated` flag.
|
||||
|
||||
#### `Drop` Implementation:
|
||||
* When `Inner<'a, T>` is dropped, if the `updated` flag is `true`, it automatically iterates through `dependents` and calls their `call()` method, ensuring reactivity.
|
||||
|
||||
## `HookEffect` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct HookEffect(Arc<Mutex<dyn FnMut() + Send + Sync>>);
|
||||
```
|
||||
|
||||
A wrapper around a mutex-protected closure that represents a side effect. These are registered as dependents of `State<T>` or `Mount` and are triggered when dependencies change.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn new<F: Fn() + Send + Sync + 'static>(f: F) -> Self`**
|
||||
* Creates a new `HookEffect` from a given closure.
|
||||
* **`fn call(&self)`**
|
||||
* Executes the wrapped closure by acquiring its mutex.
|
||||
|
||||
## `HookDependency` Trait
|
||||
|
||||
```rust
|
||||
pub trait HookDependency: Send + Sync {
|
||||
fn on_update(&self, hook: HookEffect);
|
||||
}
|
||||
```
|
||||
|
||||
The `HookDependency` trait defines how an object can register an effect (`HookEffect`) to be triggered when it updates.
|
||||
|
||||
**Implementations:**
|
||||
* **`State<T>`**: Registers the `HookEffect` to be called when `State<T>`'s value changes (via `set()`, `get()` and subsequent drop, or `update()`).
|
||||
* **`Mount`**: Registers the `HookEffect` to be called when `mount()` is invoked, or immediately if already mounted.
|
||||
|
||||
## `Mount` Struct
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Mount(Arc<Mutex<bool>>, Arc<Mutex<Vec<HookEffect>>>);
|
||||
```
|
||||
|
||||
A specialized hook for managing component lifecycle (specifically, the "mounted" state). It tracks whether a component has been mounted and queues `HookEffect`s to be run upon mounting.
|
||||
|
||||
#### Methods:
|
||||
|
||||
* **`fn mount(&self)`**
|
||||
* Sets the internal flag to `true`, indicating the component is now mounted.
|
||||
* Executes all currently queued `HookEffect`s and clears the queue.
|
||||
* Any `HookEffect` registered after `mount()` has been called will execute immediately.
|
||||
|
||||
## Hooks Functions
|
||||
|
||||
These functions are the primary way to interact with the state management system within your components.
|
||||
|
||||
### `fn use_state<T>(v: T) -> State<T>`
|
||||
|
||||
* **Purpose**: Creates and initializes a new `State<T>` instance.
|
||||
* **Usage**: `let count = use_state(0);`
|
||||
|
||||
### `fn use_effect<F: FnMut() + Send + Sync + 'static>(f: F, dependencies: &[&dyn HookDependency])`
|
||||
|
||||
* **Purpose**: Registers a side effect `f` that will run when any of the `dependencies` change, and also once initially. The effect closure is run in a `std::thread::spawn`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
let counter = use_state(0);
|
||||
use_effect(
|
||||
{ let counter = counter.clone(); move || println!("Counter changed: {}", counter.get_dl()) },
|
||||
&[&counter] // Dependencies
|
||||
);
|
||||
```
|
||||
* **Empty Dependencies**: If `dependencies` is `&[]`, the effect runs once on initial render and never again.
|
||||
|
||||
### `fn use_mount() -> Mount`
|
||||
|
||||
* **Purpose**: Creates a `Mount` instance that is immediately marked as "mounted". Effects registered with this `Mount` (via `use_effect`) will run once, immediately.
|
||||
* **Usage**: `let mount_hook = use_mount();`
|
||||
|
||||
### `fn use_mount_manual() -> Mount`
|
||||
|
||||
* **Purpose**: Creates a `Mount` instance that starts in an "unmounted" state. Effects registered with this `Mount` will *only* run when its `.mount()` method is explicitly called (either programmatically or via `!mount_hook_instance` in `rsx!`).
|
||||
* **Usage**: `let manual_mount_hook = use_mount_manual();`
|
||||
|
||||
### `fn use_sync_state<T, E, D>(cx: &Arc<Context>, v: T, decoder: D) -> State<T>`
|
||||
|
||||
* **Purpose**: Creates a `State<T>` that automatically updates its value whenever an event of type `E` is emitted to the given `Context`. The `decoder` function converts `&E` into a new `T`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
// Assume MessageChangeEvent is defined
|
||||
let message_state = use_sync_state(
|
||||
cx,
|
||||
"Default message".to_string(),
|
||||
|event: &MessageChangeEvent| event.0.clone()
|
||||
);
|
||||
```
|
||||
|
||||
### `fn use_sync_effect<T, Ev, E>(cx: &Arc<Context>, state: &State<T>, encoder: E, deps: &[&dyn HookDependency])`
|
||||
|
||||
* **Purpose**: Registers an effect that emits an event `Ev` to the given `Context` whenever the monitored `state` changes (or any other specified `deps`). The `encoder` function converts `&State<T>` into an `Ev`.
|
||||
* **Usage**:
|
||||
```rust
|
||||
let count_state = use_state(0);
|
||||
// Assume CounterUpdatedEvent is defined
|
||||
use_sync_effect(
|
||||
cx,
|
||||
&count_state,
|
||||
|s: &State<i32>| CounterUpdatedEvent { new_value: s.get_dl() },
|
||||
&[&count_state]
|
||||
);
|
||||
```
|
||||
|
||||
These hooks provide a complete and reactive state management solution, enabling dynamic and interactive TUI applications in OSUI.
|
||||
|
||||
**Next:** Delve into the details of the [Macros API](./macros-api.md).
|
||||
Reference in New Issue
Block a user