diff --git a/Cargo.toml b/Cargo.toml index 223a066..9561a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,6 @@ crossterm = "0.28.1" regex = "1.11.1" lazy_static = "1.4" paste = "1.0.15" -osui-element = "0.1.8" dyn-clone = "1.0.17" flume = "=0.10.12" +osui-element = "0.1.11" diff --git a/src/app.rs b/src/app.rs index 41f421e..89d288f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,7 +1,4 @@ -use crossterm::{ - event::Event, - terminal::{disable_raw_mode, enable_raw_mode}, -}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use crate::prelude::*; use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame}; @@ -24,13 +21,6 @@ extern "C" { fn c_run(args: *mut c_void) -> bool; } -fn event(element: &mut Element, event: Event, document: &Document) { - if let crossterm::event::Event::Resize(width, height) = event { - element.update_data(width as usize, height as usize); - } - element.event(event, document); -} - pub fn run(element: &mut Element) -> bool { hide_cursor(); enable_raw_mode().unwrap(); @@ -67,16 +57,17 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { }; let (width, height) = get_term_size(); - args.element.update_data(width, height); - event( - args.element, - crossterm::event::Event::FocusGained, + args.element.event( + crossterm::event::Event::Resize(width as u16, height as u16), &document, ); + args.element + .event(crossterm::event::Event::FocusGained, &document); document.render(); while args.running || !ptr.is_null() { - event(args.element, crossterm::event::read().unwrap(), &document); + args.element + .event(crossterm::event::read().unwrap(), &document); document.render(); } null() as *const c_void @@ -100,7 +91,7 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { args.rtx .send(if let Some(e) = args.element.get_element_by_id(&id) { crate::CommandResult::Element(e as *mut Element) - } else if args.element.get_data().2 == id { + } else if args.element.get_id() == id { crate::CommandResult::Element(args.element as *mut Element) } else { crate::CommandResult::None diff --git a/src/examples.rs b/src/examples.rs index 0bfd708..28e1910 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -3,8 +3,16 @@ use crate::prelude::*; pub fn examples() -> Element { rsx! { styling: Some(styling()), - if (2 == 2) { - rsx_elem! { button { class: "btn", "Hello, World!" } } + button { class: "btn", on_click: Handler::new(|btn: &mut Button, _, document| { + let data = document.get_element_by_id::>("my_data").unwrap(); + data.data += "!"; + btn.children.set_text(&data.data); + }), "Hello, World!" } + + { + let mut data = dataholder::(); + data.id = "my_data"; + data } } } diff --git a/src/lib.rs b/src/lib.rs index ce09ac5..6252a67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,8 +42,8 @@ pub mod prelude { pub use crate::ui::Color::*; pub use crate::ui::Number::*; pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; - pub use crate::{call, Children, ElementCore, ElementWidget}; - pub use crate::{style, Command, Document, Element, Value}; + pub use crate::{call, Children, ElementCore, ElementWidget, Element}; + pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); @@ -60,17 +60,14 @@ pub type Element = Box; /// Traits ////////////////////////////////////////////////////////////////////////////////////////////////// -pub trait ElementCore: Send + Sync { - fn get_data(&self) -> (Value, usize, String); - fn update_data(&mut self, width: usize, height: usize); +pub trait ElementCore { fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>; - fn get_child(&mut self) -> Option<&mut Element>; fn set_styling(&mut self, styling: &HashMap); + fn get_id(&self) -> String; } -pub trait ElementWidget: ElementCore + std::fmt::Debug { - fn render(&self, focused: bool) -> RenderResult; - +pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync { + fn render(&self, focused: bool) -> Option; fn event(&mut self, event: Event, document: &Document) { _ = (event, document) } @@ -245,6 +242,13 @@ impl Children { children.push(element); } } + pub fn get_child(&mut self) -> Option<&mut Element> { + if let Children::Children(children, child) = self { + children.get_mut(*child) + } else { + None + } + } } impl Handler { diff --git a/src/rsx.rs b/src/rsx.rs index 0e261af..ce3f97b 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -9,13 +9,6 @@ macro_rules! parse_rsx_param { } osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, if ($($if:tt)*) $code:block $($rest:tt)*) => { - if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)} - if let $crate::Children::Children(children, _) = &mut $elem.children { - if $($if)* {children.push($code)} - } - osui::parse_rsx_param!($elem, $($rest)*); - }; ($elem:expr, $($k:ident).+: $v:expr) => { $elem.$($k).+ = $v; @@ -46,10 +39,10 @@ macro_rules! parse_rsx_param { osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, {$inner_elem:expr} $($rest:tt)*) => { + ($elem:expr, $code:block $($rest:tt)*) => { if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)} if let $crate::Children::Children(children, _) = &mut $elem.children { - children.push({$inner_elem}); + children.push($code); } osui::parse_rsx_param!($elem, $($rest)*); }; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 2f4daf1..23b8a79 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -3,9 +3,10 @@ /// # Modules /// - `styles`: A module for defining UI styles. pub mod styles; + pub use styles::*; -use crate::{prelude::*, RenderResult, RenderWriter}; +use crate::prelude::*; use osui_element::{elem_fn, element}; /// The `Text` element represents a piece of static text in the terminal UI. @@ -31,7 +32,7 @@ pub struct Text { } impl ElementWidget for Text<'_> { - fn render(&self, focused: bool) -> RenderResult { + fn render(&self, focused: bool) -> Option { let mut writer = RenderWriter::new(focused, self.style.clone()); writer.write(&{ @@ -42,7 +43,7 @@ impl ElementWidget for Text<'_> { } }); - writer.result() + Some(writer.result()) } fn event(&mut self, event: Event, document: &Document) { call!(self.on_event(event, document)); @@ -77,7 +78,7 @@ pub struct Div { } impl ElementWidget for Div<'_> { - fn render(&self, focused: bool) -> RenderResult { + fn render(&self, focused: bool) -> Option { let mut writer = RenderWriter::new(focused, self.style.clone()); let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12); @@ -88,7 +89,7 @@ impl ElementWidget for Div<'_> { } writer.write(&frame.output()); - writer.result() + Some(writer.result()) } fn event(&mut self, event: Event, document: &Document) { @@ -97,48 +98,8 @@ impl ElementWidget for Div<'_> { 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, document); - } - *child - } - } - } - } - _ => { - if let Some(child) = self.get_child() { - child.event(event, document); - } - } + if let Some(child) = self.children.get_child() { + child.event(event, document); } } } @@ -151,10 +112,10 @@ pub struct Button { } impl ElementWidget for Button<'_> { - fn render(&self, focused: bool) -> RenderResult { + fn render(&self, focused: bool) -> Option { let mut writer = RenderWriter::new(focused, self.style.clone()); writer.write(&self.children.get_text()); - writer.result() + Some(writer.result()) } fn event(&mut self, event: Event, document: &Document) { @@ -173,3 +134,25 @@ impl ElementWidget for Button<'_> { } } } + +#[element] +#[derive(Debug)] +pub struct DataHolder<'a, T> { + pub data: T, +} + +pub fn dataholder<'a, T: std::default::Default>() -> Box> { + Box::new(DataHolder { + style: Style::default(), + data: T::default(), + children: Children::None, + class: "", + id: "", + }) +} + +impl<'a, T: std::fmt::Debug + Send + Sync> ElementWidget for DataHolder<'a, T> { + fn render(&self, _: bool) -> Option { + None + } +} diff --git a/src/utils.rs b/src/utils.rs index eb29c27..ef3df37 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,7 @@ /// let frame = create_frame(50, 10); /// render_to_frame(crate::State::Normal, 50, &mut frame, &text_element); /// ``` -use crate::{Element, Value}; +use crate::Element; use lazy_static::lazy_static; use regex::Regex; use std::{ @@ -99,60 +99,60 @@ impl Frame { } } pub fn render(&mut self, focused: bool, element: &Element) { - // let data = element.get_data(); - let output = element.render(focused); - let x; - let mut y = 0; - match output.1.y { - crate::ui::Number::Px(_y) => { - y = _y; - } - crate::ui::Number::Pe(p) => y = (self.frame.len() * p) / 100, - crate::ui::Number::Center => { - y = (self.frame.len() - output.0.matches('\n').count() + 1) / 2 - } - crate::ui::Number::Auto => { - for (i, n) in self.used.iter().enumerate() { - if *n == 0 { - y = i; - break; + if let Some(output) = element.render(focused) { + let x; + let mut y = 0; + match output.1.y { + crate::ui::Number::Px(_y) => { + y = _y; + } + crate::ui::Number::Pe(p) => y = (self.frame.len() * p) / 100, + crate::ui::Number::Center => { + y = (self.frame.len() - output.0.matches('\n').count() + 1) / 2 + } + crate::ui::Number::Auto => { + for (i, n) in self.used.iter().enumerate() { + if *n == 0 { + y = i; + break; + } + } + } + crate::ui::Number::Default => { + for (i, n) in self.used.iter().enumerate() { + if *n == 0 { + y = i; + break; + } } } } - crate::ui::Number::Default => { - for (i, n) in self.used.iter().enumerate() { - if *n == 0 { - y = i; - break; - } + match output.1.x { + crate::ui::Number::Px(_x) => { + x = _x; + } + crate::ui::Number::Pe(p) => x = (self.width * p) / 100, + crate::ui::Number::Center => { + x = (self.width + - ANSI + .replace_all(&output.0.lines().next().unwrap_or(""), "") + .len()) + / 2; + } + crate::ui::Number::Auto => { + x = *self.used.get(y).unwrap(); + } + crate::ui::Number::Default => { + x = 0; } } - } - match output.1.x { - crate::ui::Number::Px(_x) => { - x = _x; - } - crate::ui::Number::Pe(p) => x = (self.width * p) / 100, - crate::ui::Number::Center => { - x = (self.width - - ANSI - .replace_all(&output.0.lines().next().unwrap_or(""), "") - .len()) - / 2; - } - crate::ui::Number::Auto => { - x = *self.used.get(y).unwrap(); - } - crate::ui::Number::Default => { - x = 0; - } - } - for (i, line) in output.0.split('\n').enumerate() { - if self.frame.len() > y + i { - let frame_line = self.frame.get_mut(y + i).unwrap(); - let merged = merge_line(&frame_line, line, x); - *frame_line = merged.0; - *self.used.get_mut(y + i).unwrap() += merged.1.len() - 1; + for (i, line) in output.0.split('\n').enumerate() { + if self.frame.len() > y + i { + let frame_line = self.frame.get_mut(y + i).unwrap(); + let merged = merge_line(&frame_line, line, x); + *frame_line = merged.0; + *self.used.get_mut(y + i).unwrap() += merged.1.len() - 1; + } } } } @@ -183,53 +183,6 @@ pub fn flush() { stdout().flush().unwrap(); } -#[derive(Debug, Clone)] -pub enum Direction { - Left, - Right, - Up, - Down, -} - -pub fn closest_component( - components: &[Element], - current_index: usize, - direction: Direction, -) -> usize { - let current = &components[current_index].get_data(); - - components - .iter() - .enumerate() - .filter(|(i, comp_)| { - let comp = comp_.get_data(); - match direction { - Direction::Left => match comp.0 { - Value::Custom(_) => { - comp.0.get_value() < current.0.get_value() && comp.1 == current.1 - } - Value::Default(_) => *i < current_index, - }, // Left - Direction::Right => match comp.0 { - Value::Custom(_) => { - comp.0.get_value() > current.0.get_value() && comp.1 == current.1 - } - Value::Default(_) => *i > current_index, - }, // Right - Direction::Up => comp.1 < current.1 && comp.0.get_value() == current.0.get_value(), // Up - Direction::Down => { - comp.1 > current.1 && comp.0.get_value() == current.0.get_value() - } // Down - } - }) - .min_by_key(|(_, comp_)| { - let comp = comp_.get_data(); - current.0.get_value().abs_diff(comp.0.get_value()) + current.1.abs_diff(comp.1) - }) // Find the closest component - .map(|(index, _)| index) // Return the index of the closest component - .unwrap_or(current_index) // If no component is found, return the current index -} - pub fn get_term_size() -> (usize, usize) { let (width, height) = crossterm::terminal::size().unwrap(); (width as usize, height as usize)