From 5ff2866f26f8e16ff7cce789970735aa48df7f5a Mon Sep 17 00:00:00 2001 From: Leo dev Date: Thu, 7 Nov 2024 23:58:29 +0100 Subject: [PATCH] some nice changes --- src/element.rs | 57 +++++++++++++------- src/lib.rs | 10 ++-- src/macros.rs | 128 ++++++++++++++++++++++++++++++++------------- src/main.rs | 13 ++--- src/ui/elements.rs | 17 ++++-- src/ui/mod.rs | 6 +-- src/utils.rs | 4 +- 7 files changed, 158 insertions(+), 77 deletions(-) diff --git a/src/element.rs b/src/element.rs index 53a0eb0..b8c9443 100644 --- a/src/element.rs +++ b/src/element.rs @@ -5,28 +5,37 @@ //! blocks of the TUI, each with properties such as size and position and //! methods for rendering and updating. -use dyn_clone::DynClone; +use dyn_clone::{clone_trait_object, DynClone}; /// Enum representing the size of an `Element`. /// /// - `Default(usize)` - Uses a default size for the element. /// - `Custom(usize)` - Allows specifying a custom size for the element. #[derive(Debug, Clone, Copy)] -pub enum ElementSize { - Default(usize), - Custom(usize), +pub enum Value { + Default(T), + Custom(T), } -impl ElementSize { +impl Value { + /// Creates a new Value. + /// + /// # Returns + /// + /// Value::Custom(usize) + pub fn new(value: T) -> Value { + Value::Custom(value) + } + /// Retrieves the size as an `usize`. /// /// # Returns /// /// The size value, either the default or custom size. - pub fn get_size(&self) -> usize { - match *self { - ElementSize::Custom(s) => s, - ElementSize::Default(s) => s, + pub fn get_size(&self) -> T { + match self { + Value::Custom(s) => *s, + Value::Default(s) => *s, } } @@ -37,9 +46,9 @@ impl ElementSize { /// # Arguments /// /// * `size` - The size to set for the `Element`. - pub fn try_set_size(&mut self, size: usize) { - if let ElementSize::Default(_) = *self { - *self = ElementSize::Default(size); + pub fn try_set_size(&mut self, value: T) { + if let Value::Default(_) = *self { + *self = Value::Default(value); } } } @@ -47,14 +56,14 @@ impl ElementSize { /// A trait for defining UI elements in OSUI. /// /// Elements must implement methods for updating and rendering data. -/// This trait supports dynamic dispatch and cloning. -pub trait Element: std::fmt::Debug + Send + DynClone { +pub trait Element: DynClone { /// Retrieves the data for the `Element`. /// /// # Returns /// /// `ElementData` containing position and size information. fn get_data(&self) -> ElementData; + fn set_data(&mut self, data: ElementData); /// Updates the data of the `Element` based on the given dimensions. /// @@ -64,6 +73,10 @@ pub trait Element: std::fmt::Debug + Send + DynClone { /// * `_height` - The height of the terminal window or parent element. fn update_data(&mut self, _width: usize, _height: usize); + fn get_id(&mut self) -> String; + + fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box>; + /// Renders the `Element` as a `String`. /// /// # Arguments @@ -93,20 +106,26 @@ pub trait Element: std::fmt::Debug + Send + DynClone { } } -// Enable cloning of `Element` trait objects. -dyn_clone::clone_trait_object!(Element); +clone_trait_object!(Element); /// Struct holding data relevant to an `Element`, including position and size. -#[derive(Debug)] pub struct ElementData { /// X coordinate of the `Element`. pub x: usize, /// Y coordinate of the `Element`. pub y: usize, /// Width of the `Element`, which can be default or custom. - pub width: ElementSize, + pub width: Value, /// Height of the `Element`, which can be default or custom. - pub height: ElementSize, + pub height: Value, + /// Children of the `Element` + pub children: Vec>, + /// Active Child of the `Element` + pub child: usize, + /// Text of the `Element` + pub text: String, + /// Identifier of the `Element` + pub id: String, } /// Enum representing the possible responses from an element update. diff --git a/src/lib.rs b/src/lib.rs index b2436da..d6ea53c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ pub mod element; pub mod key; pub mod macros; +mod test; pub mod ui; pub mod utils; @@ -40,7 +41,7 @@ pub mod app { use crate::{ clear, create_frame, flush, get_term_size, hide_cursor, key::{read_key, Key}, - render_to_frame, show_cursor, Command, Element, ElementSize, UpdateResponse, + render_to_frame, show_cursor, Command, Element, UpdateResponse, Value, }; /// Renders a single frame of the UI to the terminal. @@ -55,8 +56,7 @@ pub mod app { fn render(elem: &mut Box, state: usize) { let (width, height) = get_term_size(); elem.update_data(width, height); - let mut frame: Vec = - create_frame(ElementSize::Custom(width), ElementSize::Custom(height)); + let mut frame: Vec = create_frame(Value::Custom(width), Value::Custom(height)); render_to_frame(state, &mut frame, elem); clear(); print!("{}", frame.join("")); @@ -146,3 +146,7 @@ pub mod app { } } } + +pub fn run_test() { + test::main_(); +} diff --git a/src/macros.rs b/src/macros.rs index 06e6755..7d5eaf8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -33,26 +33,30 @@ macro_rules! element { $( $functions:tt )* ) => { $(#[$meta])* - #[derive(Debug, Clone)] - pub struct $name { + #[derive(Clone)] + pub struct $name<'a> { pub x: usize, pub y: usize, - pub width: ElementSize, - pub height: ElementSize, + pub width: Value, + pub height: Value, pub children: Vec>, pub child: usize, pub text: String, - pub style: Style, + pub id: &'a str, $( $inner )* } - impl Element for $name { + impl Element for $name<'_> { fn get_data(&self) -> ElementData { ElementData { x: self.x, y: self.y, width: self.width.clone(), height: self.height.clone(), + children: self.children.clone(), + child: self.child.clone(), + text: self.text.clone(), + id: self.id.to_string(), } } @@ -63,21 +67,45 @@ macro_rules! element { child.update_data(width, height); } } + + fn get_id(&mut self) -> String { + self.id.to_string() + } + + fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box> { + for elem in &mut self.children { + if elem.get_id() == id { + return Some(elem); + } + } + None + } + + fn set_data(&mut self, data: ElementData) { + self.x = data.x; + self.y = data.y; + self.width = data.width; + self.height = data.height; + self.children = data.children; + self.child = data.child; + self.text = data.text; + } + $( $functions )* } - impl $name { + impl<'a> $name<'_> { /// Creates a new instance of the element with default values. - pub fn new() -> $name { + pub fn new() -> $name<'a> { $name { children: Vec::new(), x: 0, y: 0, - width: ElementSize::Default(0), - height: ElementSize::Default(0), + width: Value::Default(0), + height: Value::Default(0), child: 0, text: String::new(), - style: Style::default(), + id: "", $( $defaults )* } } @@ -119,48 +147,68 @@ macro_rules! command { /// - Various key-value pairs, child elements, and text content. even for loops #[macro_export] macro_rules! parse_rsx_param { - ($elem:expr, ) => {}; + // Properties + ($elem:expr, $($k:ident).+: $v:expr) => { + $elem.$($k).+ = $v; + }; - ($elem:expr, for ($item:ident in $expr:expr) {$($inner:tt)*} $($rest:tt)*) => { - for $item in $expr { + ($elem:expr, $($k:ident).+: $v:expr, $($rest:tt)*) => { + $elem.$($k).+ = $v; + osui::parse_rsx_param!($elem, $($rest)*); + }; + + // for completion purposes + ($elem:expr, $p:path) => { + $p; + }; + ($elem:expr, $($k:ident).+., $($rest:tt)*) => { + $elem.$($k)+.; + osui::parse_rsx_param!($elem, $($rest)*); + }; + + ($elem:expr, $($k:ident).+.: $v:expr) => { + $elem.$($k).+. = $v; + }; + ($elem:expr, $($k:ident).+.: $v:expr, $($rest:tt)*) => { + $elem.$($k).+. = $v; + osui::parse_rsx_param!($elem, $($rest)*); + }; + + ($elem:expr, $($k:ident).+:$($rest:tt)*) => { + $elem.$($k).+ = (); + }; + ($elem:expr, $($k:ident).+:$($rest:tt)*) => { + $elem.$($k).+ = $v; + osui::parse_rsx_param!($elem, $($rest)*); + }; + + // For loop + ($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => { + for $($for)* { $elem.children.push({$($inner)*}) } osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, $($k:ident: $v:expr),*) => { - $( - $elem.$k = $v; - )* - }; - - ($elem:expr, $($k:ident: $v:expr),*; $($rest:tt)*) => { - $( - $elem.$k = $v; - )* - osui::parse_rsx_param!($elem, $($rest)*); - }; - + // Expression ($elem:expr, {$ielem:expr} $($rest:tt)*) => { $elem.children.push($ielem); osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, $($k:ident: $v:expr),*, $text:expr) => { - $( - $elem.$k = $v; - )* - $elem.text = format!($text); - }; - + // Element ($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => { $elem.children.push(osui::rsx_elem!($pelem { $($inner)* })); osui::parse_rsx_param!($elem, $($rest)*) }; + // Text ($elem:expr, $text:expr) => { $elem.text = format!($text); }; + + // Empty + ($elem:expr, ) => {}; } /// Macro for creating an OSUI element with parsed parameters. @@ -174,7 +222,7 @@ macro_rules! parse_rsx_param { /// } /// } /// ``` -/// +/// /// # For loops /// ``` /// rsx! { @@ -190,6 +238,7 @@ macro_rules! parse_rsx_param { #[macro_export] macro_rules! rsx_elem { ($elem:path { $($inner:tt)* }) => {{ + #[allow(unused_mut)] let mut elem = $elem(); osui::parse_rsx_param!(elem, $($inner)*); elem as Box @@ -236,4 +285,11 @@ macro_rules! css { ..Default::default() } }}; -} \ No newline at end of file +} + +#[macro_export] +macro_rules! arc { + ($a:expr) => { + std::sync::Arc::new(std::sync::Mutex::new($a)) + }; +} diff --git a/src/main.rs b/src/main.rs index 38e045b..4d91ff0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,6 @@ -use osui::{rsx, ui::*}; +use osui::rsx; fn main() { - osui::app::run(&mut app()); -} - -fn app() -> Box { - rsx! { - text { "Hello, World!" } - } -} + rsx! {}; + // osui::run_test() +} \ No newline at end of file diff --git a/src/ui/elements.rs b/src/ui/elements.rs index faf9d4f..242e9ab 100644 --- a/src/ui/elements.rs +++ b/src/ui/elements.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; +use std::{collections::HashMap, sync::{Arc, Mutex}}; use crate::{ command, element, key::Key, render_to_frame, ui::Style, Command, Direction, Element, - ElementData, ElementSize, UpdateResponse, + ElementData, Value, UpdateResponse, }; element! { @@ -15,6 +15,7 @@ element! { self.text.clone() } } + element! { /// A clickable button element. /// @@ -22,11 +23,13 @@ element! { /// based on its interaction state, such as being "clicked". Button { /// A callback function executed when the button is clicked. - on_click: fn() + pub on_click: Arc>, + pub style: Style, } defaults { - on_click: || {} + on_click: Arc::new(Mutex::new(|_btn: &mut Button<'_>| {})), + style: Style::default(), } fn render(&self, state: usize) -> String { @@ -39,7 +42,11 @@ element! { fn event(&mut self, _state: usize, k: Key) -> UpdateResponse { if k == Key::Enter { - (self.on_click)(); + let mut btn = self.clone(); + let mut on_click = self.on_click.lock().unwrap(); + (on_click)(&mut btn); + drop(on_click); + *self = btn; return command!( Command::Render(2), Command::Sleep(120) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 70509a4..6518bd9 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -14,7 +14,7 @@ pub use elements::*; /// # Returns /// /// A `Box` containing a new `Text` element instance. -pub fn text() -> Box { +pub fn text<'a>() -> Box> { Box::new(Text::new()) } @@ -26,7 +26,7 @@ pub fn text() -> Box { /// # Returns /// /// A `Box