170 lines
5.4 KiB
Rust
170 lines
5.4 KiB
Rust
/// The `ui` module defines the basic building blocks for creating a terminal-based UI framework.
|
|
///
|
|
/// # Modules
|
|
/// - `styles`: A module for defining UI styles.
|
|
pub mod styles;
|
|
pub use styles::*;
|
|
|
|
use crate::prelude::*;
|
|
use osui_element::{elem_fn, element};
|
|
|
|
/// The `Text` element represents a piece of static text in the terminal UI.
|
|
///
|
|
/// It is used to render plain text within a UI component. The `Text` element does not interact
|
|
/// with events and only outputs the text when rendered.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// let text = Text::default();
|
|
/// let rendered_text = text.render(crate::State::Normal);
|
|
/// println!("{}", rendered_text);
|
|
/// ```
|
|
///
|
|
/// ## Methods
|
|
/// - `render(state: crate::State) -> String`: Renders the text to a string, depending on the state.
|
|
/// If the element contains text as a child, it returns that text. Otherwise, it returns an empty string.
|
|
#[element]
|
|
#[elem_fn]
|
|
#[derive(Default, Debug)]
|
|
pub struct Text {}
|
|
|
|
impl Component for Text<'_> {
|
|
fn render(&self, _: bool) -> String {
|
|
if let Children::Text(text) = &self.children {
|
|
text.clone()
|
|
} else {
|
|
String::new()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The `Div` element represents a container for other UI elements. It can hold child components
|
|
/// and render them in a specific layout. The `Div` element also handles keyboard navigation through
|
|
/// its children.
|
|
///
|
|
/// The `Div` element supports rendering a list of children and allows interactive navigation using
|
|
/// arrow keys (Up, Down, Left, Right). It renders the selected child element differently based on the state.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// let div = Div::default();
|
|
/// let rendered_div = div.render(crate::State::Normal);
|
|
/// println!("{}", rendered_div);
|
|
/// ```
|
|
///
|
|
/// ## Methods
|
|
/// - `render(state: crate::State) -> String`: Renders the `Div` and its child elements to a string.
|
|
/// The child elements are rendered according to the current selection (`child` index).
|
|
/// - `event(event: Event, state: &crate::StateChanger)`: Handles keyboard events for
|
|
/// navigation through child elements. Arrow keys (Up, Down, Left, Right) change the currently selected child.
|
|
|
|
#[element]
|
|
#[elem_fn]
|
|
#[derive(Default, Debug)]
|
|
pub struct Div {
|
|
pub styling: Option<std::collections::HashMap<StyleName, Style>>,
|
|
}
|
|
|
|
impl Component for Div<'_> {
|
|
fn render(&self, focused: bool) -> String {
|
|
let mut frame = crate::utils::create_frame(self.width.get_value(), self.height.get_value());
|
|
|
|
if let Children::Children(children, child) = &self.children {
|
|
for (i, elem) in children.iter().enumerate() {
|
|
crate::utils::render_to_frame(
|
|
focused && i == *child,
|
|
self.width.get_value(),
|
|
&mut frame,
|
|
elem,
|
|
);
|
|
}
|
|
}
|
|
|
|
frame.join("\n")
|
|
}
|
|
|
|
fn event(&mut self, event: Event) {
|
|
if let Some(styling) = self.styling.clone() {
|
|
self.set_styling(&styling);
|
|
}
|
|
match event {
|
|
Event::Key(key) => {
|
|
if let Children::Children(children, child) = &mut self.children {
|
|
*child = match key.code {
|
|
KeyCode::Up => crate::utils::closest_component(
|
|
children,
|
|
*child,
|
|
crate::utils::Direction::Up,
|
|
),
|
|
|
|
KeyCode::Down => crate::utils::closest_component(
|
|
children,
|
|
*child,
|
|
crate::utils::Direction::Down,
|
|
),
|
|
|
|
KeyCode::Left => crate::utils::closest_component(
|
|
children,
|
|
*child,
|
|
crate::utils::Direction::Left,
|
|
),
|
|
|
|
KeyCode::Right => crate::utils::closest_component(
|
|
children,
|
|
*child,
|
|
crate::utils::Direction::Right,
|
|
),
|
|
|
|
_ => {
|
|
if let Some(c) = children.get_mut(*child) {
|
|
c.event(event);
|
|
}
|
|
*child
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
if let Some(child) = self.get_child() {
|
|
child.event(event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[element]
|
|
#[elem_fn]
|
|
#[derive(Default, Debug)]
|
|
pub struct Button {
|
|
pub on_click: Handler<Button<'a>>,
|
|
pub state: &'a str,
|
|
}
|
|
|
|
impl Component for Button<'_> {
|
|
fn render(&self, focused: bool) -> String {
|
|
if self.state == "clicked" {
|
|
return self.style.write("click", &self.children.get_text());
|
|
}
|
|
if focused {
|
|
self.style.write("hover", &self.children.get_text())
|
|
} else {
|
|
self.style.write("", &self.children.get_text())
|
|
}
|
|
}
|
|
|
|
fn event(&mut self, event: Event) {
|
|
match event {
|
|
Event::Key(key) => {
|
|
if key.code == KeyCode::Enter {
|
|
self.state = "clicked";
|
|
run_handler!(self.on_click(event));
|
|
sleep(100);
|
|
self.state = "";
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|