From aa55cc059ac1e51b9218690f288aff92f9e9f53f Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 20 Jan 2025 00:51:03 +0100 Subject: [PATCH] documented the code --- src/console.rs | 53 +++++++++++++ src/lib.rs | 212 ++++++++++++++++++++++++++++--------------------- src/rsx.rs | 26 ++++++ src/state.rs | 30 ++++++- src/utils.rs | 54 +++++++++++++ 5 files changed, 282 insertions(+), 93 deletions(-) diff --git a/src/console.rs b/src/console.rs index ae3749c..0d4e91b 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,17 +1,37 @@ +//! Console module +//! +//! This module provides utilities for managing terminal interactions, including event handling, +//! rendering, and terminal state management. + use crate::{Element, Frame}; +/// Represents the console state, containing a frame for rendering and a mouse capture flag. pub struct Console(Frame, bool); +/// Enum representing various events that can occur in the console. #[derive(Debug, Clone)] pub enum Event { + /// A keyboard event. Key(crossterm::event::KeyEvent), + /// A terminal resize event with new dimensions (width, height). Resize(u16, u16), + /// A mouse event. Mouse(crossterm::event::MouseEvent), + /// A paste event with the pasted content. Paste(String), + /// An event indicating the terminal gained focus. FocusGained, + /// An event indicating the terminal lost focus. FocusLost, } +/// Initializes the console with raw mode enabled and optionally mouse capture. +/// +/// # Arguments +/// * `mouse` - A boolean flag indicating whether to enable mouse capture. +/// +/// # Returns +/// A `Console` instance wrapped in a `Result`. pub fn init(mouse: bool) -> crate::Result { crossterm::terminal::enable_raw_mode()?; crate::utils::clear()?; @@ -23,11 +43,28 @@ pub fn init(mouse: bool) -> crate::Result { } impl Console { + /// Renders a user interface element with an optional event. + /// + /// # Arguments + /// * `ui` - The UI element to render. + /// * `event` - An optional event to pass to the UI element. + /// + /// # Returns + /// A `Result` indicating success or failure. pub fn draw(&mut self, ui: Element, event: Option) -> crate::Result<()> { self.0.clear()?; ui(&mut self.0, event) } + /// Runs the console loop, rendering the UI and handling events. + /// + /// This method is disabled when the `engine` feature is enabled. + /// + /// # Arguments + /// * `ui` - The UI element to render. + /// + /// # Returns + /// A `Result` indicating success or failure. #[cfg(not(feature = "engine"))] pub fn run(&mut self, ui: Element) -> crate::Result<()> { self.draw(ui.clone(), None)?; @@ -41,6 +78,10 @@ impl Console { } } + /// Ends the console session, restoring the terminal state. + /// + /// # Returns + /// A `Result` indicating success or failure. pub fn end(&self) -> crate::Result<()> { if self.1 { crossterm::execute!(std::io::stdout(), crossterm::event::DisableMouseCapture)?; @@ -50,11 +91,19 @@ impl Console { crate::utils::show_cursor() } + /// Retrieves the current terminal size. + /// + /// # Returns + /// A tuple containing the width and height of the terminal. pub fn size(&self) -> (u16, u16) { (self.0.width, self.0.height) } } +/// Reads an event from the terminal. +/// +/// # Returns +/// An `Event` wrapped in a `Result`. pub fn read() -> crate::Result { let event = crossterm::event::read()?; @@ -68,6 +117,10 @@ pub fn read() -> crate::Result { }) } +/// Attempts to read an event from the terminal without blocking. +/// +/// # Returns +/// An `Option` containing an `Event` if one is available. pub fn try_read() -> Option { if crossterm::event::poll(std::time::Duration::ZERO).unwrap_or(false) { read().ok() diff --git a/src/lib.rs b/src/lib.rs index 0c1ce2f..bed1648 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,15 @@ +//! # Library Documentation +//! This library provides a framework for creating terminal-based user interfaces. +//! It includes support for widgets, layouts, and event handling using `crossterm`. + +//! ## Modules +//! - `console`: Handles terminal input and events. +//! - `elements`: Provides pre-defined UI elements. Optional, enabled by default. +//! - `rsx`: Supports declarative UI definition using an XML-like syntax. Optional, enabled by default. +//! - `state`: Manages state for widgets and other components. +//! - `utils`: Utility functions for terminal rendering. +//! - `prelude`: Exports commonly used types and traits for easy access. + pub mod console; #[cfg(not(feature = "no_elem"))] pub mod elements; @@ -6,33 +18,74 @@ pub mod rsx; pub mod state; pub mod utils; +/// Commonly used imports for convenience. pub mod prelude { pub use crate::*; pub use console::Event; pub use crossterm::event::{KeyCode, KeyEvent}; } +/// Type alias for simplifying error handling. +/// Represents an I/O operation result. pub use std::io::Result; +/// Type alias for a UI element. +/// An `Element` is a thread-safe function that renders onto a `Frame` +/// and optionally handles events. pub type Element = std::sync::Arc) -> crate::Result<()>>; +/// A trait representing a UI widget. pub trait Widget { + /// Renders the widget as a `String`. fn render(&self) -> String; + + /// Handles an event for the widget. Defaults to a no-op. fn event(&mut self, event: console::Event) { _ = event; } } +/// Struct representing a rectangular area in the terminal. +#[derive(Debug, Clone, Copy)] +pub struct Area { + pub width: Size, + pub height: Size, + pub x: Pos, + pub y: Pos, +} + +/// Struct representing a rendering frame. +#[derive(Debug, Default, Clone, Copy)] +pub struct Frame { + pub width: u16, + pub height: u16, + last_elem: (u16, u16), +} + +/// Enum representing positioning options for widgets. #[derive(Debug, Clone, Copy)] pub enum Pos { + /// Automatic positioning. #[allow(non_camel_case_types)] auto, + /// Centered positioning. #[allow(non_camel_case_types)] center, + /// Fixed position as a number of columns or rows. + Num(u16), +} + +/// Enum representing sizing options for widgets. +#[derive(Debug, Clone, Copy)] +pub enum Size { + /// Automatic sizing. + Auto, + /// Fixed size. Num(u16), } impl Pos { + /// Calculates the position based on the current frame dimensions. pub fn get(self, auto: u16, width: u16, frame: u16) -> u16 { match self { Self::auto => auto, @@ -42,13 +95,8 @@ impl Pos { } } -#[derive(Debug, Clone, Copy)] -pub enum Size { - Auto, - Num(u16), -} - impl Size { + /// Internal method to compute size. fn get_(self, written: u16) -> u16 { match self { Self::Auto => written, @@ -56,27 +104,79 @@ impl Size { } } + /// Computes the size based on written content or frame dimensions. pub fn get(self, written: u16, _frame: u16) -> u16 { self.get_(written) } } -#[derive(Debug, Clone, Copy)] -pub struct Area { - pub width: Size, - pub height: Size, - pub x: Pos, - pub y: Pos, +impl Area { + /// Creates a new `Area` with default values. + pub fn new() -> Self { + Self::default() + } + + /// Center the `Area` horizontally. Only available with the `portable` feature. + #[cfg(feature = "portable")] + pub fn center_x() -> Self { + let mut s = Self::default(); + s.x = Pos::center; + s + } + + /// Center the `Area` vertically. Only available with the `portable` feature. + #[cfg(feature = "portable")] + pub fn center_y() -> Self { + let mut s = Self::default(); + s.y = Pos::center; + s + } + + /// Center the `Area` both horizontally and vertically. Only available with the `portable` feature. + #[cfg(feature = "portable")] + pub fn center() -> Self { + let mut s = Self::default(); + s.x = Pos::center; + s.y = Pos::center; + s + } + + // Additional utility methods for setting properties are available with the `portable` feature. + #[cfg(feature = "portable")] + pub fn set_width(&mut self, w: Size) -> Self { + self.width = w; + *self + } + #[cfg(feature = "portable")] + pub fn set_height(&mut self, h: Size) -> Self { + self.height = h; + *self + } + #[cfg(feature = "portable")] + pub fn set_x(&mut self, x: Pos) -> Self { + self.x = x; + *self + } + #[cfg(feature = "portable")] + pub fn set_y(&mut self, y: Pos) -> Self { + self.y = y; + *self + } } -#[derive(Debug, Default, Clone, Copy)] -pub struct Frame { - pub width: u16, - pub height: u16, - last_elem: (u16, u16), +impl Default for Area { + fn default() -> Self { + Self { + width: Size::Auto, + height: Size::Auto, + x: Pos::Num(0), + y: Pos::auto, + } + } } impl Frame { + /// Draws a widget on the frame. pub fn draw(&mut self, w: &W, props: Area) -> Result<()> where W: Widget, @@ -114,12 +214,14 @@ impl Frame { Ok(()) } + /// Clears the frame. pub fn clear(&mut self) -> Result<()> { self.last_elem.0 = 0; self.last_elem.1 = 0; utils::clear() } + /// Creates a new frame with the specified dimensions. pub fn new((width, height): (u16, u16)) -> Self { Self { width, @@ -129,81 +231,7 @@ impl Frame { } } -impl Area { - pub fn new() -> Self { - Self::default() - } - - #[cfg(feature = "portable")] - pub fn center_x() -> Self { - let mut s = Self::default(); - s.x = Pos::Center; - s - } - - #[cfg(feature = "portable")] - pub fn center_y() -> Self { - let mut s = Self::default(); - s.y = Pos::Center; - s - } - - #[cfg(feature = "portable")] - pub fn center() -> Self { - let mut s = Self::default(); - s.x = Pos::Center; - s.y = Pos::Center; - s - } - - #[cfg(feature = "portable")] - pub fn set_width(&mut self, w: Size) -> Self { - self.width = w; - *self - } - - #[cfg(feature = "portable")] - pub fn set_height(&mut self, h: Size) -> Self { - self.height = h; - *self - } - - #[cfg(feature = "portable")] - pub fn set_x(&mut self, x: Pos) -> Self { - self.x = x; - *self - } - - #[cfg(feature = "portable")] - pub fn set_y(&mut self, y: Pos) -> Self { - self.y = y; - *self - } - - #[cfg(feature = "portable")] - pub fn x_auto(&mut self) -> Self { - self.x = Pos::Auto; - *self - } - - #[cfg(feature = "portable")] - pub fn y_zero(&mut self) -> Self { - self.y = Pos::Num(0); - *self - } -} - -impl Default for Area { - fn default() -> Self { - Self { - width: Size::Auto, - height: Size::Auto, - x: Pos::Num(0), - y: Pos::auto, - } - } -} - +/// Creates a new state object. pub fn use_state(v: T) -> state::State { state::State(Box::into_raw(Box::new(v))) } diff --git a/src/rsx.rs b/src/rsx.rs index 813bcaf..54c19c6 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -1,3 +1,23 @@ +/// A brief summary of what the macro does. +/// +/// A detailed explanation about the macro's purpose, +/// how it expands, and when to use it. +/// +/// # Examples +/// +/// Simple +/// ```rust +/// rsx! { +/// "Hello, World!" +/// } +/// ``` +/// +/// Button example +/// ```rust +/// rsx! { +/// button { "{count}" } +/// } +/// ``` #[macro_export] macro_rules! rsx { ($($inner:tt)*) => { @@ -10,6 +30,9 @@ macro_rules! rsx { }; } +/// # Warning +/// +/// **Don't use this macro manually** use `rsx!` instead #[macro_export] macro_rules! rsx_inner { // For loop @@ -81,6 +104,9 @@ macro_rules! rsx_inner { ($frame:expr, $event:expr;) => {}; } +/// # Warning +/// +/// **Don't use this macro manually** use `rsx!` instead #[macro_export] macro_rules! tw_area { ($a:expr, $n:ident) => { diff --git a/src/state.rs b/src/state.rs index 0e51328..c9c7a2b 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,23 +1,42 @@ +//! The `state` module provides the `State` type for managing mutable state across UI components +//! and operations. It wraps a raw pointer to enable shared mutability and integrates with common +//! traits for ease of use. + +/// A wrapper around a raw pointer to a mutable value, allowing shared state management. +/// +/// # Safety +/// This type uses unsafe code to dereference raw pointers. Proper care must be taken +/// to ensure the validity and lifetime of the referenced value. +/// +/// # Examples +/// ``` +/// let value = 42; +/// let state = State(Box::into_raw(Box::new(value))); +/// assert_eq!(*state, 42); +/// ``` #[derive(Clone, Copy)] pub struct State(pub(crate) *mut T); impl std::fmt::Display for State { + /// Formats the value of the state for display. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { &*self.0 }) } } impl std::fmt::Debug for State { + /// Formats the state for debugging purposes. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "State({:?})", unsafe { &*self.0 }) } } -//// State ops //// +//// State Operations //// impl + Clone> std::ops::Add for State { type Output = T; + /// Adds a value to the state, returning the result. fn add(self, rhs: T) -> Self::Output { unsafe { let lhs = &mut *self.0; @@ -29,6 +48,7 @@ impl + Clone> std::ops::Add for State { impl + Clone> std::ops::Sub for State { type Output = T; + /// Subtracts a value from the state, returning the result. fn sub(self, rhs: T) -> Self::Output { unsafe { let lhs = &mut *self.0; @@ -40,6 +60,7 @@ impl + Clone> std::ops::Sub for State { impl + Clone> std::ops::Div for State { type Output = T; + /// Divides the state by a value, returning the result. fn div(self, rhs: T) -> Self::Output { unsafe { let lhs = &mut *self.0; @@ -49,12 +70,14 @@ impl + Clone> std::ops::Div for State { } impl std::ops::AddAssign for State { + /// Adds a value to the state in place. fn add_assign(&mut self, rhs: T) { unsafe { *self.0 += rhs } } } impl std::ops::SubAssign for State { + /// Subtracts a value from the state in place. fn sub_assign(&mut self, rhs: T) { unsafe { *self.0 -= rhs } } @@ -62,24 +85,29 @@ impl std::ops::SubAssign for State { impl std::ops::Deref for State { type Target = T; + + /// Dereferences the state to access the underlying value. fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } } impl std::ops::DerefMut for State { + /// Dereferences the state to access the underlying mutable value. fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.0 } } } impl State { + /// Returns a copy of the current state. pub fn copy_state(&self) -> Self { Self(self.0) } } impl> PartialEq for State { + /// Compares the state with another value for equality. fn eq(&self, other: &T) -> bool { unsafe { &*self.0 == other } } diff --git a/src/utils.rs b/src/utils.rs index 99d37a4..3158a5d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,24 +1,78 @@ +//! The `utils` module provides utility functions for terminal manipulation +//! and string operations, designed to enhance terminal-based applications. + use std::io::Write; +/// Clears the terminal screen and moves the cursor to the top-left corner. +/// +/// # Returns +/// A `crate::Result<()>` indicating whether the operation succeeded. +/// +/// # Example +/// ``` +/// utils::clear().unwrap(); +/// ``` pub fn clear() -> crate::Result<()> { print!("\x1B[2J\x1B[H"); std::io::stdout().flush() } +/// Hides the terminal cursor. +/// +/// # Returns +/// A `crate::Result<()>` indicating whether the operation succeeded. +/// +/// # Example +/// ``` +/// utils::hide_cursor().unwrap(); +/// ``` pub fn hide_cursor() -> crate::Result<()> { print!("\x1b[?25l"); std::io::stdout().flush() } +/// Shows the terminal cursor. +/// +/// # Returns +/// A `crate::Result<()>` indicating whether the operation succeeded. +/// +/// # Example +/// ``` +/// utils::show_cursor().unwrap(); +/// ``` pub fn show_cursor() -> crate::Result<()> { print!("\x1B[?25h"); std::io::stdout().flush() } +/// Flushes the terminal's stdout buffer. +/// +/// # Returns +/// A `crate::Result<()>` indicating whether the operation succeeded. +/// +/// # Example +/// ``` +/// utils::flush().unwrap(); +/// ``` pub fn flush() -> crate::Result<()> { std::io::stdout().flush() } +/// Calculates the width and height of a string when rendered in a terminal. +/// +/// # Arguments +/// - `s`: The input string. +/// +/// # Returns +/// A tuple `(u16, u16)` where: +/// - The first value is the maximum width of the string in characters. +/// - The second value is the height of the string in lines. +/// +/// # Example +/// ``` +/// let (width, height) = utils::str_size("Hello\nWorld!"); +/// assert_eq!((5, 2), (width, height)); +/// ``` pub fn str_size(s: &str) -> (u16, u16) { let mut height = 1; let mut max_width = 0;