From e1da609eb186a8c4c400e7f30ba4423ce7b738b0 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Tue, 12 Nov 2024 15:59:27 +0100 Subject: [PATCH] added rebuild and Arc insted of Box --- src/app.rs | 18 +++++-- src/lib.rs | 26 ++++++---- src/macros.rs | 119 +++++++++++++++++++++++++-------------------- src/main.rs | 1 - src/test.rs | 19 ++++++-- src/ui/elements.rs | 37 ++++++++++++-- src/ui/mod.rs | 8 ++- src/utils.rs | 4 +- 8 files changed, 154 insertions(+), 78 deletions(-) diff --git a/src/app.rs b/src/app.rs index c1589ad..8dd7e82 100644 --- a/src/app.rs +++ b/src/app.rs @@ -29,17 +29,21 @@ macro_rules! render { }; } -pub fn run(elem: Box) { +pub fn run(element: Arc) -> bool { hide_cursor(); crossterm::terminal::enable_raw_mode().unwrap(); clear(); let (tx, rx) = mpsc::channel(); - let element = Arc::new(elem); + + let (exit_tx, exit_) = mpsc::channel::<()>(); thread::spawn({ let element_thread = Arc::clone(&element); move || loop { + if exit_.try_recv().is_ok() { + break; + } let event = crossterm::event::read().unwrap(); let e = Arc::clone(&element_thread); let command_handler = crate::CommandHandler(tx.clone()); @@ -57,11 +61,19 @@ pub fn run(elem: Box) { crate::Command::SetState(state) => { current_state = state; } - crate::Command::Exit => break, + crate::Command::Exit => { + exit_tx.send(()).unwrap(); + break; + } + crate::Command::Rebuild => { + exit_tx.send(()).unwrap(); + return true; + } } } else { render!(&element, current_state.clone()); } thread::sleep(std::time::Duration::from_millis(20)); } + false } diff --git a/src/lib.rs b/src/lib.rs index 9d243bb..716e884 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ pub mod macros; pub mod ui; pub mod utils; -use std::sync::mpsc; +use std::sync::{mpsc, Arc, Mutex}; pub use utils::*; pub mod app; @@ -81,9 +81,9 @@ impl Value { pub trait Element: Send + Sync { fn get_data(&self) -> (Value, usize, String); - fn update_data(&mut self, width: usize, height: usize); + fn update_data(&self, width: usize, height: usize); - fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box>; + fn get_element_by_id(&self, id: &str) -> Option>; fn render(&self, state: State) -> String { _ = state; @@ -95,16 +95,14 @@ pub trait Element: Send + Sync { } } -pub struct Handler( - pub std::sync::Mutex>, -); +pub struct Handler(pub Box); impl Handler { pub fn new(handler_fn: F) -> Handler where F: FnMut(&T, &CommandHandler, crossterm::event::Event) + 'static + Send, { - Handler(std::sync::Mutex::new(Box::new(handler_fn))) + Handler(Box::new(handler_fn)) } } @@ -118,12 +116,21 @@ impl CommandHandler { pub fn exit(&self) { self.0.send(Command::Exit).unwrap(); } + + pub fn get_element_by_id(&self) { + self.0.send(Command::Exit).unwrap(); + } + + pub fn rebuild(&self) -> bool { + self.0.send(Command::Rebuild).is_ok() + } } #[derive(Debug, Clone)] pub enum Command { SetState(State), Exit, + Rebuild, } #[derive(Debug, Clone)] @@ -135,5 +142,6 @@ pub enum State { } pub fn run_test() { - app::run(test::app()); -} \ No newline at end of file + let i = Arc::new(Mutex::new(0)); + while app::run(test::app(Arc::clone(&i))) {} +} diff --git a/src/macros.rs b/src/macros.rs index f431dfb..ce7e16e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -85,34 +85,35 @@ macro_rules! element { ) => { $(#[$meta])* pub struct $name<'a> { - pub x: $crate::Value, - pub y: usize, - pub width: $crate::Value, - pub height: $crate::Value, - pub children: Vec>, - pub child: usize, - pub text: String, - pub id: &'a str, + pub x: std::sync::Mutex<$crate::Value>, + pub y: std::sync::Mutex, + pub width: std::sync::Mutex<$crate::Value>, + pub height: std::sync::Mutex<$crate::Value>, + pub children: std::sync::Mutex>>, + pub child: std::sync::Mutex, + pub text: std::sync::Mutex, + pub id: std::sync::Mutex<&'a str>, $( $inner )* } impl $crate::Element for $name<'_> { fn get_data(&self) -> ($crate::Value, usize, String) { - (self.x.clone(), self.y.clone(), self.id.to_string()) + (self.x.lock().unwrap().clone(), self.y.lock().unwrap().clone(), self.id.lock().unwrap().to_string()) } - fn update_data(&mut self, width: usize, height: usize) { - self.width.try_set_value(width); - self.height.try_set_value(height); - for child in &mut self.children { + fn update_data(&self, width: usize, height: usize) { + self.width.lock().unwrap().try_set_value(width); + self.height.lock().unwrap().try_set_value(height); + let children = self.children.lock().unwrap(); + for child in children.iter() { child.update_data(width, height); } } - fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box> { - for elem in &mut self.children { + fn get_element_by_id(&self, id: &str) -> Option> { + for elem in &mut self.children.lock().unwrap().iter() { if elem.get_data().2 == id { - return Some(elem); + return Some(std::sync::Arc::clone(elem)); } } None @@ -124,7 +125,7 @@ macro_rules! element { impl<'a> $name<'_> { /// Creates a new instance of the element with default values. pub fn new() -> $name<'a> { - $name { + $crate::def_!($name { children: Vec::new(), x: $crate::Value::Default(0), y: 0, @@ -134,20 +135,32 @@ macro_rules! element { text: String::new(), id: "", $( $defaults )* - } + }) } - /// Retrieves a mutable reference to the current child element, if any. - /// - /// # Returns - /// An `Option` containing a mutable reference to the child `Element` or `None`. - pub fn get_child(&mut self) -> Option<&mut Box> { - self.children.get_mut(self.child) + pub fn get_child(&self) -> Option> { + if let Ok(elements) = self.children.lock() { + if let Some(e) = elements.get(*self.child.lock().unwrap()) { + return Some(std::sync::Arc::clone(e)); + } + } + None } } }; } +#[macro_export] +macro_rules! def_ { + ($name:ident { $($k:ident : $v:expr),* $(,)? }) => { + $name { + $( + $k : std::sync::Mutex::new($v) + ),* + } + }; +} + /// Macro for creating a command response from a list of commands. /// /// # Example @@ -175,63 +188,63 @@ macro_rules! command { #[macro_export] macro_rules! parse_rsx_param { // Properties - ($elem:expr, $($k:ident).+: $v:expr) => { - $elem.$($k).+ = $v; + ($elem:expr, $a:ident $($k:ident).+: $v:expr) => { + $elem.$a.lock().unwrap().$($k).+ = $v; }; - ($elem:expr, $($k:ident).+: $v:expr, $($rest:tt)*) => { - $elem.$($k).+ = $v; + ($elem:expr, $a:ident $(.$($k:ident).+)?: $v:expr, $($rest:tt)*) => { + *$elem.$a.lock().unwrap()$($($k).+)? = $v; $crate::parse_rsx_param!($elem, $($rest)*); }; // for completion purposes - ($elem:expr, $p:path) => { - $p; - }; - ($elem:expr, $($k:ident).+, $($rest:tt)*) => { - $elem.$($k).+; - $crate::parse_rsx_param!($elem, $($rest)*); - }; - ($elem:expr, $($k:ident).+., $($rest:tt)*) => { - $elem.$($k). - +.; - $crate::parse_rsx_param!($elem, $($rest)*); - }; + // ($elem:expr, $p:path) => { + // $p; + // }; + // ($elem:expr, $a:ident . $($k:ident).+, $($rest:tt)*) => { + // $elem.$a.lock().unwrap().$($k).+; + // $crate::parse_rsx_param!($elem, $($rest)*); + // }; + // ($elem:expr, $a:ident . $($k:ident).+., $($rest:tt)*) => { + // $elem.$a.lock().unwrap().$($k). + // +.; + // $crate::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; - $crate::parse_rsx_param!($elem, $($rest)*); - }; + // ($elem:expr, $a:ident . $($k:ident).+.: $v:expr) => { + // $elem.$a.lock().unwrap().$($k).+. = $v; + // }; + // ($elem:expr, $a:ident . $($k:ident).*: $v:expr, $($rest:tt)*) => { + // $elem.$a.lock().unwrap().$($k).+. = $v; + // $crate::parse_rsx_param!($elem, $($rest)*); + // }; // For loop ($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => { for $($for)* { - $elem.children.push({$($inner)*}) + $elem.children.lock().unwrap().push({$($inner)*}) } $crate::parse_rsx_param!($elem, $($rest)*); }; // Expression ($elem:expr, {$ielem:expr} $($rest:tt)*) => { - $elem.children.push($ielem); + $elem.children.lock().unwrap().push($ielem); $crate::parse_rsx_param!($elem, $($rest)*); }; // Element ($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => { - $elem.children.push($crate::rsx_elem!($pelem { $($inner)* })); + $elem.children.lock().unwrap().push($crate::rsx_elem!($pelem { $($inner)* })); $crate::parse_rsx_param!($elem, $($rest)*) }; // Text ($elem:expr, $text:expr) => { - $elem.text = format!($text); + *$elem.text.lock().unwrap() = format!($text); }; ($elem:expr, $text:expr, $($inner:tt)*) => { - $elem.text = format!($text, $($inner)*); + *$elem.text.lock().unwrap() = format!($text, $($inner)*); }; // Empty @@ -268,7 +281,7 @@ macro_rules! rsx_elem { #[allow(unused_mut)] let mut elem = $elem(); $crate::parse_rsx_param!(elem, $($inner)*); - elem as Box + elem as std::sync::Arc }}; } diff --git a/src/main.rs b/src/main.rs index d720597..c458a20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use osui::run_test; - fn main() { run_test(); } diff --git a/src/test.rs b/src/test.rs index f808f50..d619cc1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,5 +1,16 @@ -use crate::{rsx_elem, Element, ui::*}; +use std::sync::{Arc, Mutex}; -pub(crate) fn app() -> Box { - rsx_elem! { text { "Hello, World!" } } -} \ No newline at end of file +use crate::{rsx_elem, ui::*, Element, Handler}; + +pub(crate) fn app(i: Arc>) -> std::sync::Arc { + let i1 = Arc::clone(&i); + rsx_elem! { text { on_click: Handler::new(move |t: &Text, ch, _| { + *i1.lock().unwrap() += 1; + ch.rebuild(); + // { + // let mut i = i1.lock().unwrap(); + // *i += 1; + // *t.text.lock().unwrap() = i.to_string(); + // } + }), "{}", i.lock().unwrap() } } +} diff --git a/src/ui/elements.rs b/src/ui/elements.rs index 9313fe7..500dcb9 100644 --- a/src/ui/elements.rs +++ b/src/ui/elements.rs @@ -1,9 +1,38 @@ -use crate::{element, State}; +use std::sync::Mutex; + +use crate::{element, Handler, State}; element! { - Text {} - defaults {} + Text { + pub on_click: Mutex>> + } + defaults { + on_click: Handler::new(|_, _, _| {}) + } fn render(&self, _: State) -> String { - self.text.clone() + self.text.lock().unwrap().clone() + } + + fn event(&self, ch: &crate::CommandHandler, event: crossterm::event::Event) { + (*self.on_click.lock().unwrap().0)(self, ch, event); + // *self.text.lock().unwrap() = "Done!".to_string(); + } +} + +element! { + Div {} + defaults {} + fn render(&self, s: State) -> String { + if let Some(child) = self.get_child() { + child.render(s) + } else { + format!("None") + } + } + + fn event(&self, ch: &crate::CommandHandler, event: crossterm::event::Event) { + if let Some(e) = self.get_element_by_id("test") { + e.event(ch, event); + } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 5278ae0..c218482 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -7,6 +7,10 @@ pub use styles::*; pub mod elements; pub use elements::*; -pub fn text<'a>() -> Box> { - Box::new(Text::new()) +pub fn text<'a>() -> std::sync::Arc> { + std::sync::Arc::new(Text::new()) +} + +pub fn div<'a>() -> std::sync::Arc> { + std::sync::Arc::new(Div::new()) } \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index f6d8df3..2b2c85a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -78,7 +78,7 @@ pub fn render_to_frame( state: crate::State, width: usize, frame: &mut Vec, - element: &Box, + element: &std::sync::Arc, ) { let data = element.get_data(); for (i, line) in element.render(state).split('\n').enumerate() { @@ -130,7 +130,7 @@ pub enum Direction { } pub fn closest_component( - components: &[Box], + components: &[std::sync::Arc], current_index: usize, direction: Direction, ) -> usize {