Version 0.1.1
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# `osui::elements`
|
||||
|
||||
The `elements` module contains OSUI's built-in UI components, which serve as the fundamental building blocks for constructing your terminal user interfaces. These elements range from simple text to complex layout containers and interactive input fields.
|
||||
|
||||
## Core Element: `String`
|
||||
|
||||
In OSUI, a `String` (or anything that can be formatted into a `String`) implicitly acts as an `Element`. This allows you to directly embed text literals and interpolated strings within your `rsx!` markup.
|
||||
|
||||
### `Element` Trait Implementation for `String`
|
||||
|
||||
```rust
|
||||
impl Element for String {
|
||||
fn render(
|
||||
&mut self,
|
||||
scope: &mut crate::render_scope::RenderScope,
|
||||
_: &crate::render_scope::RenderContext,
|
||||
) {
|
||||
scope.draw_text(0, 0, self); // Draws the string at (0,0) relative to element's transform
|
||||
}
|
||||
|
||||
fn is_ghost(&mut self) -> bool {
|
||||
true // String elements are "ghosts" – they don't draw their own background/border.
|
||||
}
|
||||
|
||||
// as_any and as_any_mut implementations are boilerplate
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
* When a `String` is used as an element, its `render` method simply calls `scope.draw_text` to print itself.
|
||||
* It's marked as `is_ghost() -> true`, meaning it doesn't handle its own layout or background drawing. Its size contributes to its parent's `Dimension::Content` calculation.
|
||||
|
||||
**Usage:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
"Hello, World!" // Simple string literal
|
||||
format!("The answer is: {}", 42) // Formatted string
|
||||
}
|
||||
```
|
||||
|
||||
## Other Built-in Elements
|
||||
|
||||
The `elements` module exports several other essential UI elements, each with its own dedicated documentation:
|
||||
|
||||
* **[`Div`](../reference/elements/div.md)**: A generic, transparent container element used for grouping children and applying layout and style.
|
||||
* **[`FlexRow`](../reference/elements/flex.md)**: A layout container that arranges its children horizontally in a row.
|
||||
* **[`FlexCol`](../reference/elements/flex.md)**: A layout container that arranges its children vertically in a column.
|
||||
* **[`Heading`](../reference/elements/heading.md)**: Renders large, stylized ASCII art text using `figlet-rs`.
|
||||
* **[`Input`](../reference/elements/input.md)**: An interactive element for user text input.
|
||||
* **[`Paginator`](../reference/elements/paginator.md)**: A container that manages multiple "pages" (children) and allows navigation between them.
|
||||
|
||||
These elements, combined with the `Transform` and `Style` components, provide a powerful foundation for building diverse and complex terminal user interfaces in OSUI.
|
||||
@@ -0,0 +1,87 @@
|
||||
# `osui::elements::div`
|
||||
|
||||
The `Div` element is a fundamental building block in OSUI, serving as a generic, transparent container. It doesn't have its own visual representation by default but is primarily used for grouping other elements and applying layout (`Transform`) and styling (`Style`) to a collection of children.
|
||||
|
||||
## `Div` Struct
|
||||
|
||||
```rust
|
||||
pub struct Div {
|
||||
children: Vec<Arc<Widget>>, // Children widgets contained within this Div
|
||||
size: (u16, u16), // Internal tracking of the Div's calculated size
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Div::new() -> Self`
|
||||
Creates a new `Div` instance with no children and default size.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_div = Div::new(); // Create a Div programmatically
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, _: &RenderContext)`
|
||||
This method, for `Div`, primarily focuses on setting the `RenderScope`'s area based on its calculated size or default values. It does *not* issue any direct drawing commands (e.g., `draw_text`, `draw_rect`) for itself, as `Div` is transparent by default.
|
||||
|
||||
#### `after_render(&mut self, scope: &mut RenderScope, render_context: &RenderContext)`
|
||||
This is the crucial method for container elements like `Div`. It's called after the `Div` itself has been processed by the rendering pipeline.
|
||||
1. It clones the `RenderScope`'s `RawTransform` to use as a basis for its children's layout.
|
||||
2. It creates a `DivRenderer` (an `ElementRenderer` helper) which will adjust child positions relative to the `Div`.
|
||||
3. It temporarily sets the `RenderScope`'s `parent_size` to its own calculated content area. This ensures that children using `Dimension::Full` or `Position::Center`/`End` resolve correctly within the `Div`'s bounds.
|
||||
4. It iterates through its `children` and calls `scope.render_widget` for each, effectively triggering the rendering pipeline for its nested elements.
|
||||
5. After children are rendered, it restores the original `parent_size` to the `RenderScope`.
|
||||
6. It updates its internal `self.size` based on the accumulated size of its children (as reported by `DivRenderer`).
|
||||
|
||||
#### `draw_child(&mut self, element: &Arc<Widget>)`
|
||||
This method is called by the `rsx!` macro or `Rsx::draw_parent` when a widget is declared as a child of this `Div`.
|
||||
1. It adds the `element` to its internal `children` `Vec`.
|
||||
2. It injects a `NoRenderRoot` component into the child widget. This is critical: it tells the main `Screen` rendering loop *not* to render this child directly, as the `Div` itself will handle its rendering in `after_render`. This prevents double-rendering and ensures correct layout.
|
||||
|
||||
#### `is_ghost(&mut self) -> bool`
|
||||
Returns `true`. A `Div` is a "ghost" element because it primarily serves as a logical grouping and layout container and does not draw any visual representation (like a background or border) itself. Any visual properties are applied via external `Style` components associated with the `Div`'s widget.
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations for downcasting.
|
||||
|
||||
## `DivRenderer`
|
||||
|
||||
A helper struct that implements `ElementRenderer` specifically for `Div` to adjust the `RenderScope` for its children.
|
||||
|
||||
```rust
|
||||
pub struct DivRenderer<'a>(pub &'a mut RawTransform);
|
||||
```
|
||||
|
||||
### `ElementRenderer` Trait Implementation for `DivRenderer`
|
||||
|
||||
#### `before_draw(&mut self, scope: &mut RenderScope, _widget: &Arc<Widget>)`
|
||||
This method is called for each child widget of the `Div` just before that child is drawn.
|
||||
1. It updates the `Div`'s own `RawTransform` (`self.0`) to expand its `width` and `height` to encompass the child's area plus its padding.
|
||||
2. It translates the child's `RawTransform` (`t`) by the `Div`'s absolute position and padding. This ensures children are positioned correctly *inside* the `Div`.
|
||||
3. It updates the child's `RawTransform` padding by adding the `Div`'s padding.
|
||||
|
||||
## Usage in `rsx!`
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// A Div with a solid background and padding, containing text and a FlexRow
|
||||
@Transform::new().dimensions(40, 10).center().padding(2, 1);
|
||||
@Style { background: Background::Solid(0x333333), foreground: Some(0xFFFFFF) };
|
||||
Div {
|
||||
"This is content inside the Div."
|
||||
"It will respect the Div's padding and dimensions."
|
||||
|
||||
FlexRow, gap: 1, {
|
||||
"Nested"
|
||||
"FlexRow"
|
||||
"Elements"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
`Div` is an essential tool for structuring your UI, applying common styles, and managing the layout of groups of widgets.
|
||||
@@ -0,0 +1,161 @@
|
||||
# `osui::elements::flex`
|
||||
|
||||
The `flex` module provides `FlexRow` and `FlexCol` elements, which are powerful layout containers for automatically arranging their children either horizontally or vertically, with optional spacing. They are "ghost" elements, meaning they control the layout of their children but don't draw any visual elements themselves by default.
|
||||
|
||||
## `FlexRow` Struct
|
||||
|
||||
Arranges children in a row (horizontally).
|
||||
|
||||
```rust
|
||||
pub struct FlexRow {
|
||||
pub gap: u16, // Spacing in cells between adjacent children horizontally
|
||||
children: Vec<Arc<Widget>>,
|
||||
size: (u16, u16), // Internal tracking of the FlexRow's calculated size
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `FlexRow::new() -> Self`
|
||||
Creates a new `FlexRow` instance with no children, no gap, and default size.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_row = FlexRow::new();
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation for `FlexRow`
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, _: &RenderContext)`
|
||||
Similar to `Div`, this method primarily ensures the `RenderScope`'s area reflects its calculated size. It doesn't draw anything visually for the `FlexRow` itself.
|
||||
|
||||
#### `after_render(&mut self, scope: &mut RenderScope, render_context: &RenderContext)`
|
||||
This method calculates and applies the layout for its children:
|
||||
1. Clones the `RenderScope`'s `RawTransform` to use as a basis.
|
||||
2. Stores the original parent size from the `RenderScope`.
|
||||
3. Sets the `RenderScope`'s `parent_size` to its own (FlexRow's) determined `width` and `height`.
|
||||
4. Initializes `v = 0`; this variable tracks the current horizontal offset for placing children.
|
||||
5. Creates a `RowRenderer` helper which will modify `RenderScope`'s transforms for each child.
|
||||
6. Iterates through `self.children`, calling `scope.render_widget` for each. The `RowRenderer` updates the `x` position for each child and increments `v` to prepare for the next child.
|
||||
7. Restores the original `parent_size` to the `RenderScope`.
|
||||
8. Updates `self.size` based on the final accumulated width and maximum height of its children (as calculated by `RowRenderer`).
|
||||
|
||||
#### `draw_child(&mut self, element: &Arc<Widget>)`
|
||||
Adds a child `Widget` to the `FlexRow`'s internal `children` list and injects `NoRenderRoot` into the child to prevent direct rendering by the main `Screen` loop.
|
||||
|
||||
#### `is_ghost(&mut self) -> bool`
|
||||
Returns `true`, as `FlexRow` is a layout-only container.
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations for downcasting.
|
||||
|
||||
## `FlexCol` Struct
|
||||
|
||||
Arranges children in a column (vertically).
|
||||
|
||||
```rust
|
||||
pub struct FlexCol {
|
||||
pub gap: u16, // Spacing in cells between adjacent children vertically
|
||||
children: Vec<Arc<Widget>>,
|
||||
size: (u16, u16), // Internal tracking of the FlexCol's calculated size
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `FlexCol::new() -> Self`
|
||||
Creates a new `FlexCol` instance with no children, no gap, and default size.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_col = FlexCol::new();
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation for `FlexCol`
|
||||
|
||||
The implementation mirrors `FlexRow`, but for vertical arrangement:
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, _: &RenderContext)`
|
||||
Ensures `RenderScope` area is set. No direct drawing.
|
||||
|
||||
#### `after_render(&mut self, scope: &mut RenderScope, render_context: &RenderContext)`
|
||||
1. Clones `RawTransform`.
|
||||
2. Sets `RenderScope` `parent_size` to its own dimensions.
|
||||
3. Initializes `v = 0` (this variable tracks the current vertical offset).
|
||||
4. Creates a `ColumnRenderer` helper.
|
||||
5. Iterates `children`, calling `scope.render_widget`. The `ColumnRenderer` updates the `y` position for each child and increments `v` for the next child.
|
||||
6. Restores original `parent_size`.
|
||||
7. Updates `self.size` based on the accumulated height and maximum width of its children.
|
||||
|
||||
#### `draw_child(&mut self, element: &Arc<Widget>)`
|
||||
Adds child and injects `NoRenderRoot`.
|
||||
|
||||
#### `is_ghost(&mut self) -> bool`
|
||||
Returns `true`.
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations.
|
||||
|
||||
## `RowRenderer` (Helper)
|
||||
|
||||
An `ElementRenderer` implementation specifically for `FlexRow` to adjust the `RenderScope` for its children's positions.
|
||||
|
||||
```rust
|
||||
pub struct RowRenderer<'a>(&'a mut RawTransform, u16, &'a mut u16);
|
||||
```
|
||||
|
||||
### `ElementRenderer` Trait Implementation for `RowRenderer`
|
||||
|
||||
#### `before_draw(&mut self, scope: &mut RenderScope, _widget: &Arc<Widget>)`
|
||||
Called for each child of a `FlexRow` just before the child is drawn.
|
||||
1. Updates the `FlexRow`'s `RawTransform` (`self.0`) to encompass the child's area and the running horizontal offset.
|
||||
2. Adjusts the child's `RawTransform` (`t`) by the parent `FlexRow`'s absolute position and adds the current horizontal offset (`*self.2`).
|
||||
3. Increments `*self.2` (the running horizontal offset) by the child's width, its horizontal padding, and the `gap` to prepare for the next child.
|
||||
4. Adds the parent `FlexRow`'s padding to the child's `RawTransform` padding.
|
||||
|
||||
## `ColumnRenderer` (Helper)
|
||||
|
||||
An `ElementRenderer` implementation specifically for `FlexCol` to adjust the `RenderScope` for its children's positions.
|
||||
|
||||
```rust
|
||||
pub struct ColumnRenderer<'a>(&'a mut RawTransform, u16, &'a mut u16);
|
||||
```
|
||||
|
||||
### `ElementRenderer` Trait Implementation for `ColumnRenderer`
|
||||
|
||||
#### `before_draw(&mut self, scope: &mut RenderScope, _widget: &Arc<Widget>)`
|
||||
Called for each child of a `FlexCol` just before the child is drawn.
|
||||
1. Updates the `FlexCol`'s `RawTransform` (`self.0`) to encompass the child's area and the running vertical offset.
|
||||
2. Adjusts the child's `RawTransform` (`t`) by the parent `FlexCol`'s absolute position and adds the current vertical offset (`*self.2`).
|
||||
3. Increments `*self.2` (the running vertical offset) by the child's height, its vertical padding, and the `gap` to prepare for the next child.
|
||||
4. Adds the parent `FlexCol`'s padding to the child's `RawTransform` padding.
|
||||
|
||||
## Usage in `rsx!`
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// A FlexRow with 2 cells gap between items
|
||||
@Transform::new().padding(1,1);
|
||||
@Style { background: Background::Outline(0x00FF00) };
|
||||
FlexRow, gap: 2, {
|
||||
"First Item"
|
||||
Div { "Second Item (a Div)" }
|
||||
@Style { foreground: Some(0xFF0000) };
|
||||
"Third Item (Red Text)"
|
||||
}
|
||||
|
||||
// A FlexCol with 1 cell gap, centered horizontally
|
||||
@Transform::new().x(Center).margin(0, 5); // Margin to separate from the row above
|
||||
@Style { background: Background::Outline(0x0000FF) };
|
||||
FlexCol, gap: 1, {
|
||||
"Column Item 1"
|
||||
Input { } // An input field
|
||||
"Column Item 3"
|
||||
}
|
||||
}
|
||||
```
|
||||
Flex containers are powerful for building responsive and neatly aligned layouts without manual coordinate calculations.
|
||||
@@ -0,0 +1,67 @@
|
||||
# `osui::elements::heading`
|
||||
|
||||
The `Heading` element provides a way to render large, stylized text using FIGlet fonts (ASCII art). It's suitable for titles, banners, and decorative text in your terminal UI.
|
||||
|
||||
## `Heading` Struct
|
||||
|
||||
```rust
|
||||
pub struct Heading {
|
||||
pub font: FIGfont, // The FIGfont instance to use for rendering
|
||||
pub smooth: bool, // If true, attempts to replace ASCII art characters with Unicode line drawing characters
|
||||
children: Vec<Arc<Widget>>, // Holds children, usually a single String element
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Heading::new() -> Heading`
|
||||
Creates a new `Heading` instance. By default, it uses the `FIGfont::standard()` font and `smooth` is `false`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_heading = Heading::new();
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, _: &RenderContext)`
|
||||
This method performs the core rendering for the `Heading`.
|
||||
1. It iterates through its `children` (typically expects a single `String` child).
|
||||
2. It attempts to downcast the child's `Element` to a `String` and concatenates the text.
|
||||
3. It then uses the `FIGfont::convert` method to transform the accumulated text into ASCII art.
|
||||
4. If `self.smooth` is `true`, it replaces common ASCII art characters (`-` and `|`) with their Unicode line-drawing equivalents (`─` and `│`) for a cleaner look.
|
||||
5. Finally, it calls `scope.draw_text(0, 0, ...)` to render the generated ASCII art. The `RenderScope`'s width and height will automatically be updated by `draw_text` to encompass the size of the rendered ASCII art.
|
||||
|
||||
#### `draw_child(&mut self, element: &Arc<Widget>)`
|
||||
This method is called when an element is declared as a child of the `Heading` (e.g., the text within its `rsx!` block).
|
||||
1. It injects a `NoRenderRoot` component into the child. This is important to ensure the child `String` element is not rendered independently by the main `Screen` loop, but rather its content is *read* by the `Heading` and then the `Heading` renders the ASCII art.
|
||||
2. It adds the `element` to its internal `children` `Vec`.
|
||||
|
||||
#### `is_ghost(&mut self) -> bool`
|
||||
Returns `true`. `Heading` is a "ghost" element because it primarily processes and renders the content of its children into a new visual form (ASCII art) rather than directly displaying its own structural properties. Any background or other styling would be applied via a `Style` component on the `Heading`'s widget itself.
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations for downcasting.
|
||||
|
||||
## Usage in `rsx!`
|
||||
|
||||
You typically pass the text for the `Heading` as a child. You can also set its `smooth` property.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// A standard FIGlet heading
|
||||
Heading { "OSUI" }
|
||||
|
||||
// A smooth FIGlet heading, separated by a margin
|
||||
@Transform::new().margin(0, 5); // Add some vertical space
|
||||
Heading, smooth: true, { "Awesome!" }
|
||||
|
||||
// Using Heading with dynamic text from state
|
||||
%my_dynamic_text_state
|
||||
Heading { format!("Hello {}", my_dynamic_text_state.get()) }
|
||||
}
|
||||
```
|
||||
`Heading` is an easy way to add visual flair and prominence to titles in your TUI.
|
||||
@@ -0,0 +1,83 @@
|
||||
# `osui::elements::input`
|
||||
|
||||
The `Input` element provides a basic interactive text input field for your OSUI applications. It allows users to type, backspace, delete characters, and move the cursor within the input area.
|
||||
|
||||
## `Input` Struct
|
||||
|
||||
```rust
|
||||
pub struct Input {
|
||||
pub state: State<String>, // Reactive state holding the input string
|
||||
cursor: usize, // Current cursor position within the string
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Input::new() -> Self`
|
||||
Creates a new `Input` instance with an empty `String` for its `state` and the `cursor` at position `0`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_input = Input::new();
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, render_context: &RenderContext)`
|
||||
This method is responsible for drawing the input field's current text and the cursor.
|
||||
1. It retrieves the current string from `self.state`.
|
||||
2. It calls `scope.draw_text(0, 0, &s)` to render the entire input string.
|
||||
3. **Cursor Rendering (Focus Indicator)**: If `render_context.is_focused()` is `true` (meaning this `Input` widget has keyboard focus), it draws an "inverted" character at the current `self.cursor` position. If the cursor is at the end of the string, it draws an inverted space. This visually indicates where the user is typing.
|
||||
|
||||
#### `event(&mut self, event: &dyn Event)`
|
||||
This method handles incoming `crossterm::event::Event`s, specifically keyboard input, when the `Input` widget is focused.
|
||||
It checks if the event is a `KeyEvent` and if modifiers (other than `Shift`) are present, it ignores the event to prevent unintended actions (e.g., `Ctrl+C`).
|
||||
It then matches on `KeyCode`:
|
||||
* **`KeyCode::Char(c)`**: Inserts the character `c` at the `cursor` position in the `state` string and increments `cursor`.
|
||||
* **`KeyCode::Backspace`**: If `cursor > 0`, removes the character before the cursor and decrements `cursor`.
|
||||
* **`KeyCode::Delete`**: If `cursor` is not at the end of the string, removes the character at the `cursor` position.
|
||||
* **`KeyCode::Left`**: Moves `cursor` one position to the left (if not already at `0`).
|
||||
* **`KeyCode::Right`**: Moves `cursor` one position to the right (if not already at the end of the string).
|
||||
After any modification, the `Input`'s `state` is automatically marked as changed (due to `DerefMut` on `State::get()`), triggering a re-render.
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations for downcasting.
|
||||
|
||||
## Usage in `rsx!`
|
||||
|
||||
The `Input` element needs to be part of the widget tree. For it to receive keyboard input, it must be the `focused` widget. You typically achieve this using the `Focused` component (provided by `RelativeFocusExtension`).
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let screen = Screen::new();
|
||||
screen.extension(InputExtension); // Essential for keyboard input
|
||||
screen.extension(RelativeFocusExtension::new()); // Manages focus
|
||||
|
||||
rsx! {
|
||||
FlexCol, gap: 1, {
|
||||
"Username:"
|
||||
@Transform::new().dimensions(30, 1).padding(1, 0); // Give it some padding
|
||||
@Style { background: Background::Outline(0xAAAAAA), foreground: Some(0xFFFFFF) };
|
||||
@Focused; // This input will be focused by default
|
||||
Input { }
|
||||
|
||||
"Password:"
|
||||
@Transform::new().dimensions(30, 1).padding(1, 0);
|
||||
@Style { background: Background::RoundedOutline(0xAAAAAA), foreground: Some(0xFFFFFF) };
|
||||
Input { } // This input will only be focused via navigation (e.g., Shift+Down arrow)
|
||||
}
|
||||
}
|
||||
.draw(&screen);
|
||||
|
||||
screen.run()
|
||||
}
|
||||
```
|
||||
**Accessing Input Value:**
|
||||
The `Input` element manages its own `State<String>`. If you need to access the typed value from another part of your application (e.g., when a submit button is pressed), you would typically:
|
||||
1. **Inject your own `State<String>`**: Instead of `Input { }`, you could modify `Input::new()` or create a custom `Input` variant that takes an external `State<String>` to bind to.
|
||||
2. **Get component by ID**: If using `IdExtension`, you could assign an `Id` to the `Input` widget, then later retrieve the `Arc<Widget>` by ID and call `widget.get::<Input>()` to access its `state` field.
|
||||
|
||||
The `Input` element provides a crucial interactive component for building forms and dynamic data entry in your TUI.
|
||||
@@ -0,0 +1,96 @@
|
||||
# `osui::elements::paginator`
|
||||
|
||||
The `Paginator` element is a container that manages a collection of child widgets, displaying only one child at a time. It provides built-in logic to navigate between these "pages" using keyboard events (specifically, `Tab` and `Shift+Tab`).
|
||||
|
||||
## `Paginator` Struct
|
||||
|
||||
```rust
|
||||
pub struct Paginator {
|
||||
children: Vec<Arc<Widget>>, // The list of pages/children
|
||||
size: (u16, u16), // Internal tracking of the Paginator's calculated size
|
||||
index: usize, // The index of the currently displayed child
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Paginator::new() -> Self`
|
||||
Creates a new `Paginator` instance with no children and an initial `index` of `0`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_paginator = Paginator::new();
|
||||
```
|
||||
|
||||
### `Element` Trait Implementation
|
||||
|
||||
#### `render(&mut self, scope: &mut RenderScope, _: &RenderContext)`
|
||||
This method primarily focuses on setting the `RenderScope`'s area based on its calculated size. It does not draw any visual elements for the `Paginator` itself, acting as a "ghost" element.
|
||||
|
||||
#### `after_render(&mut self, scope: &mut RenderScope, render_context: &RenderContext)`
|
||||
This is the core rendering logic for `Paginator`:
|
||||
1. It checks if `self.index` points to a valid child in `self.children`.
|
||||
2. If a child exists at the current `index`, it prepares a `RenderScope` (cloning its own `RawTransform`).
|
||||
3. It sets the `RenderScope`'s `parent_size` to its own (Paginator's) determined `width` and `height`, so the child lays out correctly within the Paginator's bounds.
|
||||
4. It creates a `DivRenderer` helper to manage the child's positioning.
|
||||
5. It calls `scope.render_widget` for *only the currently active child widget*.
|
||||
6. After the child is rendered, it restores the original `parent_size` to the `RenderScope`.
|
||||
7. It updates its internal `self.size` to reflect the size of the currently displayed child (plus any accumulated size from `DivRenderer`).
|
||||
|
||||
#### `event(&mut self, event: &dyn Event)`
|
||||
This method handles keyboard events for navigation:
|
||||
1. It listens for `crossterm::event::Event::Key` events.
|
||||
2. If `KeyCode::Tab` is pressed:
|
||||
* It increments `self.index`. If `self.index` goes beyond the last child, it wraps around to `0`.
|
||||
3. If `KeyCode::BackTab` (Shift+Tab) is pressed:
|
||||
* It decrements `self.index`. If `self.index` goes below `0`, it wraps around to the last child.
|
||||
These index changes will trigger a re-render in the next frame, displaying the new page.
|
||||
|
||||
#### `is_ghost(&mut self) -> bool`
|
||||
Returns `true`. `Paginator` is a "ghost" element because it is a logical container that controls which of its children is visible, but it does not draw itself. Any styling applied to the `Paginator` widget will apply to the area it manages.
|
||||
|
||||
#### `draw_child(&mut self, element: &Arc<Widget>)`
|
||||
This method is called when a widget is declared as a child of this `Paginator` in `rsx!`.
|
||||
1. It adds the `element` to its internal `children` `Vec`.
|
||||
2. It injects a `NoRenderRoot` component into the child to ensure the main `Screen` rendering loop doesn't render it directly (the `Paginator` handles rendering the active child).
|
||||
|
||||
#### `as_any(&self) -> &dyn std::any::Any` / `as_any_mut(&mut self) -> &mut dyn std::any::Any`
|
||||
Standard implementations for downcasting.
|
||||
|
||||
## Usage in `rsx!`
|
||||
|
||||
The direct children of a `Paginator` element become its pages. You can use any other element (e.g., `Div`, `FlexCol`) as a page container.
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
// Paginator often needs explicit dimensions to define the space for its pages
|
||||
@Transform::new().dimensions(60, 20).center();
|
||||
@Style { background: Background::Solid(0x222222) }; // Optional background for the paginator's area
|
||||
Paginator {
|
||||
// Page 1: Simple text and instructions
|
||||
FlexCol {
|
||||
"Welcome to the first page!"
|
||||
"Press TAB to go to the next page."
|
||||
}
|
||||
|
||||
// Page 2: Contains an input field
|
||||
FlexCol {
|
||||
"This is the second page."
|
||||
"Type something here:"
|
||||
@Transform::new().dimensions(30, 1);
|
||||
@Style { background: Background::Outline(0x555555) };
|
||||
Input { }
|
||||
}
|
||||
|
||||
// Page 3: A heading
|
||||
FlexCol {
|
||||
Heading { "The End" }
|
||||
"This is the last page. Shift+TAB to go back."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
`Paginator` is useful for organizing complex UIs into logical sections, preventing clutter, and improving user experience by allowing easy navigation between distinct views.
|
||||
@@ -0,0 +1,186 @@
|
||||
# `osui::extensions`
|
||||
|
||||
The `extensions` module defines the core traits and types for extending OSUI's functionality. It provides a global event bus and lifecycle hooks that allow custom logic to interact with the entire UI application.
|
||||
|
||||
## `Extension` Trait
|
||||
|
||||
The central trait for adding global behaviors to an OSUI application. Any struct implementing this trait can be registered with the `Screen` to receive lifecycle and event callbacks.
|
||||
|
||||
```rust
|
||||
pub trait Extension {
|
||||
/// Called once when the extension is registered with the `Screen`.
|
||||
#[allow(unused)]
|
||||
fn init(&mut self, _ctx: &Context) {}
|
||||
|
||||
/// Called when any `Event` is dispatched across the system.
|
||||
#[allow(unused)]
|
||||
fn event(&mut self, _ctx: &Context, _event: &dyn Event) {}
|
||||
|
||||
/// Called when `Screen::close()` is invoked, before the application terminates.
|
||||
#[allow(unused)]
|
||||
fn on_close(&mut self) {}
|
||||
|
||||
/// Called before any widgets are rendered in a frame, with a scope for the entire screen.
|
||||
#[allow(unused)]
|
||||
fn render(&mut self, _ctx: &Context, _scope: &mut RenderScope) {}
|
||||
|
||||
/// Called before a specific widget's `Element::render` method is invoked.
|
||||
#[allow(unused)]
|
||||
fn render_widget(&mut self, _ctx: &Context, _scope: &mut RenderScope, _widget: &Arc<Widget>) {}
|
||||
|
||||
/// Called after a specific widget's `Element::after_render` method is invoked.
|
||||
#[allow(unused)]
|
||||
fn after_render_widget(
|
||||
&mut self,
|
||||
_ctx: &Context,
|
||||
_scope: &mut RenderScope,
|
||||
_widget: &Arc<Widget>,
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
## `Event` Trait
|
||||
|
||||
A marker trait for types that can be dispatched as events within the OSUI system. Events are type-erased (`dyn Event`) when dispatched, requiring downcasting to retrieve their specific type.
|
||||
|
||||
```rust
|
||||
pub trait Event: Send + Sync {
|
||||
/// Returns a type-erased reference to this object, enabling downcasting.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
impl<'a> dyn Event + 'a {
|
||||
/// Attempts to downcast a dynamic `Event` trait object to a concrete type `T`.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// * `T`: The concrete event type to downcast to. Must also implement `Event`.
|
||||
///
|
||||
/// # Returns
|
||||
/// `Some(&T)` if the downcast is successful, `None` otherwise.
|
||||
pub fn get<T: Event + 'static>(&self) -> Option<&T> {
|
||||
self.as_any().downcast_ref()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `Context`
|
||||
|
||||
The `Context` struct provides a way for extensions and widgets to interact with the global OSUI `Screen` instance. It holds an `Arc<Screen>` and offers convenience methods for dispatching events, querying widgets, and accessing components.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
screen: Arc<Screen>,
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Context::new(screen: Arc<Screen>) -> Self`
|
||||
Creates a new `Context` instance.
|
||||
|
||||
**Arguments:**
|
||||
* `screen`: An `Arc` to the `Screen` instance that this context will operate on.
|
||||
|
||||
#### `Context::event<E: Event + Clone + 'static>(&self, e: &E)`
|
||||
Dispatches an event `e` throughout the OSUI system.
|
||||
This will:
|
||||
1. Call the `event` method on all widgets (specifically, on any `Handler<E>` components attached to them, and on the `Element::event` method of focused widgets).
|
||||
2. Call the `event` method on all registered `Extension`s.
|
||||
|
||||
**Arguments:**
|
||||
* `e`: A reference to the event to dispatch.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
// Inside an extension or a component:
|
||||
// self.ctx.event(&MyCustomEvent { /* ... */ });
|
||||
```
|
||||
|
||||
#### `Context::get_widgets(&self) -> MutexGuard<Vec<Arc<Widget>>>`
|
||||
Returns a `MutexGuard` to the `Vec` of `Arc<Widget>` that the `Screen` is currently managing. This allows extensions to iterate over and manipulate the global list of widgets.
|
||||
|
||||
**Returns:**
|
||||
A `MutexGuard` providing mutable access to the list of root widgets.
|
||||
|
||||
#### `Context::iter_components<C: Component + 'static + Clone, F: FnMut(&Arc<Widget>, Option<C>)>(&self, mut iterator: F)`
|
||||
Iterates over all widgets managed by the `Screen` and applies a provided closure to each widget, along with an `Option` of a cloned component of type `C` if the widget has one.
|
||||
|
||||
**Type Parameters:**
|
||||
* `C`: The `Component` type to look for.
|
||||
* `F`: The closure to execute for each widget.
|
||||
|
||||
**Arguments:**
|
||||
* `iterator`: A closure that takes an `Arc<Widget>` and an `Option<C>`.
|
||||
|
||||
#### `Context::get_components<C: Component + 'static + Clone>(&self) -> Vec<C>`
|
||||
Collects all instances of a specific `Component` type from all widgets managed by the `Screen` into a `Vec`.
|
||||
|
||||
**Type Parameters:**
|
||||
* `C`: The `Component` type to collect.
|
||||
|
||||
**Returns:**
|
||||
A `Vec` containing cloned instances of component `C`.
|
||||
|
||||
#### `Context::render_root(&self, scope: &mut RenderScope)`
|
||||
Calls the `render` hook for all registered `Extension`s, allowing them to draw global elements that span the entire screen. This is typically called once per frame before individual widgets are rendered.
|
||||
|
||||
**Arguments:**
|
||||
* `scope`: The `RenderScope` representing the entire screen.
|
||||
|
||||
#### `Context::render(&self, w: &Arc<Widget>, scope: &mut RenderScope)`
|
||||
Calls the `render_widget` hook for all registered `Extension`s for a specific `widget`. This is invoked during the rendering of each individual widget.
|
||||
|
||||
**Arguments:**
|
||||
* `w`: The `Arc<Widget>` currently being rendered.
|
||||
* `scope`: The `RenderScope` for `w`.
|
||||
|
||||
#### `Context::after_render(&self, w: &Arc<Widget>, scope: &mut RenderScope)`
|
||||
Calls the `after_render_widget` hook for all registered `Extension`s for a specific `widget`. This is invoked after a widget's `Element::after_render` method has completed.
|
||||
|
||||
**Arguments:**
|
||||
* `w`: The `Arc<Widget>` that has just finished its `after_render` phase.
|
||||
* `scope`: The `RenderScope` for `w`.
|
||||
|
||||
## `Handler<E>` (Component)
|
||||
|
||||
A component that allows a widget to listen for and react to specific `Event` types `E`.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct Handler<E: Event>(Arc<Mutex<dyn FnMut(&Arc<Widget>, &E) + Send + Sync>>);
|
||||
```
|
||||
|
||||
### Implementations
|
||||
|
||||
#### `impl<E: Event + 'static> Component for Handler<E>`
|
||||
`Handler<E>` implements the `Component` trait, allowing it to be attached to any `Widget`.
|
||||
|
||||
#### `impl<E: Event + 'static> Handler<E>`
|
||||
#### `Handler::new<F: FnMut(&Arc<Widget>, &E) + Send + Sync + 'static>(f: F) -> Handler<E>`
|
||||
Creates a new `Handler<E>` with the given mutable closure `f`. This closure will be called when an event of type `E` is dispatched.
|
||||
|
||||
**Arguments:**
|
||||
* `f`: The closure to execute when the event occurs. It receives the `Arc<Widget>` it's attached to and a reference to the event.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
event!(ButtonClicked);
|
||||
|
||||
rsx! {
|
||||
@Handler::new(|widget_arc, event: &ButtonClicked| {
|
||||
println!("Button clicked on widget: {:?}", widget_arc);
|
||||
});
|
||||
Div { "My Button" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Handler::call(&self, w: &Arc<Widget>, e: &E)`
|
||||
Invokes the internal closure with the provided widget and event. This method is called internally by `Widget::event`.
|
||||
|
||||
**Arguments:**
|
||||
* `w`: The `Arc<Widget>` that owns this handler.
|
||||
* `e`: The event to process.
|
||||
@@ -0,0 +1,86 @@
|
||||
# `osui::extensions::focus`
|
||||
|
||||
The `focus` module provides extensions and components for managing keyboard focus within your OSUI application. It enables navigation between widgets using keyboard input, which is crucial for interactive elements like `Input` fields.
|
||||
|
||||
## Components
|
||||
|
||||
### `AlwaysFocused` (Component)
|
||||
```rust
|
||||
component!(AlwaysFocused);
|
||||
```
|
||||
A marker component that, when attached to a widget, ensures that the widget remains focused regardless of user navigation. This is useful for global event handlers or root containers that should always receive input.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@AlwaysFocused;
|
||||
@Handler::new(|_, e: &crossterm::event::Event| {
|
||||
// This handler will always receive events.
|
||||
});
|
||||
Div { "Global Handler" }
|
||||
}
|
||||
```
|
||||
|
||||
### `Focused` (Component)
|
||||
```rust
|
||||
component!(Focused);
|
||||
```
|
||||
A marker component that, when attached to a widget, indicates that this widget should be initially focused when the application starts or when focus is determined. The `RelativeFocusExtension` uses this to set initial focus.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
FlexCol {
|
||||
"Username:"
|
||||
@Focused; // This Input will be focused by default
|
||||
Input { }
|
||||
"Password:"
|
||||
Input { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `RelativeFocusExtension`
|
||||
|
||||
This extension manages focus between eligible widgets based on their relative positions on the screen. It allows users to navigate the UI using arrow keys (often combined with Shift).
|
||||
|
||||
```rust
|
||||
pub struct RelativeFocusExtension {
|
||||
cursor: usize, // Internal index of the currently focused widget
|
||||
rendered: Arc<Mutex<Vec<(usize, u16, u16)>>>, // Stores widget indices and their (x,y) positions
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `RelativeFocusExtension::new() -> Self`
|
||||
Creates a new instance of the `RelativeFocusExtension`.
|
||||
|
||||
### `Extension` Trait Implementation
|
||||
|
||||
#### `init(&mut self, _ctx: &Context)`
|
||||
When initialized, it checks existing widgets for the `Focused` component and sets the first one found as focused.
|
||||
|
||||
#### `event(&mut self, ctx: &Context, event: &dyn Event)`
|
||||
Listens for `crossterm::event::Event`s, specifically `KeyCode::Right`, `Left`, `Up`, `Down` when combined with `KeyModifiers::SHIFT`.
|
||||
When such a key is pressed, it calculates the closest eligible widget in the specified direction based on the `rendered` positions and updates `self.cursor` to that widget's index. It then sets the focus state (`set_focused`) for all widgets accordingly.
|
||||
Widgets with the `AlwaysFocused` component remain focused.
|
||||
|
||||
#### `render(&mut self, _ctx: &Context, _scope: &mut RenderScope)`
|
||||
Clears the internal `rendered` list at the beginning of each frame. This ensures that widget positions are recalculated based on the latest render.
|
||||
|
||||
#### `after_render_widget(&mut self, ctx: &Context, scope: &mut RenderScope, widget: &Arc<Widget>)`
|
||||
After each widget is rendered, this hook captures its absolute position (`RawTransform.x`, `RawTransform.y`) and its index in the `Screen`'s widget list, storing it in the `rendered` list. This data is then used by the `event` method for focus navigation calculations. It runs in a separate thread for performance.
|
||||
|
||||
### How Focus Navigation Works
|
||||
|
||||
1. **Position Tracking**: During the `after_render_widget` phase, the `RelativeFocusExtension` records the `(x, y)` coordinates of every non-ghost widget that is rendered.
|
||||
2. **Event Listening**: When the user presses `Shift + Arrow Key`, the `event` method is triggered.
|
||||
3. **Closest Widget Calculation**: The `find_closest_in_direction` helper function (internal to the module) determines the next best widget to focus. It prioritizes:
|
||||
* Widgets directly in the line of the arrow key (same row for left/right, same column for up/down).
|
||||
* If no direct match, it finds the geographically closest widget in the general direction.
|
||||
4. **Focus Update**: The extension then updates the `focused` state on widgets using `widget.set_focused(true/false)`. Widgets with the `Focused` component (usually `Input` fields) will then react to subsequent key events.
|
||||
|
||||
**Note on `Tab` / `BackTab`**: While `RelativeFocusExtension` handles arrow keys, `Paginator` elements handle `Tab` and `Shift+Tab` internally to cycle through their pages. For general `Tab` navigation across *all* focusable elements in the UI, you would implement a custom `Handler` on a root widget that iterates through widgets and sets focus, or extend `RelativeFocusExtension` to handle `Tab` more generically.
|
||||
@@ -0,0 +1,78 @@
|
||||
# `osui::extensions::id`
|
||||
|
||||
The `id` module provides a simple way to assign unique identifiers to OSUI widgets and retrieve them later by that ID. This is useful for direct access to specific UI elements for manipulation or querying.
|
||||
|
||||
## `Id` (Component)
|
||||
|
||||
```rust
|
||||
component!(Id(pub usize));
|
||||
```
|
||||
A tuple struct component that holds a `usize` value, representing a unique identifier for the widget it's attached to.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Id(101); // Assign ID 101 to this Div
|
||||
Div { "My uniquely identified div" }
|
||||
}
|
||||
```
|
||||
|
||||
## `IdExtension`
|
||||
|
||||
The `IdExtension` provides functionality to look up widgets by their `Id` component.
|
||||
|
||||
```rust
|
||||
pub struct IdExtension(pub Arc<Screen>);
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `IdExtension::new(screen: Arc<Screen>) -> Arc<Self>`
|
||||
Creates a new `IdExtension` instance. It requires an `Arc<Screen>` because it needs to access the screen's list of widgets to perform ID lookups.
|
||||
|
||||
**Arguments:**
|
||||
* `screen`: An `Arc` to the main `Screen` instance.
|
||||
|
||||
**Returns:**
|
||||
An `Arc<IdExtension>`. It's recommended to store extensions that are queried by other parts of your app in an `Arc` so they can be easily shared.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let id_ext = IdExtension::new(screen.clone());
|
||||
screen.extension(id_ext.clone()); // Register the extension
|
||||
```
|
||||
|
||||
#### `IdExtension::get_element(self: &Arc<IdExtension>, id: usize) -> Option<Arc<Widget>>`
|
||||
Retrieves an `Arc<Widget>` from the `Screen`'s widget list that has an `Id` component matching the provided `id`.
|
||||
|
||||
**Arguments:**
|
||||
* `id`: The `usize` identifier to search for.
|
||||
|
||||
**Returns:**
|
||||
`Some(Arc<Widget>)` if a widget with the matching ID is found, `None` otherwise.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let id_ext = IdExtension::new(screen.clone());
|
||||
screen.extension(id_ext.clone());
|
||||
|
||||
rsx! {
|
||||
@Id(100);
|
||||
Div { "Hello, ID 100!" }
|
||||
}.draw(&screen);
|
||||
|
||||
// Later, perhaps in an event handler or another part of your app:
|
||||
if let Some(widget_100) = id_ext.get_element(100) {
|
||||
// You can now interact with widget_100 directly, e.g., get its transform, set components etc.
|
||||
// let transform: Transform = widget_100.get().unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
### `Extension` Trait Implementation
|
||||
|
||||
`IdExtension` currently has an empty implementation for the `Extension` trait (`impl Extension for Arc<IdExtension> {}`). This means it doesn't hook into any lifecycle events or event dispatching by default. Its primary purpose is to provide the `get_element` lookup method, which is invoked manually when needed.
|
||||
@@ -0,0 +1,75 @@
|
||||
# `osui::extensions::input_handling`
|
||||
|
||||
The `input_handling` module provides the `InputExtension`, which integrates `crossterm` for low-level terminal input and dispatches these events throughout the OSUI system. This is a fundamental extension required for any interactive OSUI application.
|
||||
|
||||
## `InputExtension`
|
||||
|
||||
Manages raw terminal input and dispatches `crossterm` events.
|
||||
|
||||
```rust
|
||||
pub struct InputExtension;
|
||||
```
|
||||
|
||||
### `Extension` Trait Implementation
|
||||
|
||||
#### `init(&mut self, ctx: &Context)`
|
||||
Called when the extension is initialized.
|
||||
1. Enables `crossterm`'s raw mode (`crossterm::terminal::enable_raw_mode().unwrap()`). Raw mode allows direct, unbuffered input capture, essential for TUI applications.
|
||||
2. Spawns a new thread. This thread continuously listens for `crossterm::event::read()` events.
|
||||
3. Whenever an event is read successfully, it dispatches the event using `ctx.event(&e)`. This makes the `crossterm::event::Event` available to all widgets (via `Element::event` or `Handler<crossterm::event::Event>` components) and other extensions.
|
||||
|
||||
**Why a separate thread?**
|
||||
Reading input from the terminal (`crossterm::event::read()`) is a blocking operation. Spawning it in a separate thread prevents the main rendering loop from freezing while waiting for user input, ensuring the UI remains responsive.
|
||||
|
||||
#### `on_close(&mut self)`
|
||||
Called when `Screen::close()` is invoked.
|
||||
1. Disables `crossterm`'s raw mode (`crossterm::terminal::disable_raw_mode().unwrap()`). This restores the terminal to its normal, buffered input state, which is crucial for a clean exit.
|
||||
|
||||
### `Event` Trait Implementation
|
||||
|
||||
The `crossterm::event::Event` enum itself implements OSUI's `Event` trait, allowing `InputExtension` to dispatch it directly.
|
||||
|
||||
```rust
|
||||
impl crate::extensions::Event for crossterm::event::Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
```
|
||||
This means you can easily listen for `crossterm` events in your widgets or other extensions using `Handler<crossterm::event::Event>` or by downcasting the `dyn Event` in an `Extension::event` method.
|
||||
|
||||
## Usage
|
||||
|
||||
You must register the `InputExtension` with your `Screen` for keyboard and mouse input to work in your OSUI application.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let screen = Screen::new();
|
||||
|
||||
// Register the InputExtension
|
||||
screen.extension(InputExtension);
|
||||
|
||||
// Other extensions and UI setup
|
||||
screen.extension(RelativeFocusExtension::new()); // Often used with InputExtension for navigation
|
||||
|
||||
rsx! {
|
||||
// A global event handler to exit on Escape key, relying on InputExtension
|
||||
@Handler::new({
|
||||
let screen = screen.clone();
|
||||
move |_, e: &crossterm::event::Event| {
|
||||
if let crossterm::event::Event::Key(crossterm::event::KeyEvent { code: crossterm::event::KeyCode::Esc, .. }) = e {
|
||||
screen.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
// An Input widget that will receive key events from InputExtension
|
||||
Input { }
|
||||
}.draw(&screen);
|
||||
|
||||
screen.run()
|
||||
}
|
||||
```
|
||||
`InputExtension` is a foundational component for building interactive terminal user interfaces with OSUI.
|
||||
@@ -0,0 +1,93 @@
|
||||
# `osui::extensions::tick`
|
||||
|
||||
The `tick` module provides the `TickExtension` and `TickEvent`, enabling applications to receive periodic updates at a defined rate. This is useful for animations, game loops, or any time-based logic.
|
||||
|
||||
## `TickEvent` (Event)
|
||||
|
||||
```rust
|
||||
event!(TickEvent(pub u32));
|
||||
```
|
||||
A custom event type dispatched by the `TickExtension`. It contains a `u32` value representing the current tick count since the extension started.
|
||||
|
||||
**Fields:**
|
||||
* `0`: `u32` - The current tick count.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
// Listen for TickEvent
|
||||
rsx! {
|
||||
@Handler::new(|_, e: &TickEvent| {
|
||||
println!("Tick: {}", e.0);
|
||||
});
|
||||
Div { "Listening for ticks" }
|
||||
}
|
||||
```
|
||||
|
||||
## `TickExtension`
|
||||
|
||||
Dispatches `TickEvent`s at a configurable interval.
|
||||
|
||||
```rust
|
||||
pub struct TickExtension(pub u16);
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
* `0`: `u16` - The desired ticks per second (Hz). For example, `TickExtension(30)` would dispatch a `TickEvent` approximately every 33 milliseconds (1000ms / 30Hz).
|
||||
|
||||
### `Extension` Trait Implementation
|
||||
|
||||
#### `init(&mut self, ctx: &Context)`
|
||||
Called when the `TickExtension` is registered with the `Screen`.
|
||||
1. Calculates the `rate_dur` (duration per tick) in milliseconds: `1000 / self.0 as u64`.
|
||||
2. Spawns a new thread.
|
||||
3. In this new thread, it enters an infinite loop:
|
||||
* It dispatches a `TickEvent` with the current tick count using `ctx.event(&TickEvent(tick))`.
|
||||
* The `tick` counter is incremented.
|
||||
* The thread sleeps for the calculated `rate_dur`.
|
||||
|
||||
**Why a separate thread?**
|
||||
Similar to `InputExtension`, `TickExtension` spawns a separate thread because `std::thread::sleep` is a blocking operation. This ensures that the main rendering loop continues to run smoothly, independent of the tick rate.
|
||||
|
||||
## Usage
|
||||
|
||||
To use `TickExtension`, you simply need to create an instance with your desired tick rate and register it with the `Screen`. Then, your widgets or other extensions can listen for `TickEvent`s using `Handler<TickEvent>`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let screen = Screen::new();
|
||||
|
||||
// Register essential extensions
|
||||
screen.extension(InputExtension);
|
||||
screen.extension(RelativeFocusExtension::new());
|
||||
|
||||
// Register TickExtension for 30 ticks per second
|
||||
screen.extension(TickExtension(30));
|
||||
|
||||
// Create a state to display the tick count
|
||||
let tick_count_state = use_state(0u32);
|
||||
|
||||
rsx! {
|
||||
// Attach a Handler to update the state on each TickEvent
|
||||
@Handler::new({
|
||||
let state_clone = tick_count_state.clone();
|
||||
move |_, e: &TickEvent| {
|
||||
state_clone.set(e.0); // Update the state with the current tick count
|
||||
}
|
||||
});
|
||||
// Display the reactive tick count
|
||||
%tick_count_state
|
||||
Div {
|
||||
format!("Current Tick: {}", tick_count_state.get())
|
||||
}
|
||||
}
|
||||
.draw(&screen);
|
||||
|
||||
screen.run()
|
||||
}
|
||||
```
|
||||
This example demonstrates how `TickExtension` provides a consistent timing mechanism, allowing you to build dynamic and animated UIs.
|
||||
@@ -0,0 +1,92 @@
|
||||
# `osui::extensions::velocity`
|
||||
|
||||
The `velocity` module provides the `VelocityExtension` and `Velocity` component, enabling simple animation by automatically moving widgets with a constant velocity.
|
||||
|
||||
## `Velocity` (Component)
|
||||
|
||||
```rust
|
||||
component!(Velocity(pub i32, pub i32));
|
||||
```
|
||||
A tuple struct component that holds two `i32` values, representing the horizontal (`x`) and vertical (`y`) velocity of a widget. The values represent "cells per second" for movement.
|
||||
|
||||
**Fields:**
|
||||
* `0`: `i32` - Horizontal velocity (cells per second). Positive moves right, negative moves left.
|
||||
* `1`: `i32` - Vertical velocity (cells per second). Positive moves down, negative moves up.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Velocity(10, 0); // Move 10 cells right per second
|
||||
@Transform::new().x(0).y(0);
|
||||
Div { "Moving Right" }
|
||||
}
|
||||
```
|
||||
|
||||
## `VelocityExtension`
|
||||
|
||||
This extension is responsible for applying the specified `Velocity` to widgets that also have a `Transform` component, causing them to move across the screen.
|
||||
|
||||
```rust
|
||||
pub struct VelocityExtension;
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `VelocityExtension::apply_velocity(ticks: u16, velocity: i32, x: &mut u16)`
|
||||
An internal helper function that updates a single coordinate (`x` or `y`) based on the elapsed `ticks` and `velocity`. It ensures that movement occurs at the specified `velocity` (cells per second) by checking if enough time has passed since the last movement.
|
||||
|
||||
#### `VelocityExtension::apply_velocity_xy(ticks: u16, widget: &Arc<Widget>)`
|
||||
An internal helper that applies `Velocity` to both `x` and `y` coordinates of a widget's `Transform` component. It retrieves the `Velocity` and `Transform` components, calculates new positions using `apply_velocity`, and then updates the `Transform` component on the widget.
|
||||
|
||||
### `Extension` Trait Implementation
|
||||
|
||||
#### `init(&mut self, ctx: &super::Context)`
|
||||
Called when the extension is initialized.
|
||||
1. Clones the `Context`.
|
||||
2. Spawns a new thread.
|
||||
3. In this thread, it enters an infinite loop:
|
||||
* Initializes a `tick` counter (0 to 1000, then resets). This acts as a granular time counter.
|
||||
* Iterates over all widgets managed by the `Screen` (obtained via `ctx.get_widgets()`).
|
||||
* For each widget, it calls `Self::apply_velocity_xy(tick, widget)` to update its position if it has a `Velocity` and `Transform` component.
|
||||
* The thread sleeps for 1 millisecond. This ensures very frequent checks and smooth potential movement, as `velocity` is defined in "cells per second".
|
||||
|
||||
**Why a separate thread?**
|
||||
Similar to `InputExtension` and `TickExtension`, a separate thread is used because time-based operations like `thread::sleep` are blocking. This prevents the velocity calculations from blocking the main rendering loop and ensures smooth animations. The 1ms sleep provides a high refresh rate for velocity updates.
|
||||
|
||||
## Usage
|
||||
|
||||
To use `VelocityExtension`, you need to:
|
||||
1. Register it with your `Screen`.
|
||||
2. Attach both a `Transform` and a `Velocity` component to the widget you want to animate. The `Transform` should have `Position::Const` for the coordinates you want to animate.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let screen = Screen::new();
|
||||
|
||||
screen.extension(InputExtension); // Required for general terminal operations
|
||||
screen.extension(RelativeFocusExtension::new()); // Optional, but good practice
|
||||
screen.extension(VelocityExtension); // Register the VelocityExtension
|
||||
|
||||
rsx! {
|
||||
// A Div that moves right at 10 cells/second
|
||||
@Velocity(10, 0);
|
||||
@Transform::new().x(0).y(0).dimensions(10, 1); // Must have Const position to be moved
|
||||
@Style { background: Background::Solid(0xFF0000) };
|
||||
Div { "Moving Text" }
|
||||
|
||||
// A Div that moves down at 5 cells/second, starting below the first
|
||||
@Velocity(0, 5);
|
||||
@Transform::new().x(0).y(2).dimensions(10, 1);
|
||||
@Style { background: Background::Solid(0x0000FF) };
|
||||
Div { "Moving Down" }
|
||||
}
|
||||
.draw(&screen);
|
||||
|
||||
screen.run()
|
||||
}
|
||||
```
|
||||
`VelocityExtension` provides a simple, component-based way to add continuous motion to your OSUI elements. For more complex animations, you might combine it with `TickExtension` and custom logic within a widget's `event` method or a more advanced animation extension.
|
||||
@@ -0,0 +1,86 @@
|
||||
# `osui::frontend`
|
||||
|
||||
The `frontend` module defines the internal structures that represent the declarative UI tree parsed by the `rsx!` macro. This tree is then used to construct the actual `Widget` hierarchy managed by the `Screen`.
|
||||
|
||||
## `RsxElement` Enum
|
||||
|
||||
Represents a single node in the RSX tree before it's converted into a `Widget`.
|
||||
|
||||
```rust
|
||||
pub enum RsxElement {
|
||||
/// A static widget with its children.
|
||||
/// Used when the `rsx!` macro detects a `static` element or a string literal without dependencies.
|
||||
Element(StaticWidget, Rsx),
|
||||
|
||||
/// A dynamically generated widget with its dependencies and children.
|
||||
/// Used when the `rsx!` macro detects a `%dependency` or a non-static string literal.
|
||||
DynElement(
|
||||
Box<dyn FnMut() -> WidgetLoad + Send + Sync>,
|
||||
Vec<Box<dyn DependencyHandler>>,
|
||||
Rsx,
|
||||
),
|
||||
}
|
||||
```
|
||||
|
||||
* **`Element(StaticWidget, Rsx)`**: Holds a pre-constructed `StaticWidget` and its child `Rsx` tree. This variant is for UI parts that do not change dynamically.
|
||||
* **`DynElement(Box<dyn FnMut() -> WidgetLoad + Send + Sync>, Vec<Box<dyn DependencyHandler>>, Rsx)`**:
|
||||
* The `Box<dyn FnMut() -> WidgetLoad + Send + Sync>` is a closure that, when executed, will create the `WidgetLoad` for this dynamic widget. This allows deferring the widget's construction until it's actually needed or when it needs to be rebuilt.
|
||||
* `Vec<Box<dyn DependencyHandler>>`: A list of reactive dependencies (like `State<T>`) that, when changed, will trigger this dynamic widget to rebuild itself by re-executing its `FnMut() -> WidgetLoad` closure.
|
||||
* `Rsx`: The child RSX tree for this dynamic widget.
|
||||
|
||||
## `Rsx` Struct
|
||||
|
||||
A container representing a collection (a list or a group) of `RsxElement`s. This is the top-level type generated by the `rsx!` macro.
|
||||
|
||||
```rust
|
||||
pub struct Rsx(pub Vec<RsxElement>);
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Rsx::draw(self, screen: &Arc<Screen>)`
|
||||
Draws the `Rsx` tree onto the given `Screen` as root-level widgets.
|
||||
This is the entry point for rendering the UI defined by an `rsx!` block. It effectively calls `draw_parent` with no parent.
|
||||
|
||||
**Arguments:**
|
||||
* `screen`: An `Arc` to the `Screen` instance.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
rsx! { "Hello, OSUI!" }.draw(&screen);
|
||||
```
|
||||
|
||||
#### `Rsx::draw_parent(self, screen: &Arc<Screen>, parent: Option<Arc<Widget>>)`
|
||||
Recursively draws the `Rsx` tree with an optional parent widget.
|
||||
This method iterates through the `RsxElement`s:
|
||||
* For `DynElement`s, it calls the internal `FnMut()` to create a `WidgetLoad`, then creates an `Arc<Widget::Dynamic>` via `screen.draw_box_dyn`. It registers all the element's dependencies with this new dynamic widget.
|
||||
* For `Element`s (static), it creates an `Arc<Widget::Static>` via `screen.draw_widget`.
|
||||
* If a `parent` `Arc<Widget>` is provided, the newly created child widget is registered with the parent's `Element` via `parent.get_elem().draw_child(&new_widget)`. This is how the parent-child relationships are established in the runtime widget tree.
|
||||
* It then recursively calls `draw_parent` for the child `Rsx` tree, passing the newly created widget as the `parent`.
|
||||
|
||||
**Arguments:**
|
||||
* `screen`: An `Arc` to the `Screen` instance.
|
||||
* `parent`: An `Option<Arc<Widget>>` representing the parent widget. `None` for root widgets.
|
||||
|
||||
#### `Rsx::create_element<F: FnMut() -> WidgetLoad + Send + Sync + 'static>(&mut self, load: F, dependencies: Vec<Box<dyn DependencyHandler>>, children: Rsx)`
|
||||
Adds a dynamically constructed `RsxElement::DynElement` to this `Rsx` container. This method is primarily used internally by the `rsx!` macro.
|
||||
|
||||
**Arguments:**
|
||||
* `load`: A closure that generates the `WidgetLoad` for the dynamic element.
|
||||
* `dependencies`: A `Vec` of boxed `DependencyHandler`s that this element depends on.
|
||||
* `children`: The `Rsx` tree representing the children of this element.
|
||||
|
||||
#### `Rsx::create_element_static(&mut self, element: StaticWidget, children: Rsx)`
|
||||
Adds a statically defined `RsxElement::Element` to this `Rsx` container. This method is primarily used internally by the `rsx!` macro.
|
||||
|
||||
**Arguments:**
|
||||
* `element`: A pre-constructed `StaticWidget`.
|
||||
* `children`: The `Rsx` tree representing the children of this element.
|
||||
|
||||
#### `Rsx::expand(&mut self, other: &mut Rsx)`
|
||||
Appends the elements from another `Rsx` tree into this one. This is used by the `rsx!` macro when handling the `$expand => (...args)` syntax.
|
||||
|
||||
**Arguments:**
|
||||
* `other`: A mutable reference to another `Rsx` container whose elements will be moved into `self`.
|
||||
@@ -0,0 +1,31 @@
|
||||
# API Reference
|
||||
|
||||
This section provides detailed documentation for all public modules, structs, enums, traits, and macros within the OSUI library.
|
||||
|
||||
## Core Concepts & Structure
|
||||
|
||||
* [Screen](../reference/screen.md): The main application context for managing widgets and extensions.
|
||||
* [Widget Model](../reference/widget.md): `Element`, `Component`, `Widget` (Static/Dynamic), and `WidgetLoad`.
|
||||
* [Rendering & Scope](../reference/render-scope.md): `RenderScope`, `RenderMethod`, and `ElementRenderer`.
|
||||
* [State Management](../reference/state.md): `State<T>` and `DependencyHandler` for reactivity.
|
||||
* [Frontend & Macros](../reference/frontend.md): `RsxElement`, `Rsx`, and the `event!`, `component!`, `transform!`, `rsx!` macros.
|
||||
* [Utilities](../reference/utils.md): Helper functions for terminal control and string manipulation.
|
||||
|
||||
## Built-in Components & Extensions
|
||||
|
||||
* [Style & Layout](../reference/style.md): `Transform`, `Position`, `Dimension`, `Style`, `Background`.
|
||||
* [Extensions Overview](../reference/extensions.md): The `Extension` trait, `Event` trait, and `Context` for global behaviors.
|
||||
* [Focus Extension](../reference/extensions/focus.md): `AlwaysFocused`, `Focused`, `RelativeFocusExtension` for keyboard navigation.
|
||||
* [ID Extension](../reference/extensions/id.md): `IdExtension`, `Id` for unique widget identification.
|
||||
* [Input Handling Extension](../reference/extensions/input_handling.md): `InputExtension` for keyboard and mouse input.
|
||||
* [Tick Extension](../reference/extensions/tick.md): `TickExtension`, `TickEvent` for timed events.
|
||||
* [Velocity Extension](../reference/extensions/velocity.md): `VelocityExtension`, `Velocity` for simple animations.
|
||||
|
||||
## Built-in UI Elements
|
||||
|
||||
* [Elements Overview](../reference/elements.md): General Element trait and String as an Element.
|
||||
* [Div](../reference/elements/div.md): A generic container element.
|
||||
* [Flex Containers](../reference/elements/flex.md): `FlexRow` and `FlexCol` for automatic horizontal/vertical layout.
|
||||
* [Heading](../reference/elements/heading.md): Renders large ASCII art text.
|
||||
* [Input](../reference/elements/input.md): An interactive text input field.
|
||||
* [Paginator](../reference/elements/paginator.md): Manages and navigates between multiple pages/children.
|
||||
@@ -0,0 +1,198 @@
|
||||
# Macros
|
||||
|
||||
OSUI provides several declarative macros to simplify common tasks like defining custom events, components, `Transform`s, and UI trees.
|
||||
|
||||
## `event!` Macro
|
||||
|
||||
Declares a struct that implements the `Event` trait, simplifying event type creation for OSUI's reactive system.
|
||||
|
||||
### Variants
|
||||
|
||||
* **`event!(Name)`**: Defines a unit struct named `Name`.
|
||||
* **`event!(Name { ... })`**: Defines a named struct with fields.
|
||||
* **`event!(Name (...))`**: Defines a tuple struct.
|
||||
|
||||
### Usage
|
||||
|
||||
```rust
|
||||
use osui::macros::event; // Import the macro
|
||||
|
||||
// Unit struct event
|
||||
event!(Clicked);
|
||||
|
||||
// Named struct event with fields
|
||||
event!(Resized { width: u32, height: u32 });
|
||||
|
||||
// Tuple struct event
|
||||
event!(Moved(u32, u32));
|
||||
|
||||
// Example usage
|
||||
fn main() {
|
||||
let click_event = Clicked;
|
||||
let resize_event = Resized { width: 80, height: 24 };
|
||||
let move_event = Moved(10, 20);
|
||||
|
||||
// Events can be dispatched and handled
|
||||
// (requires an OSUI screen and event context)
|
||||
}
|
||||
```
|
||||
|
||||
## `component!` Macro
|
||||
|
||||
Declares a struct that implements the `Component` trait, reducing boilerplate when defining new data or behavior extensions for widgets.
|
||||
|
||||
### Variants
|
||||
|
||||
* **`component!(Name)`**: Defines a unit struct.
|
||||
* **`component!(Name { ... })`**: Defines a named struct with fields.
|
||||
* **`component!(Name (...))`**: Defines a tuple struct.
|
||||
|
||||
### Usage
|
||||
|
||||
```rust
|
||||
use osui::macros::component; // Import the macro
|
||||
|
||||
// Unit struct component (e.g., a marker for a property)
|
||||
component!(Focusable);
|
||||
|
||||
// Named struct component (e.g., a tooltip message)
|
||||
component!(Tooltip { text: String });
|
||||
|
||||
// Tuple struct component (e.g., a fixed size)
|
||||
component!(Size(u32, u32));
|
||||
|
||||
// Example usage (assuming a widget instance `my_widget`)
|
||||
// my_widget.component(Focusable);
|
||||
// my_widget.component(Tooltip { text: "Hello".to_string() });
|
||||
// my_widget.component(Size(100, 50));
|
||||
```
|
||||
|
||||
## `event_handler!` Macro
|
||||
|
||||
Creates an event handler closure that safely calls a method on a `self` instance. This is particularly useful when you need to register a `'static` event handler that interacts with the current object, often requiring unsafe raw pointers.
|
||||
|
||||
### Arguments
|
||||
|
||||
* `$self_ty`: The type of `self` (e.g., `Self` or a specific struct name).
|
||||
* `$self`: The instance variable (usually `self`) being used.
|
||||
* `$events`: The event source object, which must have an `.on` method (e.g., an `EventHandler` wrapper that allows registering closures). This part is not shown in the source, but it implies such an interface.
|
||||
* `$method`: The method on `$self` to call when an event is received.
|
||||
|
||||
### Safety
|
||||
|
||||
This macro uses `unsafe` code to cast `self` to a raw pointer and dereference it. It is the developer's responsibility to ensure that the `self` reference remains valid for the entire lifetime of the generated closure. Incorrect use can lead to use-after-free or other memory safety issues. Use with extreme caution and only when necessary for `'static` lifetimes.
|
||||
|
||||
### Usage (Conceptual Example)
|
||||
|
||||
```rust
|
||||
use osui::prelude::*; // Assuming event_handler! is in prelude or imported
|
||||
|
||||
struct MyComponent {
|
||||
// ... fields
|
||||
}
|
||||
|
||||
impl MyComponent {
|
||||
// A method to be called by the event handler
|
||||
fn handle_click(&mut self, _widget: &Arc<Widget>, _event: &Clicked) {
|
||||
println!("MyComponent was clicked!");
|
||||
}
|
||||
|
||||
fn setup_event_listener(self: &Arc<Self>, some_event_source: &Arc<Widget>) {
|
||||
// You'd attach a Handler component to `some_event_source`
|
||||
// which then triggers `self.handle_click`
|
||||
some_event_source.set_component(Handler::new({
|
||||
// This is a conceptual expansion of what event_handler! might do
|
||||
let self_ref = Arc::downgrade(self); // Weak reference for safety
|
||||
move |widget_arc, event: &Clicked| {
|
||||
if let Some(strong_self) = self_ref.upgrade() {
|
||||
// Safe dereference if the component still exists
|
||||
let mut_self = Arc::get_mut(&mut strong_self).unwrap(); // Requires Arc to be unique
|
||||
mut_self.handle_click(widget_arc, event);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
**Note**: The provided `event_handler!` macro in the source code directly converts `self` to a raw pointer. The example above shows a safer, `Arc`-based approach that's more common in modern Rust GUI frameworks for `'static` closures where the lifetime of `self` is not guaranteed. OSUI's `Handler` component itself simplifies common patterns, often making direct use of `event_handler!` macro unnecessary unless you are working with bare `&mut self` on objects whose lifetime is strictly controlled.
|
||||
|
||||
## `transform!` Macro
|
||||
|
||||
A convenient macro for constructing a `Transform` component with specified properties. It simplifies the setup compared to using `Transform::new()` followed by multiple fluent method calls.
|
||||
|
||||
### Arguments
|
||||
|
||||
Takes comma-separated key-value pairs where the key is a public field of `Transform` and the value is an expression that can be converted into the field's type (e.g., `u16` for `Position::Const`, `Dimension::Const`).
|
||||
|
||||
### Usage
|
||||
|
||||
```rust
|
||||
use osui::prelude::*; // Imports Transform, Position, Dimension
|
||||
|
||||
rsx! {
|
||||
// Sets x: Const(10), y: Center, width: Full, height: Const(5), px: 1
|
||||
@transform!(x: 10, y: Center, width: Full, height: 5, px: 1);
|
||||
Div { "Transformed Div" }
|
||||
|
||||
// Minimal transform, only setting width
|
||||
@transform!(width: 20);
|
||||
Div { "Width 20" }
|
||||
}
|
||||
```
|
||||
This macro makes it very concise to apply layout rules directly within your `rsx!` syntax.
|
||||
|
||||
## `rsx!` Macro
|
||||
|
||||
The primary macro for declaratively defining your OSUI user interface. It provides a JSX-like syntax for nesting elements, attaching components, and specifying dependencies.
|
||||
|
||||
### Arguments
|
||||
|
||||
Takes a sequence of UI elements, potentially nested within curly braces `{}`.
|
||||
|
||||
### Features
|
||||
|
||||
* **Text Literals**: Direct strings (e.g., `"Hello"`) become text elements.
|
||||
* **Element Tags**: Element struct names (e.g., `Div`, `Input`) followed by properties and children.
|
||||
* **Properties**: `field: value` pairs set element properties (e.g., `Heading, smooth: true,`).
|
||||
* **Children**: Elements nested within `{}` are children of the parent.
|
||||
* **Dependencies**: `%variable_name` registers `variable_name` (must implement `DependencyHandler`) as a dependency for a dynamic widget.
|
||||
* **Components**: `@ComponentType` or `@ComponentType::new(args)` attaches a component to the element.
|
||||
* **Static Elements**: `static ElementType { ... }` creates a `StaticWidget` (no reactivity overhead).
|
||||
* **Expansion**: `$another_macro => (args)` allows embedding UI generated by other macros/functions.
|
||||
|
||||
### Usage
|
||||
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
let my_state_var = use_state("Initial".to_string());
|
||||
|
||||
rsx! {
|
||||
// Simple text
|
||||
"Welcome to OSUI!"
|
||||
|
||||
// Static Div with styling
|
||||
@Transform::new().dimensions(20, 5);
|
||||
@Style { background: Background::Solid(0x333333), foreground: Some(0xFFFFFF) };
|
||||
static Div { "This is a static box." }
|
||||
|
||||
// Dynamic Div reacting to `my_state_var`
|
||||
%my_state_var
|
||||
Div {
|
||||
format!("Current state: {}", my_state_var.get())
|
||||
}
|
||||
|
||||
// FlexRow with Heading and Input
|
||||
FlexRow, gap: 2, {
|
||||
Heading { "User Input" }
|
||||
@Transform::new().dimensions(30, 1);
|
||||
@Style { background: Background::Outline(0xAAAAAA) };
|
||||
@Focused;
|
||||
Input { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `rsx_inner!` Macro
|
||||
|
||||
An internal, recursive macro used by `rsx!` to parse and build the UI tree. **This macro is not intended for direct use by developers.** Its definition shows the complex pattern matching used to process the various `rsx!` syntax forms.
|
||||
@@ -0,0 +1,150 @@
|
||||
# `osui::render_scope`
|
||||
|
||||
The `render_scope` module defines the `RenderScope` struct, which is central to OSUI's drawing and layout process. It acts as a context for rendering operations, handling transformations, parent-child dimensions, and collecting drawing instructions before they are flushed to the terminal.
|
||||
|
||||
## `ElementRenderer` Trait
|
||||
|
||||
A trait that can be implemented by custom renderers, allowing them to hook into the drawing process right before `RenderScope::draw` is called. Used internally by container elements like `Div` and `Flex`.
|
||||
|
||||
```rust
|
||||
pub trait ElementRenderer {
|
||||
/// Called right after the `after_render` function is called for a widget,
|
||||
/// just before the `RenderScope::draw` method is invoked.
|
||||
#[allow(unused)]
|
||||
fn before_draw(&mut self, scope: &mut RenderScope, widget: &Arc<Widget>) {}
|
||||
}
|
||||
```
|
||||
|
||||
## `RenderMethod` Enum
|
||||
|
||||
An internal enum representing a single primitive draw instruction. These methods are accumulated in `RenderScope`'s `render_stack`.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
enum RenderMethod {
|
||||
/// Plain text rendering at current transform. (x, y, text)
|
||||
Text(u16, u16, String),
|
||||
/// Plain text rendering at current transform with foreground/background swapped. (x, y, text)
|
||||
TextInverted(u16, u16, String),
|
||||
/// Text rendered with a specific 24-bit color. (x, y, text, color)
|
||||
TextColored(u16, u16, String, u32),
|
||||
/// A filled rectangle of a given size and background color. (x, y, width, height, color)
|
||||
Rectangle(u16, u16, u16, u16, u32),
|
||||
}
|
||||
```
|
||||
|
||||
## `RenderScope`
|
||||
|
||||
`RenderScope` is the primary context object passed around during the rendering phase. It contains mutable state for the current widget's layout, style, and accumulated draw commands.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct RenderScope {
|
||||
transform: RawTransform, // Resolved layout (position, dimensions, padding)
|
||||
render_stack: Vec<RenderMethod>, // Stack of drawing instructions
|
||||
parent_width: u16, // Width of the parent container
|
||||
parent_height: u16, // Height of the parent container
|
||||
style: Style, // Current style applied to this scope
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `RenderScope::new() -> RenderScope`
|
||||
Creates a new, empty `RenderScope` with default `RawTransform` and `Style`.
|
||||
|
||||
#### `RenderScope::set_transform_raw(&mut self, transform: RawTransform)`
|
||||
Directly sets the internal `RawTransform` for this scope. This is usually managed internally or by `ElementRenderer`s.
|
||||
|
||||
#### `RenderScope::set_transform(&mut self, transform: &Transform)`
|
||||
Applies a high-level `Transform` configuration to this scope. This method resolves the `Position` and `Dimension` rules into the concrete `RawTransform` based on the `parent_width` and `parent_height` of the scope. It also applies padding (`px`, `py`).
|
||||
|
||||
#### `RenderScope::draw_text(&mut self, x: u16, y: u16, text: &str)`
|
||||
Adds a plain text draw instruction to the `render_stack`.
|
||||
The `(x, y)` coordinates are relative to the current `RenderScope`'s top-left corner (after its `Transform` is applied and considering padding). This method also updates the `RenderScope`'s `RawTransform` width and height if the drawn text exceeds the current content size, which is important for `Dimension::Content`.
|
||||
|
||||
#### `RenderScope::draw_text_inverted(&mut self, x: u16, y: u16, text: &str)`
|
||||
Adds a text draw instruction where the foreground and background colors are swapped (inverted).
|
||||
|
||||
#### `RenderScope::draw_text_colored(&mut self, x: u16, y: u16, text: &str, color: u32)`
|
||||
Adds a text draw instruction with a specific 24-bit RGB foreground `color`.
|
||||
|
||||
#### `RenderScope::draw_rect(&mut self, x: u16, y: u16, width: u16, height: u16, color: u32)`
|
||||
Adds a filled rectangle draw instruction. The `(x, y)` coordinates are relative to the current `RenderScope`'s top-left corner. This method also updates the `RenderScope`'s `RawTransform` width and height if the rectangle exceeds the current content size.
|
||||
|
||||
#### `RenderScope::use_area(&mut self, width: u16, height: u16)`
|
||||
Manually ensures that the `RenderScope`'s `RawTransform` has at least the specified `width` and `height`. This is useful for elements with `Dimension::Content` that might not draw explicit text or rectangles but still need to claim space.
|
||||
|
||||
#### `RenderScope::draw(&self)`
|
||||
Flushes all accumulated `RenderMethod` instructions in the `render_stack` to the actual terminal. This method also draws any background defined by the `Style` component (e.g., solid fill, outline) *before* the text/rectangle commands.
|
||||
|
||||
#### `RenderScope::clear(&mut self)`
|
||||
Clears all accumulated render instructions and resets the internal `RawTransform` and `Style` to defaults. This is called at the beginning of rendering each new widget.
|
||||
|
||||
#### `RenderScope::get_size(&self) -> (u16, u16)`
|
||||
Returns the current width and height of the `RenderScope`'s internal `RawTransform`. This represents the *calculated* size of the widget's content area.
|
||||
|
||||
#### `RenderScope::get_size_or(&self, width: u16, height: u16) -> (u16, u16)`
|
||||
Returns the current size. If the current width or height is `0`, it defaults to the provided `width` or `height` respectively.
|
||||
|
||||
#### `RenderScope::get_size_or_parent(&self) -> (u16, u16)`
|
||||
Returns the current size. If the current width or height is `0`, it defaults to the `parent_width` or `parent_height` respectively.
|
||||
|
||||
#### `RenderScope::get_parent_size(&self) -> (u16, u16)`
|
||||
Returns the width and height of the parent container that this `RenderScope` is operating within. This is crucial for `Dimension::Full` and `Position::Center`/`End` calculations.
|
||||
|
||||
#### `RenderScope::set_parent_size(&mut self, width: u16, height: u16)`
|
||||
Sets the dimensions of the parent container for this `RenderScope`. Container elements (like `Div`, `FlexRow`) use this to define the available space for their children.
|
||||
|
||||
#### `RenderScope::get_transform_mut(&mut self) -> &mut RawTransform`
|
||||
Returns a mutable reference to the internal `RawTransform`. Use with caution.
|
||||
|
||||
#### `RenderScope::get_transform(&self) -> &RawTransform`
|
||||
Returns an immutable reference to the internal `RawTransform`.
|
||||
|
||||
#### `RenderScope::set_style(&mut self, style: Style)`
|
||||
Sets the `Style` for the current render scope. This style will apply to any `draw_text` or background drawing operations within this scope.
|
||||
|
||||
#### `RenderScope::get_style(&mut self) -> &mut Style`
|
||||
Returns a mutable reference to the `Style` currently applied to this scope.
|
||||
|
||||
#### `RenderScope::render_widget(&mut self, renderer: &mut dyn ElementRenderer, ctx: &crate::extensions::Context, widget: &std::sync::Arc<crate::widget::Widget>) -> bool`
|
||||
This is the central function for rendering a single widget and its associated process. It performs:
|
||||
1. Clears the `RenderScope`.
|
||||
2. Applies `Style` and `Transform` components to the `RenderScope`.
|
||||
3. Calls the widget's `Element::render` method.
|
||||
4. Calls `Context::render` (extension hook `render_widget`).
|
||||
5. Re-applies `Transform` (for final position based on calculated size).
|
||||
6. Calls `ElementRenderer::before_draw` (for parent-controlled child positioning).
|
||||
7. Calls `RenderScope::draw` to flush commands to terminal.
|
||||
8. Calls the widget's `Element::after_render` method.
|
||||
9. Calls `Context::after_render` (extension hook `after_render_widget`).
|
||||
10. Calls `widget.auto_refresh()` for dynamic widgets.
|
||||
|
||||
**Returns:**
|
||||
`true` if the widget was rendered, `false` if it had a `NoRender` component.
|
||||
|
||||
## `RenderContext`
|
||||
|
||||
A wrapper around the global `Context` that also indicates whether the currently rendering widget is focused.
|
||||
|
||||
```rust
|
||||
pub struct RenderContext(Context, bool);
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `RenderContext::new(c: &Context, focused: bool) -> Self`
|
||||
Creates a new `RenderContext`.
|
||||
|
||||
#### `RenderContext::is_focused(&self) -> bool`
|
||||
Returns `true` if the widget associated with this `RenderContext` is currently focused. Elements often use this to draw a focus indicator.
|
||||
|
||||
#### `RenderContext::render(&self, w: &Arc<Widget>, scope: &mut RenderScope)`
|
||||
Delegates to `Context::render`, allowing the widget to trigger extension `render_widget` hooks.
|
||||
|
||||
#### `RenderContext::after_render(&self, w: &Arc<Widget>, scope: &mut RenderScope)`
|
||||
Delegates to `Context::after_render`, allowing the widget to trigger extension `after_render_widget` hooks.
|
||||
|
||||
#### `RenderContext::get_context(&self) -> &Context`
|
||||
Returns an immutable reference to the underlying global `Context`.
|
||||
@@ -0,0 +1,182 @@
|
||||
# `osui::Screen`
|
||||
|
||||
The `Screen` struct is the central orchestrator of an OSUI application. It manages the UI tree (widgets), registers extensions, and runs the main rendering and event loop. It's the primary entry point for setting up and running your terminal user interface.
|
||||
|
||||
## Struct Definition
|
||||
|
||||
```rust
|
||||
pub struct Screen {
|
||||
pub widgets: Mutex<Vec<Arc<Widget>>>,
|
||||
extensions: Mutex<Vec<Arc<Mutex<Box<dyn Extension + Send + Sync>>>>>,
|
||||
running: Mutex<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
* **`widgets`**: A `Mutex` protecting a `Vec` of `Arc<Widget>`. This holds the top-level widgets that `Screen` is responsible for rendering.
|
||||
* **`extensions`**: A `Mutex` protecting a `Vec` of registered `Extension` implementations. Extensions provide global behaviors and hooks into the rendering and event pipeline.
|
||||
* **`running`**: A `Mutex<bool>` flag controlling the main event loop's execution.
|
||||
|
||||
## Associated Items
|
||||
|
||||
### Methods
|
||||
|
||||
#### `Screen::new() -> Arc<Self>`
|
||||
Creates a new `Screen` instance wrapped in an `Arc`.
|
||||
It's recommended to always create the `Screen` this way, as its `Arc` can then be easily cloned and passed to extensions or other parts of your application without moving ownership.
|
||||
|
||||
**Example:**
|
||||
```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 to the screen and returns its `Arc<Widget>` handle.
|
||||
This is a convenience method that wraps the provided `Element` into a `StaticWidget`.
|
||||
|
||||
**Arguments:**
|
||||
* `element`: An instance of a type that implements the `Element` trait.
|
||||
|
||||
**Returns:**
|
||||
An `Arc<Widget>` representing the newly drawn static widget.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let my_text_widget = screen.draw("Hello, Static World!");
|
||||
```
|
||||
|
||||
#### `Screen::draw_box(self: &Arc<Self>, element: BoxedElement) -> Arc<Widget>`
|
||||
Draws a boxed `Element` to the screen and returns its `Arc<Widget>` handle.
|
||||
Similar to `draw`, but takes a `BoxedElement` directly.
|
||||
|
||||
**Arguments:**
|
||||
* `element`: A `Box<dyn Element + Send + Sync>`.
|
||||
|
||||
**Returns:**
|
||||
An `Arc<Widget>` representing the newly drawn static widget.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let my_div_widget = screen.draw_box(Box::new(Div::new()));
|
||||
```
|
||||
|
||||
#### `Screen::draw_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(self: &Arc<Self>, element: F) -> Arc<Widget>`
|
||||
Draws a dynamic element (reactive widget) to the screen, built from a closure, and returns its `Arc<Widget>` handle.
|
||||
This method creates a `DynWidget` that can re-evaluate its content based on dependencies.
|
||||
|
||||
**Arguments:**
|
||||
* `element`: A closure that, when called, returns a `WidgetLoad`. This closure defines how the dynamic widget's content is generated.
|
||||
|
||||
**Returns:**
|
||||
An `Arc<Widget>` representing the newly drawn dynamic widget.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
let counter_state = use_state(0);
|
||||
let dynamic_text_widget = screen.draw_dyn({
|
||||
let counter_clone = counter_state.clone();
|
||||
move || {
|
||||
WidgetLoad::new(format!("Count: {}", counter_clone.get()))
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### `Screen::draw_box_dyn(self: &Arc<Self>, element: Box<dyn FnMut() -> WidgetLoad + Send + Sync>) -> Arc<Widget>`
|
||||
Draws a dynamic element from a boxed closure and returns its `Arc<Widget>` handle.
|
||||
Similar to `draw_dyn`, but takes a boxed closure directly.
|
||||
|
||||
**Arguments:**
|
||||
* `element`: A `Box<dyn FnMut() -> WidgetLoad + Send + Sync>`.
|
||||
|
||||
**Returns:**
|
||||
An `Arc<Widget>` representing the newly drawn dynamic widget.
|
||||
|
||||
#### `Screen::draw_widget(self: &Arc<Self>, widget: Arc<Widget>)`
|
||||
Adds an existing `Arc<Widget>` to the screen's managed widget list.
|
||||
This is used internally by `draw` and `draw_dyn` but can be called directly if you're constructing `Arc<Widget>` instances manually.
|
||||
|
||||
**Arguments:**
|
||||
* `widget`: The `Arc<Widget>` to add.
|
||||
|
||||
**Notes:**
|
||||
* The first widget added to the screen via any `draw` method or `draw_widget` will automatically be set as `focused`.
|
||||
|
||||
#### `Screen::extension<E: Extension + Send + Sync + 'static>(self: &Arc<Self>, ext: E)`
|
||||
Registers an extension with the screen.
|
||||
Extensions provide global hooks for lifecycle events, rendering, and event handling. They are initialized once and remain active for the screen's lifetime.
|
||||
|
||||
**Arguments:**
|
||||
* `ext`: An instance of a type that implements the `Extension` trait.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
screen.extension(InputExtension); // Register the input handling extension
|
||||
screen.extension(RelativeFocusExtension::new()); // Register the focus navigation extension
|
||||
```
|
||||
|
||||
#### `Screen::run(self: &Arc<Self>) -> std::io::Result<()>`
|
||||
Starts the main rendering and event loop.
|
||||
This method blocks the current thread and continuously renders the UI, processes events, and updates dynamic widgets until `screen.close()` is called. It also performs initial setup and final cleanup of the terminal.
|
||||
|
||||
**Returns:**
|
||||
A `std::io::Result<()>` indicating success or failure of terminal operations.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let screen = Screen::new();
|
||||
// ... draw widgets, register extensions ...
|
||||
screen.run()?; // Start the loop
|
||||
```
|
||||
|
||||
#### `Screen::render(self: &Arc<Self>, ctx: &Context) -> std::io::Result<()>`
|
||||
Renders all widgets and applies extensions for a single frame.
|
||||
This method is called internally by `Screen::run` and should generally not be called directly by users.
|
||||
|
||||
**Arguments:**
|
||||
* `ctx`: A `Context` object providing access to screen functionalities.
|
||||
|
||||
#### `Screen::close(self: &Arc<Self>)`
|
||||
Closes the main event loop and performs cleanup.
|
||||
This method sets the internal `running` flag to `false`, causing the `Screen::run` loop to terminate. It also calls `on_close` for all registered extensions and restores the terminal cursor and screen state.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
// Inside an event handler or another thread:
|
||||
// screen.close();
|
||||
```
|
||||
|
||||
## Related Components
|
||||
|
||||
### `NoRender` (Component)
|
||||
```rust
|
||||
component!(NoRender);
|
||||
```
|
||||
When attached to a widget, this component indicates that the widget should *not* be directly rendered by the `Screen`'s main loop. This is commonly used for child widgets managed and rendered by their parent "ghost" elements (e.g., `Div`, `FlexRow`, `Paginator`). The parent element takes responsibility for rendering these children within its own `after_render` phase.
|
||||
|
||||
### `NoRenderRoot` (Component)
|
||||
```rust
|
||||
component!(NoRenderRoot);
|
||||
```
|
||||
Similar to `NoRender`, but specifically used to prevent a widget from being rendered by the *root* `Screen` renderer. It is typically injected by parent elements (like `Div`) onto their children, indicating that the child's rendering is handled by the parent's `after_render` and thus shouldn't be processed by the main `Screen` loop. This avoids double-rendering or incorrect layout calculations at the root level.
|
||||
|
||||
## `RenderWrapperEvent` (Event)
|
||||
|
||||
```rust
|
||||
event!(RenderWrapperEvent(*mut RenderScope));
|
||||
```
|
||||
A special internal event type used to pass a mutable reference to a `RenderScope` during rendering.
|
||||
It's primarily used by `Handler<RenderWrapperEvent>` components to allow extensions or custom logic to directly manipulate the `RenderScope` for a widget *before* its `Element::render` method is called.
|
||||
|
||||
**Methods:**
|
||||
* `get_scope(&self) -> &mut RenderScope`: Returns a mutable reference to the underlying `RenderScope`.
|
||||
* **Safety**: The caller must ensure the pointer is valid for the lifetime of the event. This is generally handled internally by OSUI.
|
||||
@@ -0,0 +1,146 @@
|
||||
# `osui::state`
|
||||
|
||||
The `state` module provides OSUI's built-in reactivity system, allowing widgets to automatically update when their associated data changes. This is achieved through the `State<T>` struct and the `DependencyHandler` trait.
|
||||
|
||||
## `DependencyHandler` Trait
|
||||
|
||||
A trait for types that can signal changes, triggering reactive updates in `DynWidget`s. `State<T>` is the primary implementer.
|
||||
|
||||
```rust
|
||||
pub trait DependencyHandler: std::fmt::Debug + Send + Sync {
|
||||
/// Called when a dependent widget registers itself with this dependency.
|
||||
/// This typically increments an internal counter of dependents.
|
||||
fn add(&self);
|
||||
|
||||
/// Returns `true` if the state has changed since the last `check()`.
|
||||
/// If `true`, it typically "consumes" one change notification.
|
||||
fn check(&self) -> bool;
|
||||
}
|
||||
```
|
||||
|
||||
## `State<T>`
|
||||
|
||||
`State<T>` is a reactive data container. It wraps a value of type `T` and provides methods to get/set the value and signal changes to dependent widgets.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State<T> {
|
||||
inner: Arc<Mutex<Inner<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Inner<T> {
|
||||
value: T,
|
||||
dependencies: usize, // Count of widgets depending on this state
|
||||
changed: usize, // Count of pending changes to be processed by dependents
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Functions
|
||||
|
||||
#### `use_state<T>(v: T) -> State<T>`
|
||||
Creates a new `State<T>` instance, initialized with the provided value `v`.
|
||||
This is the recommended way to create reactive state variables.
|
||||
|
||||
**Arguments:**
|
||||
* `v`: The initial value for the state.
|
||||
|
||||
**Returns:**
|
||||
A new `State<T>` instance.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_counter = use_state(0);
|
||||
let my_text = use_state(String::from("Initial Text"));
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `State::get_dl(&self) -> T`
|
||||
Returns a cloned copy of the inner value `T`.
|
||||
This method is recommended for preventing potential deadlocks if you only need to read the value and don't need to hold a lock for extended periods. It does *not* mark the state as changed.
|
||||
|
||||
**Returns:**
|
||||
A `T` (clone of the inner value).
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_state = use_state(42);
|
||||
let value = my_state.get_dl(); // value is 42
|
||||
```
|
||||
|
||||
#### `State::get(&self) -> MutexGuard<'_, Inner<T>>`
|
||||
Acquires a `MutexGuard` for the inner `Inner<T>` struct, providing mutable (or immutable) access to the `value` within `Inner<T>`.
|
||||
This method *will* block if another thread or part of the application is currently holding the lock.
|
||||
|
||||
**Returns:**
|
||||
A `MutexGuard` that dereferences to `Inner<T>`. Since `Inner<T>` implements `Deref` and `DerefMut` for `T`, you can often treat `state.get()` as a direct reference to `T`.
|
||||
|
||||
**Example (modifying value and marking as changed):**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_state = use_state(0);
|
||||
{
|
||||
let mut inner_guard = my_state.get(); // Acquire lock
|
||||
*inner_guard += 1; // Modify value using DerefMut; automatically marks as changed
|
||||
} // Lock is released here
|
||||
```
|
||||
|
||||
#### `State::set(&self, v: T)`
|
||||
Sets the inner value of the state to `v` and explicitly marks it as changed.
|
||||
This is an alternative to acquiring a `get()` lock and reassigning.
|
||||
|
||||
**Arguments:**
|
||||
* `v`: The new value for the state.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let my_state = use_state("hello".to_string());
|
||||
my_state.set("world".to_string()); // State is updated and marked changed
|
||||
```
|
||||
|
||||
#### `State::update(&self)`
|
||||
Explicitly marks the state as updated without changing its value.
|
||||
This is useful if you've modified the inner `T` through a method that doesn't trigger `DerefMut` on `Inner<T>` (e.g., if `T` is a complex mutable struct and you called a method on it while holding the `MutexGuard` without reassigning the `T` itself).
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
#[derive(Debug, Clone)]
|
||||
struct MyData { count: i32 }
|
||||
let my_data_state = use_state(MyData { count: 0 });
|
||||
{
|
||||
let mut data_guard = my_data_state.get();
|
||||
data_guard.count += 1; // Directly modify field within the guard
|
||||
}
|
||||
my_data_state.update(); // Manually signal that the state has changed
|
||||
```
|
||||
|
||||
### Implementations
|
||||
|
||||
#### `impl<T: Debug + Send + Sync> DependencyHandler for State<T>`
|
||||
`State<T>` implements `DependencyHandler`.
|
||||
* `add()`: Increments `inner.dependencies`.
|
||||
* `check()`: Returns `true` if `inner.changed > 0`, then decrements `inner.changed`. This ensures each dependent consumes one change notification.
|
||||
|
||||
#### `impl<T: Display> Display for State<T>`
|
||||
`State<T>` implements `Display`, allowing it to be formatted directly (e.g., in `format!` strings or debug output), by displaying its inner `value`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let count = use_state(10);
|
||||
println!("Current count: {}", count); // Output: "Current count: 10"
|
||||
```
|
||||
|
||||
#### `impl<T> Deref for Inner<T>`
|
||||
Allows immutable dereferencing of `Inner<T>` to `T`.
|
||||
This means `state.get().value` can be simply `*state.get()`.
|
||||
|
||||
#### `impl<T> DerefMut for Inner<T>`
|
||||
Allows mutable dereferencing of `Inner<T>` to `T`.
|
||||
Crucially, when this is used, the `changed` counter within `Inner<T>` is set to `dependencies`, marking the state as changed for all its dependents.
|
||||
This means `*state.get() = new_value;` will trigger the change notification.
|
||||
@@ -0,0 +1,279 @@
|
||||
# `osui::style`
|
||||
|
||||
The `style` module defines the structures and enums used to control the visual appearance and layout of OSUI widgets. It provides a declarative way to specify positions, dimensions, and backgrounds.
|
||||
|
||||
## `RawTransform`
|
||||
|
||||
`RawTransform` holds the concrete, resolved layout information for a widget. These values are absolute coordinates and sizes in terminal cells, derived after all layout calculations have been performed. Developers typically interact with `Transform` rather than `RawTransform` directly.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawTransform {
|
||||
pub x: u16, // Absolute X-coordinate (column) of the widget's top-left corner.
|
||||
pub y: u16, // Absolute Y-coordinate (row) of the widget's top-left corner.
|
||||
pub width: u16, // Resolved width of the widget in cells.
|
||||
pub height: u16, // Resolved height of the widget in cells.
|
||||
pub px: u16, // Resolved horizontal padding, derived from Transform.
|
||||
pub py: u16, // Resolved vertical padding, derived from Transform.
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `RawTransform::new() -> RawTransform`
|
||||
Creates a new `RawTransform` instance with all fields set to `0`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::style::RawTransform;
|
||||
let raw_t = RawTransform::new(); // x:0, y:0, width:0, height:0, px:0, py:0
|
||||
```
|
||||
|
||||
## `Position`
|
||||
|
||||
`Position` defines how a widget is placed horizontally or vertically relative to its parent container.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Position {
|
||||
/// Fixed position in cells from the origin (top/left).
|
||||
Const(u16),
|
||||
/// Centered in the parent's available space.
|
||||
Center,
|
||||
/// Aligned to the end (right for x, bottom for y) of the parent.
|
||||
End,
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Position::use_position(&self, size: u16, parent: u16, m: i32, r: &mut u16)`
|
||||
Applies a `Position` rule to determine a final coordinate.
|
||||
This method is used internally by `Transform::use_position` to resolve the `x` or `y` coordinate based on the widget's own size, its parent's available size, and any margin.
|
||||
|
||||
**Arguments:**
|
||||
* `size`: The widget's own resolved dimension (width for `x`, height for `y`).
|
||||
* `parent`: The parent's available dimension (parent width for `x`, parent height for `y`).
|
||||
* `m`: The margin value (mx for `x`, my for `y`).
|
||||
* `r`: A mutable reference to the `u16` where the resolved coordinate should be stored.
|
||||
|
||||
### Implementations
|
||||
|
||||
#### `impl From<u16> for Position`
|
||||
Allows `u16` values to be implicitly converted to `Position::Const(value)`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::style::Position;
|
||||
let pos_x: Position = 10; // Equivalent to Position::Const(10)
|
||||
```
|
||||
|
||||
## `Dimension`
|
||||
|
||||
`Dimension` defines the sizing rule for a widget's width or height.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Dimension {
|
||||
/// Fills the available space from the parent.
|
||||
Full,
|
||||
/// Automatically sized to fit content. The element determines its own size.
|
||||
Content,
|
||||
/// Fixed size in cells.
|
||||
Const(u16),
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Dimension::use_dimension(&self, parent: u16, r: &mut u16)`
|
||||
Applies a `Dimension` rule to determine a final size.
|
||||
This method is used internally by `Transform::use_dimensions` to resolve the `width` or `height`.
|
||||
|
||||
**Arguments:**
|
||||
* `parent`: The parent's available dimension (parent width for `width`, parent height for `height`).
|
||||
* `r`: A mutable reference to the `u16` where the resolved dimension should be stored.
|
||||
|
||||
### Implementations
|
||||
|
||||
#### `impl From<u16> for Dimension`
|
||||
Allows `u16` values to be implicitly converted to `Dimension::Const(value)`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::style::Dimension;
|
||||
let dim_w: Dimension = 50; // Equivalent to Dimension::Const(50)
|
||||
```
|
||||
|
||||
## `Background`
|
||||
|
||||
`Background` defines the visual appearance of a widget's background.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Background {
|
||||
/// Transparent / no background.
|
||||
NoBackground,
|
||||
/// Draws a basic rectangular outline using the given 24-bit RGB color.
|
||||
Outline(u32),
|
||||
/// Draws a rounded rectangular outline using the given 24-bit RGB color.
|
||||
RoundedOutline(u32),
|
||||
/// Fills the background with the specified 24-bit RGB color.
|
||||
Solid(u32),
|
||||
}
|
||||
```
|
||||
|
||||
## `Transform` (Component)
|
||||
|
||||
The `Transform` component is attached to widgets to define their layout rules using `Position` and `Dimension` enums.
|
||||
|
||||
```rust
|
||||
component!(Transform {
|
||||
pub x: Position,
|
||||
pub y: Position,
|
||||
pub mx: i32, // Horizontal margin (offset)
|
||||
pub my: i32, // Vertical margin (offset)
|
||||
pub px: u16, // Horizontal padding (internal spacing)
|
||||
pub py: u16, // Vertical padding (internal spacing)
|
||||
pub width: Dimension,
|
||||
pub height: Dimension,
|
||||
});
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Transform::new() -> Transform`
|
||||
Creates a default `Transform` with top-left alignment (`Const(0)` for x/y), no margins or padding, and content sizing (`Dimension::Content`).
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let default_transform = Transform::new();
|
||||
```
|
||||
|
||||
#### `Transform::center() -> Transform`
|
||||
Shortcut for centering both horizontally and vertically. Sets `x: Position::Center` and `y: Position::Center`, with other fields as default.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::center();
|
||||
Div { "I am centered" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::bottom(mut self) -> Self`
|
||||
Fluent method to align the widget to the bottom of its parent. Sets `self.y = Position::End`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::new().bottom();
|
||||
Div { "I am at the bottom" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::right(mut self) -> Self`
|
||||
Fluent method to align the widget to the right of its parent. Sets `self.x = Position::End`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::new().right();
|
||||
Div { "I am at the right" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::margin(mut self, x: i32, y: i32) -> Self`
|
||||
Fluent method to add margin (offset) from the parent edge. Sets `self.mx = x` and `self.my = y`.
|
||||
|
||||
**Arguments:**
|
||||
* `x`: Horizontal margin. Positive moves right, negative moves left.
|
||||
* `y`: Vertical margin. Positive moves down, negative moves up.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::new().right().bottom().margin(-2, -1);
|
||||
Div { "2 cells from right, 1 cell from bottom" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::padding(mut self, x: u16, y: u16) -> Self`
|
||||
Fluent method to add internal spacing (padding) around the content. Sets `self.px = x` and `self.py = y`. This padding is added *inside* the widget's determined `width` and `height`.
|
||||
|
||||
**Arguments:**
|
||||
* `x`: Horizontal padding.
|
||||
* `y`: Vertical padding.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::new().dimensions(10, 3).padding(1, 0);
|
||||
Div { "Padded text" } // Text will be 1 cell in from left/right edges
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::dimensions(mut self, width: u16, height: u16) -> Self`
|
||||
Fluent method to set constant dimensions. Sets `self.width = Dimension::Const(width)` and `self.height = Dimension::Const(height)`.
|
||||
|
||||
**Arguments:**
|
||||
* `width`: Fixed width in cells.
|
||||
* `height`: Fixed height in cells.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
rsx! {
|
||||
@Transform::new().dimensions(20, 5);
|
||||
Div { "A 20x5 cell box" }
|
||||
}
|
||||
```
|
||||
|
||||
#### `Transform::use_dimensions(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform)`
|
||||
Resolves the `Dimension` rules (`width`, `height`) into absolute values and updates the `raw.width` and `raw.height` fields of the provided `RawTransform`. This is an internal method called during the rendering pipeline.
|
||||
|
||||
#### `Transform::use_position(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform)`
|
||||
Resolves the `Position` rules (`x`, `y`) into absolute coordinates and updates the `raw.x` and `raw.y` fields of the provided `RawTransform`. This is an internal method called during the rendering pipeline, usually *after* dimensions have been resolved.
|
||||
|
||||
## `Style` (Component)
|
||||
|
||||
The `Style` component defines the background and foreground appearance of a widget.
|
||||
|
||||
```rust
|
||||
component!(Style {
|
||||
pub background: Background,
|
||||
pub foreground: Option<u32>, // 24-bit RGB color, or None for default
|
||||
});
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Style::new() -> Self`
|
||||
Creates a default `Style` with `NoBackground` and `foreground: None`.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let default_style = Style::new();
|
||||
```
|
||||
|
||||
**Usage with `rsx!`:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
|
||||
rsx! {
|
||||
@Transform::new().dimensions(20, 5);
|
||||
@Style { background: Background::Solid(0x550055), foreground: Some(0xFFFFFF) };
|
||||
Div { "Purple background, white text" }
|
||||
|
||||
@Transform::new().dimensions(20, 5).margin(0, 6);
|
||||
@Style { background: Background::Outline(0x00FF00) };
|
||||
Div { "Green outline, default text color" }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,135 @@
|
||||
# `osui::utils`
|
||||
|
||||
The `utils` module provides a collection of small, standalone utility functions for common terminal manipulations and string operations. These functions are used internally by OSUI but can also be helpful for developers building their own terminal-based applications.
|
||||
|
||||
## Functions
|
||||
|
||||
#### `clear() -> io::Result<()>`
|
||||
Clears the entire terminal screen and moves the cursor to the top-left corner (position 1,1).
|
||||
|
||||
**Returns:**
|
||||
A `std::io::Result<()>` indicating success or if an I/O error occurred.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
utils::clear().expect("Failed to clear terminal");
|
||||
```
|
||||
|
||||
#### `hide_cursor() -> io::Result<()>`
|
||||
Hides the terminal cursor. This is typically called at the start of an OSUI application to provide a cleaner UI experience.
|
||||
|
||||
**Returns:**
|
||||
A `std::io::Result<()>` indicating success or if an I/O error occurred.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
utils::hide_cursor().expect("Failed to hide cursor");
|
||||
```
|
||||
|
||||
#### `show_cursor() -> io::Result<()>`
|
||||
Shows the terminal cursor. This is typically called when the OSUI application exits or needs to return control to the user.
|
||||
|
||||
**Returns:**
|
||||
A `std::io::Result<()>` indicating success or if an I/O error occurred.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
utils::show_cursor().expect("Failed to show cursor");
|
||||
```
|
||||
|
||||
#### `flush() -> io::Result<()>`
|
||||
Flushes the standard output buffer. This ensures that any buffered print commands are immediately written to the terminal. OSUI often calls this after printing to ensure visual updates are instantaneous.
|
||||
|
||||
**Returns:**
|
||||
A `std::io::Result<()>` indicating success or if an I/O error occurred.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
print!("Some text that might be buffered.");
|
||||
utils::flush().expect("Failed to flush stdout");
|
||||
```
|
||||
|
||||
#### `str_size(s: &str) -> (u16, u16)`
|
||||
Calculates the dimensions (width and height) of a string as it would be rendered in a terminal.
|
||||
It accounts for newline characters (`\n`) to determine height and calculates the maximum line width.
|
||||
|
||||
**Arguments:**
|
||||
* `s`: The input string.
|
||||
|
||||
**Returns:**
|
||||
A tuple `(max_width, height)` where:
|
||||
* `max_width`: The maximum width of any line in the string, in characters.
|
||||
* `height`: The number of lines in the string.
|
||||
|
||||
**Example:**
|
||||
```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 hexadecimal RGB color code (e.g., `0xFF0000` for red) into an ANSI escape sequence for setting the **foreground** color.
|
||||
|
||||
**Arguments:**
|
||||
* `hex`: A `u32` representing the RGB color (0xRRGGBB).
|
||||
|
||||
**Returns:**
|
||||
A `String` containing the ANSI escape code.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
let red_fg = utils::hex_ansi(0xFF0000);
|
||||
println!("{}This text is red\x1b[0m", red_fg); // \x1b[0m resets color
|
||||
```
|
||||
|
||||
#### `hex_ansi_bg(hex: u32) -> String`
|
||||
Converts a 24-bit hexadecimal RGB color code into an ANSI escape sequence for setting the **background** color.
|
||||
|
||||
**Arguments:**
|
||||
* `hex`: A `u32` representing the RGB color (0xRRGGBB).
|
||||
|
||||
**Returns:**
|
||||
A `String` containing the ANSI escape code.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
let blue_bg = utils::hex_ansi_bg(0x0000FF);
|
||||
println!("{}This text has a blue background\x1b[0m", blue_bg);
|
||||
```
|
||||
|
||||
#### `print(x: u16, y: u16, text: &str)`
|
||||
Prints a string to the terminal at specific coordinates. The coordinates are 1-based (row 1, column 1 is top-left). Includes an ANSI reset code `\x1b[0m` after the text.
|
||||
|
||||
**Arguments:**
|
||||
* `x`: The 1-based column.
|
||||
* `y`: The 1-based row.
|
||||
* `text`: The string to print.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
utils::print(5, 3, "Hello at (5,3)");
|
||||
```
|
||||
|
||||
#### `print_liner(x: u16, y: u16, liner: &str, text: &str)`
|
||||
Prints a string to the terminal at specific coordinates, prefixed with an ANSI "liner" (e.g., a color escape sequence). This function is used by OSUI to apply colors to text. The `liner` string is printed *before* the text on each line. Includes an ANSI reset code `\x1b[0m` after the text.
|
||||
|
||||
**Arguments:**
|
||||
* `x`: The 1-based column.
|
||||
* `y`: The 1-based row.
|
||||
* `liner`: The ANSI escape sequence to apply (e.g., color code).
|
||||
* `text`: The string to print.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::utils;
|
||||
let green = utils::hex_ansi(0x00FF00);
|
||||
utils::print_liner(1, 1, &green, "This text is green");
|
||||
```
|
||||
@@ -0,0 +1,265 @@
|
||||
# `osui::widget`
|
||||
|
||||
The `widget` module defines the fundamental traits and types that constitute OSUI's widget system. It provides the building blocks (`Element`, `Component`) and the containers (`Widget`, `StaticWidget`, `DynWidget`) necessary to create and manage the UI tree.
|
||||
|
||||
## `Element` Trait
|
||||
|
||||
The core trait for anything that can be rendered in the UI. Elements are responsible for their own rendering logic and can define hooks for lifecycle events and child rendering.
|
||||
|
||||
```rust
|
||||
pub trait Element: Send + Sync {
|
||||
/// Called to perform rendering for the element. Elements draw their own content here.
|
||||
#[allow(unused)]
|
||||
fn render(
|
||||
&mut self,
|
||||
scope: &mut RenderScope,
|
||||
render_context: &crate::render_scope::RenderContext,
|
||||
) {}
|
||||
|
||||
/// Called after rendering, for follow-up logic or cleanup.
|
||||
/// Container elements typically trigger rendering of their children here.
|
||||
#[allow(unused)]
|
||||
fn after_render(
|
||||
&mut self,
|
||||
scope: &mut RenderScope,
|
||||
render_context: &crate::render_scope::RenderContext,
|
||||
) {}
|
||||
|
||||
/// Called by a parent widget to add a child to this element.
|
||||
#[allow(unused)]
|
||||
fn draw_child(&mut self, element: &Arc<Widget>) {}
|
||||
|
||||
/// Called to handle events for this element.
|
||||
#[allow(unused)]
|
||||
fn event(&mut self, event: &dyn Event) {}
|
||||
|
||||
/// Returns `true` if this element is a "ghost" element.
|
||||
/// Ghost elements primarily serve as layout or logical containers and do not draw themselves.
|
||||
fn is_ghost(&mut self) -> bool { false }
|
||||
|
||||
/// Returns a type-erased reference to this object for downcasting.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Returns a mutable type-erased reference to this object for downcasting.
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
## `Component` Trait
|
||||
|
||||
An optional trait for state or metadata attached to widgets. Components are dynamic extensions to a widget's behavior or data, stored in a `HashMap` by `TypeId`.
|
||||
|
||||
```rust
|
||||
pub trait Component: Send + Sync {
|
||||
/// Returns a type-erased reference to this object for downcasting.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
/// Returns a mutable type-erased reference to this object for downcasting.
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
```
|
||||
|
||||
## `BoxedElement`
|
||||
Type alias for a boxed, trait-object Element.
|
||||
`pub type BoxedElement = Box<dyn Element + Send + Sync>;`
|
||||
|
||||
## `BoxedComponent`
|
||||
Type alias for a boxed, trait-object Component.
|
||||
`pub type BoxedComponent = Box<dyn Component + Send + Sync>;`
|
||||
|
||||
## `WidgetLoad`
|
||||
|
||||
A temporary container for a widget during initial construction. It holds the root `BoxedElement` and any associated `BoxedComponent`s.
|
||||
|
||||
```rust
|
||||
pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `WidgetLoad::new<E: Element + 'static>(e: E) -> Self`
|
||||
Creates a new `WidgetLoad` with a given root `Element`.
|
||||
|
||||
**Arguments:**
|
||||
* `e`: The element to wrap.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let wl = WidgetLoad::new(String::from("Hello"));
|
||||
```
|
||||
|
||||
#### `WidgetLoad::component<C: Component + 'static>(mut self, c: C) -> Self`
|
||||
Attaches a component to the `WidgetLoad`. If a component of the same type already exists, it is *not* replaced. Use `set_component` for replacement.
|
||||
|
||||
**Arguments:**
|
||||
* `c`: The component to attach.
|
||||
|
||||
**Returns:**
|
||||
`self`, for chaining.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let wl = WidgetLoad::new(Div::new()).component(Transform::new().dimensions(10, 10));
|
||||
```
|
||||
|
||||
#### `WidgetLoad::set_component<C: Component + 'static>(mut self, c: C) -> Self`
|
||||
Replaces any existing component of the same type with the new component.
|
||||
|
||||
**Arguments:**
|
||||
* `c`: The component to set.
|
||||
|
||||
**Returns:**
|
||||
`self`, for chaining.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let wl = WidgetLoad::new(Div::new())
|
||||
.component(Transform::new().x(5)) // Adds first transform
|
||||
.set_component(Transform::new().x(10)); // Replaces with new transform
|
||||
```
|
||||
|
||||
#### `WidgetLoad::get<C: Component + 'static + Clone>(&self) -> Option<C>`
|
||||
Attempts to retrieve a component of the given type from the `WidgetLoad`. Requires the component to be `Clone`.
|
||||
|
||||
**Returns:**
|
||||
`Some(C)` if found, `None` otherwise.
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
use osui::prelude::*;
|
||||
let wl = WidgetLoad::new(Div::new()).component(Transform::new().x(5));
|
||||
let transform: Option<Transform> = wl.get(); // transform will be Some(Transform { x: Const(5), ... })
|
||||
```
|
||||
|
||||
## `StaticWidget`
|
||||
|
||||
A widget with fixed content and no dynamic behavior. It holds a `Mutex` wrapped `BoxedElement` and a `Mutex` wrapped `HashMap` of components.
|
||||
|
||||
```rust
|
||||
pub struct StaticWidget {
|
||||
element: Mutex<BoxedElement>,
|
||||
components: Mutex<HashMap<TypeId, BoxedComponent>>,
|
||||
focused: Mutex<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `StaticWidget::new(e: BoxedElement) -> Self`
|
||||
Creates a new `StaticWidget` from a `BoxedElement`.
|
||||
|
||||
#### `StaticWidget::component<C: Component + 'static>(&self, c: C)`
|
||||
Attaches a component. If a component of the same type exists, it's not replaced.
|
||||
|
||||
#### `StaticWidget::set_component<C: Component + 'static>(&self, c: C)`
|
||||
Replaces any existing component of the same type.
|
||||
|
||||
#### `StaticWidget::get<C: Component + 'static + Clone>(&self) -> Option<C>`
|
||||
Retrieves a cloned component of the specified type.
|
||||
|
||||
## `DynWidget`
|
||||
|
||||
A widget with dynamic content and dependency tracking. It can be rebuilt using a provided `FnMut()` function when dependencies change, enabling reactive updates.
|
||||
|
||||
```rust
|
||||
pub struct DynWidget {
|
||||
element: Mutex<BoxedElement>,
|
||||
components: Mutex<HashMap<TypeId, BoxedComponent>>,
|
||||
load: Mutex<Box<dyn FnMut() -> WidgetLoad + Send + Sync>>, // The closure that rebuilds the widget
|
||||
dependencies: Mutex<Vec<Box<dyn DependencyHandler>>>, // Tracked dependencies
|
||||
injection: Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>, // For runtime modification
|
||||
focused: Mutex<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `DynWidget::new<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self`
|
||||
Creates a new `DynWidget` from a closure that returns a `WidgetLoad`. The closure is immediately executed once to initialize the widget's content.
|
||||
|
||||
#### `DynWidget::inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(&self, f: F)`
|
||||
Replaces or modifies the widget's structure on subsequent reloads and initializations. The provided closure takes the `WidgetLoad` generated by the `load` closure and returns a modified `WidgetLoad`. This is useful for dynamically adding components or wrapping elements. It triggers an immediate `refresh()`.
|
||||
|
||||
#### `DynWidget::refresh(&self)`
|
||||
Forces the widget to rebuild its content by re-evaluating the original `load` function. If an `injection` closure is present, it's applied after the `load` function. This method updates the internal `element` and `components`.
|
||||
|
||||
#### `DynWidget::auto_refresh(&self)`
|
||||
Checks if any registered dependencies have changed (via `DependencyHandler::check()`). If so, it calls `self.refresh()`. This method is called automatically by the `Screen` in its rendering loop for `DynWidget`s.
|
||||
|
||||
#### `DynWidget::dependency<D: DependencyHandler + 'static>(&self, d: D)`
|
||||
Adds a dependency to this widget. When `d` signals a change, the widget will `auto_refresh()`. The `add()` method of the `DependencyHandler` is called.
|
||||
|
||||
#### `DynWidget::dependency_box(&self, d: Box<dyn DependencyHandler>)`
|
||||
Adds a boxed dependency. Similar to `dependency` but takes a `Box<dyn DependencyHandler>`.
|
||||
|
||||
#### `DynWidget::component<C: Component + 'static>(&self, c: C)`
|
||||
Attaches a component. If a component of the same type exists, it's not replaced.
|
||||
|
||||
#### `DynWidget::set_component<C: Component + 'static>(&self, c: C)`
|
||||
Replaces any existing component of the same type.
|
||||
|
||||
#### `DynWidget::get<C: Component + 'static + Clone>(&self) -> Option<C>`
|
||||
Retrieves a cloned component of the specified type.
|
||||
|
||||
## `Widget` (Enum)
|
||||
|
||||
A reference-counted wrapper around either a static or dynamic widget. `Arc<Widget>` is the standard way to store and pass around widgets in the UI tree.
|
||||
|
||||
```rust
|
||||
pub enum Widget {
|
||||
Static(StaticWidget),
|
||||
Dynamic(DynWidget),
|
||||
}
|
||||
```
|
||||
|
||||
### Associated Methods
|
||||
|
||||
#### `Widget::new_static(e: BoxedElement) -> Self`
|
||||
Creates a new `Widget::Static` from a `BoxedElement`.
|
||||
|
||||
#### `Widget::new_dyn<F: FnMut() -> WidgetLoad + 'static + Send + Sync>(e: F) -> Self`
|
||||
Creates a new `Widget::Dynamic` from a closure that returns a `WidgetLoad`.
|
||||
|
||||
#### `Widget::is_focused(&self) -> bool`
|
||||
Returns `true` if the widget currently has focus. Focus is managed by extensions like `RelativeFocusExtension`.
|
||||
|
||||
#### `Widget::is_ghost(&self) -> bool`
|
||||
Delegates to the underlying `Element::is_ghost()`. Returns `true` if the element is primarily a layout container.
|
||||
|
||||
#### `Widget::set_focused(&self, f: bool)`
|
||||
Sets the focus status of the widget. This method is usually called by focus management extensions.
|
||||
|
||||
#### `Widget::get_elem(&self) -> MutexGuard<BoxedElement>`
|
||||
Provides a `MutexGuard` for mutable access to the underlying `BoxedElement`. Use with caution to avoid deadlocks.
|
||||
|
||||
#### `Widget::after_render(&self)`
|
||||
Internal: Calls `after_render` hooks for the underlying element and extensions.
|
||||
|
||||
#### `Widget::component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self>`
|
||||
Attaches a component to the widget. If a component of the same type already exists, it is *not* replaced. Returns `self` for chaining.
|
||||
|
||||
#### `Widget::set_component<C: Component + 'static>(self: &Arc<Self>, c: C) -> &Arc<Self>`
|
||||
Replaces any existing component of the same type with the new component. Returns `self` for chaining.
|
||||
|
||||
#### `Widget::get<C: Component + 'static + Clone>(&self) -> Option<C>`
|
||||
Retrieves a cloned component of the specified type from the widget's component map.
|
||||
|
||||
#### `Widget::inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(self: &Arc<Self>, mut f: F)`
|
||||
Injects a modification closure into a `DynWidget`. For `StaticWidget`s, it applies the modification directly to its components. This allows runtime structural changes to widgets.
|
||||
|
||||
#### `Widget::refresh(self: &Arc<Self>)`
|
||||
Forces a `DynWidget` to rebuild its content. Does nothing for `StaticWidget`s.
|
||||
|
||||
#### `Widget::auto_refresh(self: &Arc<Self>)`
|
||||
Triggers `auto_refresh` on a `DynWidget` (checking and rebuilding if dependencies changed). Does nothing for `StaticWidget`s.
|
||||
|
||||
#### `Widget::dependency<D: DependencyHandler + 'static>(self: &Arc<Self>, d: D) -> &Arc<Self>`
|
||||
Adds a dependency to a `DynWidget`. Does nothing for `StaticWidget`s. Returns `self` for chaining.
|
||||
|
||||
#### `Widget::dependency_box(self: &Arc<Self>, d: Box<dyn DependencyHandler>) -> &Arc<Self>`
|
||||
Adds a boxed dependency to a `DynWidget`. Does nothing for `StaticWidget`s. Returns `self` for chaining.
|
||||
|
||||
#### `Widget::event<E: Event + Clone + 'static>(self: &Arc<Self>, e: &E)`
|
||||
Dispatches an event to the widget. If the widget has a `Handler<E>` component, its callback is invoked. If the widget is focused, its underlying `Element::event` method is also called.
|
||||
Reference in New Issue
Block a user