Version 0.1.1

This commit is contained in:
2025-08-10 14:14:04 -05:00
parent 54b9cba486
commit b4891d4795
39 changed files with 4587 additions and 0 deletions
@@ -0,0 +1,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.