diff --git a/src/examples.rs b/src/examples.rs index 709242f..d68f82c 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -1,55 +1,31 @@ use crate::prelude::*; -pub fn examples() -> Element { - rsx! { - styling: Some(styling()), - - button { class: "btn", on_click: fn(btn: &mut Button, _, document) { - let count = document.use_state::("count"); - *count += 1; - btn.children.set_text(&count.to_string()); - if *count == 10 { - document.exit(); - } - }, "Click me!" } - - text { "Welcome!" } - - @count: i32; - } -} - pub fn todo_app() -> Element { + let count = State::new(0); + rsx! { styling: Some(styling()), - class: "root", - div { - class: "tab", - text { class: "title", "not started" } - text { class: "todo", "TODO: Something" } - } - div { - class: "tab", - text { class: "title", "completed" } - } + + button { on_click: fn(_, _, _) @count { + let mut count = count.use_state(); + *count += 1; + }, class: "btn", "Count: {count}" } } } pub fn styling() -> Css { css! { - "tab" { - x: Auto, - width: 50%, - outline: true, - } "title" { - width: Auto, - color: Red, - } - "todo" { - x: 0 px, - y: Auto, color: Green, + x: Center, + } + + "container" { + width: 50%, + } + + "btn": "clicked" { + color: Red } } } diff --git a/src/lib.rs b/src/lib.rs index c3ec52a..d977a6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,10 +37,10 @@ pub mod utils; pub mod prelude { pub use crate::ui::Color::*; pub use crate::ui::Font::*; - pub use crate::ui::Number::*; + pub use crate::ui::Number::{Auto, Center}; pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler}; pub use crate::{call, Children, Element, ElementCore, ElementWidget}; - pub use crate::{style, Document}; + pub use crate::{style, Document, State}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); @@ -88,19 +88,29 @@ pub struct Writer { focused: bool, size: (u16, u16), absolute: (u16, u16), + written: (u16, u16), } ////////////////////////////////////////////////////////////////////////////////////////////////// /// Enums ////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Debug)] pub enum Children { None, - Text(String), + Text(Arc String>), Children(Vec, usize), } +impl std::fmt::Debug for Children { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&match self { + Self::None => String::new(), + Self::Children(a, b) => format!("({b}, {a:?})"), + Self::Text(a) => format!("{:?}", a()), + }) + } +} + ////////////////////////////////////////////////////////////////////////////////////////////////// /// Implementations ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -152,12 +162,6 @@ impl Document { pub fn draw(&mut self, element: &mut Element) { self.element = element; } - pub fn use_state(&self, name: &str) -> &mut T { - &mut self - .get_element_by_id::>(name) - .unwrap() - .data - } } impl Default for Children { @@ -175,31 +179,25 @@ impl Children { } pub fn get_text(&self) -> String { match self { - Children::Text(text) => text.clone(), + Children::Text(text) => text(), _ => String::new(), } } - pub fn get_text_mut(&mut self) -> Option<&mut String> { - match self { - Children::Text(text) => Some(text), - _ => None, - } - } - pub fn set_text(&mut self, text: &str) { + pub fn set_text(&mut self, text: Arc String>) { match self { Children::Text(t) => { - *t = text.to_string(); + *t = text; } _ => {} } } - pub fn set_text_force(&mut self, text: &str) { + pub fn set_text_force(&mut self, text: Arc String>) { match self { Children::Text(t) => { - *t = text.to_string(); + *t = text; } _ => { - *self = Children::Text(text.to_string()); + *self = Children::Text(text); } } } @@ -238,6 +236,7 @@ impl Writer { focused, size, absolute, + written: (0, 1), } } @@ -249,6 +248,11 @@ impl Writer { self.absolute.0 + 1, self.style.write(line) ); + let line_len = line.len() as u16; + if line_len > self.written.0 { + self.written.0 = line_len; + } + self.written.1 += 1; } } @@ -258,8 +262,23 @@ impl Writer { pub fn get_size(&self) -> (u16, u16) { ( - self.style.width.as_size(self.size.0, self.style.outline), - self.style.height.as_size(self.size.1, self.style.outline), + self.style + .width + .as_size(self.size.0, self.size.0, self.style.outline), + self.style + .height + .as_size(self.size.1, self.size.1, self.style.outline), + ) + } + + pub fn get_size_root(&self) -> (u16, u16) { + ( + self.style + .width + .as_size(self.written.0, self.size.0, self.style.outline), + self.style + .height + .as_size(self.written.1, self.size.1, self.style.outline), ) } @@ -306,3 +325,26 @@ pub fn run(element: &mut Element) -> bool { unsafe { (*document.running).unwrap() } } + +pub struct State(Arc>); +impl State { + pub fn new<'a>(v: T) -> State { + State(Arc::new(Mutex::new(v))) + } + pub fn use_state<'a>(&'a self) -> std::sync::MutexGuard<'a, T> { + self.0.lock().unwrap() + } + pub fn copy_state<'a>(&'a self) -> State { + State(Arc::clone(&self.0)) + } +} + +impl std::fmt::Display for State { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Acquire the lock, handle potential poisoning + match self.0.lock() { + Ok(guard) => write!(f, "{}", *guard), + Err(poisoned) => write!(f, "", *poisoned.into_inner()), + } + } +} diff --git a/src/main.rs b/src/main.rs index 6c3666c..5cd4e91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use osui::{examples::todo_app, prelude::*}; +use osui::{examples::todo_app, launch}; fn main() { - launch!(todo_app()) + launch!(todo_app()); } diff --git a/src/rsx.rs b/src/rsx.rs index 3e2d64f..cea619f 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -12,17 +12,6 @@ macro_rules! check_expr { } #[macro_export] macro_rules! parse_rsx_param { - // Data fields - ($elem:expr, @$name:ident: $type:ty; $($rest:tt)*) => { - if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)} - if let $crate::Children::Children(children, _) = &mut $elem.children { - let mut $name = $crate::ui::data_holder::<$type>(); - $name.id = stringify!($name); - children.push($name); - } - osui::parse_rsx_param!($elem, $($rest)*) - }; - ($elem:expr, for ($($for: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 { @@ -34,7 +23,17 @@ macro_rules! parse_rsx_param { }; ($elem:expr, $($k:ident).+: fn($($params:tt)*) $code:block $(, $($rest:tt)*)?) => { - $elem.$($k).+ = $crate::Handler::new(|$($params)*| $code); + $elem.$($k).+ = $crate::Handler::new(move |$($params)*| $code); + osui::parse_rsx_param!($elem, $($($rest)*)?); + }; + + ($elem:expr, $($k:ident).+: fn($($params:tt)*) @$($v:ident),* $code:block $(, $($rest:tt)*)?) => { + $elem.$($k).+ = $crate::Handler::new({ + $( + let $v = $v.copy_state(); + )* + move |$($params)*| $code + }); osui::parse_rsx_param!($elem, $($($rest)*)?); }; @@ -78,16 +77,8 @@ macro_rules! parse_rsx_param { osui::parse_rsx_param!($elem, $($rest)*) }; - ($elem:expr, $elem_path:path as $t:ty { $($inner:tt)* } $($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(osui::ersx!($elem_path as $t { $($inner)* })); - } - osui::parse_rsx_param!($elem, $($rest)*) - }; - ($elem:expr, $text:expr) => { - $elem.children.set_text_force(&format!($text)) + $elem.children.set_text_force(std::sync::Arc::new(move || format!($text))) }; ($elem:expr, $text:expr, $($inner:tt)*) => { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index f14999e..906d612 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -18,9 +18,7 @@ pub struct Text { impl ElementWidget for Text<'_> { fn render(&self, writer: &mut crate::Writer) { - if let Children::Text(text) = &self.children { - writer.write(&text); - } + writer.write(&self.children.get_text()); } fn event(&mut self, event: Event, document: &Document) { call!(self.on_event(event, document)); @@ -77,7 +75,7 @@ impl ElementWidget for Button<'_> { self.style.set_state("clicked"); call!(self.on_click(event, document)); document.render(); - sleep(100); + sleep(65); self.style.set_state(""); } } @@ -85,23 +83,3 @@ impl ElementWidget for Button<'_> { } } } - -#[element] -#[derive(Debug)] -pub struct DataHolder<'a, T> { - pub data: T, -} - -pub fn data_holder<'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, _writer: &mut crate::Writer) {} -} diff --git a/src/ui/styles.rs b/src/ui/styles.rs index a859043..4a5fb22 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -310,31 +310,16 @@ impl Number { _ => frame_size, } } - pub fn as_size(&self, frame_size: u16, outline: bool) -> u16 { - if outline { + pub fn as_size(&self, written: u16, frame_size: u16, outline: bool) -> u16 { + if *self == Number::Auto || *self == Number::Default { + written + } else if outline { self.as_size_raw(frame_size) - 2 } else { self.as_size_raw(frame_size) } } - pub fn as_position_y(&self, used: &Vec, frame_size: u16) -> u16 { - match self { - crate::ui::Number::Px(px) => *px, - crate::ui::Number::Pe(pe) => (frame_size * pe) / 100, - crate::ui::Number::Center => (frame_size) / 2, - crate::ui::Number::Auto | crate::ui::Number::Default => { - let mut y = 0; - for (i, n) in used.iter().enumerate() { - if *n == 0 { - y = i as u16; - break; - } - } - y - } - } - } - pub fn as_position_x(&self, used: &u16, frame_size: u16) -> u16 { + pub fn as_position(&self, used: &u16, frame_size: u16) -> u16 { match self { crate::ui::Number::Px(px) => *px, crate::ui::Number::Pe(pe) => (frame_size * pe) / 100, diff --git a/src/utils.rs b/src/utils.rs index 36fc994..495fcc1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,7 @@ use std::io::{stdout, Write}; +use crossterm::terminal::disable_raw_mode; + pub struct Frame { used: Vec, width: u16, @@ -21,9 +23,21 @@ impl Frame { pub fn render(&mut self, focused: bool, element: &crate::Element) { let style = element.get_style().get(focused); if style.visible { - let y = style.y.as_position_y(&self.used, self.height); + let y = style.y.as_position( + &{ + let mut y_ = 0; + for n in self.used.iter().rev() { + if *n == 0 { + break; + } + y_ += 1; + } + y_ + }, + self.height, + ); if let Some(y_) = self.used.get(y as usize) { - let x = style.x.as_position_x(y_, self.width); + let x = style.x.as_position(y_, self.width); let mut writer = crate::Writer::new( focused, style.clone(), @@ -31,9 +45,17 @@ impl Frame { (self.width, self.height), ); element.render(&mut writer); - let (width, height) = writer.get_size(); + let (width, height) = writer.get_size_root(); + if style.outline { + clear(); + show_cursor(); + disable_raw_mode().unwrap(); + panic!("the 'outline' option isn't available right now. please set 'outline' to 'false'") + } for i in y..y + height { - *self.used.get_mut(i as usize).unwrap() += x + width; + if let Some(o) = self.used.get_mut(i as usize) { + *o += x + width; + } } } }