v0.1.0 with spark
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user