From a75543ef605dbbdc0d8c7e44623d326f687dd4f8 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 20 Nov 2024 21:11:28 +0100 Subject: [PATCH 01/18] small changes --- src/app.rs | 1 + src/macros.rs | 94 ++++++--------------------------------------------- src/main.rs | 4 +-- 3 files changed, 14 insertions(+), 85 deletions(-) diff --git a/src/app.rs b/src/app.rs index 8e22e3f..86eafac 100644 --- a/src/app.rs +++ b/src/app.rs @@ -36,6 +36,7 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { let element = unsafe { &mut *(ptr as *mut Element) }; let (width, height) = get_term_size(); element.update_data(width, height); + event(element, crossterm::event::Event::FocusGained); loop { if ptr.is_null() { break; diff --git a/src/macros.rs b/src/macros.rs index 88c3966..54c5fea 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,48 +1,3 @@ -/// **parse_rsx_param!** - A macro for parsing and setting parameters on elements, allowing for dynamic handling of element properties and children. -/// -/// This macro enables flexible configuration of UI elements. It handles setting fields, adding children, invoking paths, and processing complex structures. -/// -/// ## Example -/// ```rust -/// parse_rsx_param!(elem, title: "Hello", for (i in 0..5) { }, text: "World"); -/// ``` -/// -/// ## Syntax -/// -/// **Set property or field of an element:** -/// ```rust -/// parse_rsx_param!($elem, $($k:ident).+: $v:expr); -/// ``` -/// -/// **Process multiple parameters:** -/// ```rust -/// parse_rsx_param!($elem, $($k:ident).+: $v:expr, $($rest:tt)*); -/// ``` -/// -/// **Handle path expression:** -/// ```rust -/// parse_rsx_param!($elem, $p:path); -/// ``` -/// -/// **Add nested children:** -/// ```rust -/// parse_rsx_param!($elem, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*); -/// ``` -/// -/// **Add child element directly:** -/// ```rust -/// parse_rsx_param!($elem, {$ielem:expr} $($rest:tt)*); -/// ``` -/// -/// **Set element text:** -/// ```rust -/// parse_rsx_param!($elem, $text:expr); -/// ``` -/// -/// **Handle empty case:** -/// ```rust -/// parse_rsx_param!($elem, ); -/// ``` #[macro_export] macro_rules! parse_rsx_param { ($elem:expr, $($k:ident).+: $v:expr) => { @@ -53,7 +8,6 @@ macro_rules! parse_rsx_param { $elem.$($k).+ = $v; osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, $p:path) => { $p; }; @@ -85,18 +39,18 @@ macro_rules! parse_rsx_param { osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, {$ielem:expr} $($rest:tt)*) => { + ($elem:expr, {$inner_elem:expr} $($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({$ielem}); + children.push({$inner_elem}); } osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => { + ($elem:expr, $elem_path:path { $($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::rsx_elem!($pelem { $($inner)* })); + children.push(osui::rsx_elem!($elem_path { $($inner)* })); } osui::parse_rsx_param!($elem, $($rest)*) }; @@ -111,19 +65,6 @@ macro_rules! parse_rsx_param { ($elem:expr, ) => {}; } -/// **rsx_elem!** - A macro to instantiate a UI element and apply parameters to it. -/// -/// This macro simplifies creating an element and applying a set of properties or children to it, enabling a declarative style of UI building. -/// -/// ## Example -/// ```rust -/// let element = rsx_elem!(ui::Button { text: "Click me" }); -/// ``` -/// -/// ## Syntax -/// ```rust -/// rsx_elem!($elem:path { $($inner:tt)* }) -/// ``` #[macro_export] macro_rules! rsx_elem { ($elem:path { $($inner:tt)* }) => {{ @@ -134,27 +75,14 @@ macro_rules! rsx_elem { }}; } -/// **rsx!** - A shorthand macro to create a `div` element and apply nested content to it. -/// -/// This macro is a simple wrapper for `rsx_elem!`, making it easy to create a `div` element and add children to it. -/// -/// ## Example -/// ```rust -/// rsx! { -/// text { "Hello, World" } -/// div { button {"Click me!"} } -/// } -/// ``` -/// -/// ## Syntax -/// ```rust -/// rsx!($($inner:tt)*) -/// ``` #[macro_export] macro_rules! rsx { ($($inner:tt)*) => {{ - $crate::rsx_elem!( $crate::ui::div { $($inner)* } ) + let mut elem = $crate::ui::div(); + $crate::parse_rsx_param!(elem, $($inner)* ); + elem }}; + } #[macro_export] @@ -219,7 +147,7 @@ macro_rules! _css { $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, .$name:ident::$kind:ident { $($inner:tt)* } $($rest:tt)* + $hm:expr, .$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); if let Some(style) = $hm.get_mut(&name) { @@ -261,7 +189,7 @@ macro_rules! _css { $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, #$name:ident::$kind:ident { $($inner:tt)* } $($rest:tt)* + $hm:expr, #$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); if let Some(style) = $hm.get_mut(&name) { @@ -303,7 +231,7 @@ macro_rules! _css { $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, $name:ident::$kind:ident { $($inner:tt)* } $($rest:tt)* + $hm:expr, $name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); if let Some(style) = $hm.get_mut(&name) { diff --git a/src/main.rs b/src/main.rs index 3e7931e..6b078c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,13 +6,13 @@ fn main() { fn app() -> Element { rsx! { styling: Some(styles()), - button { class: "btn", "This is a button!" } + button { class: "btn", id: "test", "This is a button!" } } } fn styles() -> Css { css! { - .btn::clicked { + .btn: clicked { color: Blue } } From 24eb831c5713ab36245b66df4b04214ac6314558 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 20 Nov 2024 23:37:57 +0100 Subject: [PATCH 02/18] starting to document the code --- src/css.rs | 171 ++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/macros.rs | 250 +------------------------------------------------- src/rsx.rs | 95 +++++++++++++++++++ 4 files changed, 269 insertions(+), 249 deletions(-) create mode 100644 src/css.rs create mode 100644 src/rsx.rs diff --git a/src/css.rs b/src/css.rs new file mode 100644 index 0000000..1ed1c97 --- /dev/null +++ b/src/css.rs @@ -0,0 +1,171 @@ +#[macro_export] +macro_rules! __css { + ($style:expr,)=>{}; + ( + $style:expr, ($name:ident): $value:expr + ) => {{ + $style.other.insert(stringify!($name).to_string(), Box::new($value)); + }}; + ( + $style:expr, ($name:ident): $value:expr, $($other:tt)* + ) => {{ + $style.other.insert(stringify!($name).to_string(), Box::new($value)); + $crate::__css!($($other)*); + }}; + ( + $style:expr, $name:ident: $value:expr + ) => {{ + $style.$name = $value; + }}; +} + +#[macro_export] +macro_rules! _css { + ($style:expr,) => {}; + ( + $hm:expr, .$name:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + $crate::__css!(style.0, $($inner)*); + } else { + let mut style = $crate::ui::Style::default(); + + $crate::__css!(style.0, $($inner)*); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; + ( + $hm:expr, .$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + } else { + let mut style = $crate::ui::Style::default(); + + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; + + + ( + $hm:expr, #$name:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + $crate::__css!(style.0, $($inner)*); + } else { + let mut style = $crate::ui::Style::default(); + + $crate::__css!(style.0, $($inner)*); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; + ( + $hm:expr, #$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + } else { + let mut style = $crate::ui::Style::default(); + + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; + + + ( + $hm:expr, $name:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + $crate::__css!(style.0, $($inner)*); + } else { + let mut style = $crate::ui::Style::default(); + + $crate::__css!(style.0, $($inner)*); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; + ( + $hm:expr, $name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* + ) => {{ + let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); + if let Some(style) = $hm.get_mut(&name) { + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + } else { + let mut style = $crate::ui::Style::default(); + + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + style.1.insert(stringify!($kind).to_string(), style_elem); + + $hm.insert( + name, + style, + ); + } + $crate::_css!($hm, $($rest)*); + }}; +} + +/// Creates a styling map `Css` +/// +/// # Examples +/// ``` +/// .btn: clicked { +/// color: Blue +/// } +/// ``` +/// +/// # Returns +/// `Css` +#[macro_export] +macro_rules! css { + ( + $($inner:tt)* + ) => {{ + let mut hm: $crate::ui::Css = std::collections::HashMap::new(); + $crate::_css!(hm, $($inner)*); + hm + }}; +} diff --git a/src/lib.rs b/src/lib.rs index 92b23b6..040d1da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub mod app; pub mod macros; pub mod ui; pub mod utils; +pub mod rsx; +pub mod css; pub use app::run; diff --git a/src/macros.rs b/src/macros.rs index 54c5fea..02ecc4c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,90 +1,3 @@ -#[macro_export] -macro_rules! parse_rsx_param { - ($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, $p:path) => { - $p; - }; - ($elem:expr, $($k:ident).+, $($rest:tt)*) => { - $elem.$($k).+; - osui::parse_rsx_param!($elem, $($rest)*); - }; - ($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, for ($($for:tt)*) { $($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 { - for $($for)* { - children.push({$($inner)*}) - } - } - osui::parse_rsx_param!($elem, $($rest)*); - }; - - ($elem:expr, {$inner_elem:expr} $($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}); - } - osui::parse_rsx_param!($elem, $($rest)*); - }; - - ($elem:expr, $elem_path:path { $($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::rsx_elem!($elem_path { $($inner)* })); - } - osui::parse_rsx_param!($elem, $($rest)*) - }; - - ($elem:expr, $text:expr) => { - if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text))} - }; - ($elem:expr, $text:expr, $($inner:tt)*) => { - if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text, $($inner)*))} - }; - - ($elem:expr, ) => {}; -} - -#[macro_export] -macro_rules! rsx_elem { - ($elem:path { $($inner:tt)* }) => {{ - #[allow(unused_mut)] - let mut elem = $elem(); - $crate::parse_rsx_param!(elem, $($inner)*); - elem as $crate::Element - }}; -} - -#[macro_export] -macro_rules! rsx { - ($($inner:tt)*) => {{ - let mut elem = $crate::ui::div(); - $crate::parse_rsx_param!(elem, $($inner)* ); - elem - }}; - -} - #[macro_export] macro_rules! val { ($a:expr) => { @@ -102,165 +15,4 @@ macro_rules! run_handler { ($self:ident.$handler:ident) => {{ &$self.$handler; }}; -} - -#[macro_export] -macro_rules! __css { - ($style:expr,)=>{}; - ( - $style:expr, ($name:ident): $value:expr - ) => {{ - $style.other.insert(stringify!($name).to_string(), Box::new($value)); - }}; - ( - $style:expr, ($name:ident): $value:expr, $($other:tt)* - ) => {{ - $style.other.insert(stringify!($name).to_string(), Box::new($value)); - $crate::__css!($($other)*); - }}; - ( - $style:expr, $name:ident: $value:expr - ) => {{ - $style.$name = $value; - }}; -} - -#[macro_export] -macro_rules! _css { - ($style:expr,) => {}; - ( - $hm:expr, .$name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); - } else { - let mut style = $crate::ui::Style::default(); - - $crate::__css!(style.0, $($inner)*); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, .$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - - - ( - $hm:expr, #$name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); - } else { - let mut style = $crate::ui::Style::default(); - - $crate::__css!(style.0, $($inner)*); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, #$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - - - ( - $hm:expr, $name:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); - } else { - let mut style = $crate::ui::Style::default(); - - $crate::__css!(style.0, $($inner)*); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; - ( - $hm:expr, $name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* - ) => {{ - let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - } else { - let mut style = $crate::ui::Style::default(); - - let mut style_elem = $crate::ui::StyleElement::default(); - $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); - - $hm.insert( - name, - style, - ); - } - $crate::_css!($hm, $($rest)*); - }}; -} - -#[macro_export] -macro_rules! css { - ( - $($inner:tt)* - ) => {{ - let mut hm: $crate::ui::Css = std::collections::HashMap::new(); - $crate::_css!(hm, $($inner)*); - hm - }}; -} +} \ No newline at end of file diff --git a/src/rsx.rs b/src/rsx.rs new file mode 100644 index 0000000..ea010bf --- /dev/null +++ b/src/rsx.rs @@ -0,0 +1,95 @@ +#[macro_export] +macro_rules! parse_rsx_param { + ($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, $p:path) => { + $p; + }; + ($elem:expr, $($k:ident).+, $($rest:tt)*) => { + $elem.$($k).+; + osui::parse_rsx_param!($elem, $($rest)*); + }; + ($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, for ($($for:tt)*) { $($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 { + for $($for)* { + children.push({$($inner)*}) + } + } + osui::parse_rsx_param!($elem, $($rest)*); + }; + + ($elem:expr, {$inner_elem:expr} $($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}); + } + osui::parse_rsx_param!($elem, $($rest)*); + }; + + ($elem:expr, $elem_path:path { $($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::rsx_elem!($elem_path { $($inner)* })); + } + osui::parse_rsx_param!($elem, $($rest)*) + }; + + ($elem:expr, $text:expr) => { + if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text))} + }; + ($elem:expr, $text:expr, $($inner:tt)*) => { + if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text, $($inner)*))} + }; + + ($elem:expr, ) => {}; +} + +#[macro_export] +macro_rules! rsx_elem { + ($elem:path { $($inner:tt)* }) => {{ + #[allow(unused_mut)] + let mut elem = $elem(); + $crate::parse_rsx_param!(elem, $($inner)*); + elem as $crate::Element + }}; +} + +/// Makes a div and puts elements into it +/// +/// # Example +/// ``` +/// rsx! { +/// button { class: "btn", "Click me!" } +/// } +/// ``` +/// +/// # Returns +/// A `osui::Element` - Which is a `Box` +#[macro_export] +macro_rules! rsx { + ($($inner:tt)*) => { + $crate::rsx_elem!{ $crate::ui::div { $($inner)* } } + }; + +} From 1f4ac1b14fd5be485d7a9694d1475608d9cde11e Mon Sep 17 00:00:00 2001 From: Leo dev Date: Thu, 21 Nov 2024 19:44:52 +0100 Subject: [PATCH 03/18] small change --- src/css.rs | 142 ++++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 30 +++++++++-- src/main.rs | 7 ++- 3 files changed, 132 insertions(+), 47 deletions(-) diff --git a/src/css.rs b/src/css.rs index 1ed1c97..71da4bf 100644 --- a/src/css.rs +++ b/src/css.rs @@ -1,41 +1,37 @@ #[macro_export] macro_rules! __css { - ($style:expr,)=>{}; + ($_style:expr,)=>{}; ( - $style:expr, ($name:ident): $value:expr + $_style:expr, $name:ident: $value:expr $(, $($other:tt)*)? ) => {{ - $style.other.insert(stringify!($name).to_string(), Box::new($value)); + $_style.$name = $value; + $crate::__css!($_style, $($($other)*)?); }}; ( - $style:expr, ($name:ident): $value:expr, $($other:tt)* + $_style:expr, ($name:ident): $value:expr $(, $($other:tt)*)? ) => {{ - $style.other.insert(stringify!($name).to_string(), Box::new($value)); - $crate::__css!($($other)*); - }}; - ( - $style:expr, $name:ident: $value:expr - ) => {{ - $style.$name = $value; + $_style.other.insert(stringify!($name).to_string(), Box::new($value)); + $crate::__css!($_style, $($($other)*)?); }}; } #[macro_export] macro_rules! _css { - ($style:expr,) => {}; + ($_style:expr,) => {}; ( $hm:expr, .$name:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); + if let Some(_style) = $hm.get_mut(&name) { + $crate::__css!(_style.0, $($inner)*); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); - $crate::__css!(style.0, $($inner)*); + $crate::__css!(_style.0, $($inner)*); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -44,20 +40,20 @@ macro_rules! _css { $hm:expr, .$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Class(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { + if let Some(_style) = $hm.get_mut(&name) { let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -68,16 +64,16 @@ macro_rules! _css { $hm:expr, #$name:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); + if let Some(_style) = $hm.get_mut(&name) { + $crate::__css!(_style.0, $($inner)*); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); - $crate::__css!(style.0, $($inner)*); + $crate::__css!(_style.0, $($inner)*); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -86,20 +82,20 @@ macro_rules! _css { $hm:expr, #$name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Id(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { + if let Some(_style) = $hm.get_mut(&name) { let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -110,16 +106,16 @@ macro_rules! _css { $hm:expr, $name:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { - $crate::__css!(style.0, $($inner)*); + if let Some(_style) = $hm.get_mut(&name) { + $crate::__css!(_style.0, $($inner)*); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); - $crate::__css!(style.0, $($inner)*); + $crate::__css!(_style.0, $($inner)*); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -128,20 +124,20 @@ macro_rules! _css { $hm:expr, $name:ident:$kind:ident { $($inner:tt)* } $($rest:tt)* ) => {{ let name = $crate::ui::StyleName::Component(stringify!($name).to_string()); - if let Some(style) = $hm.get_mut(&name) { + if let Some(_style) = $hm.get_mut(&name) { let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); } else { - let mut style = $crate::ui::Style::default(); + let mut _style = $crate::ui::Style::default(); let mut style_elem = $crate::ui::StyleElement::default(); $crate::__css!(style_elem, $($inner)*); - style.1.insert(stringify!($kind).to_string(), style_elem); + _style.1.insert(stringify!($kind).to_string(), style_elem); $hm.insert( name, - style, + _style, ); } $crate::_css!($hm, $($rest)*); @@ -156,7 +152,7 @@ macro_rules! _css { /// color: Blue /// } /// ``` -/// +/// /// # Returns /// `Css` #[macro_export] @@ -169,3 +165,63 @@ macro_rules! css { hm }}; } + +#[macro_export] +macro_rules! style { + ( + $key:ident: $value:expr $(, $($rest:tt)*)? + ) => {{ + let mut _style = $crate::ui::Style::default(); + $crate::__css!(_style.0, $key: $value); + $crate::style!{ _style; $($($rest)*)? } + }}; + ( + $style:expr; $key:ident: $value:expr $(, $($rest:tt)*)? + ) => {{ + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!($style.0, $key: $value); + $crate::style!{ $style; $($($rest)*)? } + }}; + ( + ($key:ident): $value:expr $(, $($rest:tt)*)? + ) => {{ + let mut _style = $crate::ui::Style::default(); + $crate::__css!(_style.0, ($key): $value); + $crate::style!{ _style; $($($rest)*)? } + }}; + ( + $style:expr; ($key:ident): $value:expr $(, $($rest:tt)*)? + ) => {{ + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!($style.0, ($key): $value); + $crate::style!{ $style; $($($rest)*)? } + }}; + + ( + $kind:ident {$($inner:tt)*} $(, $($rest:tt)*)? + ) => {{ + let mut _style = $crate::ui::Style::default(); + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + _style.1.insert(stringify!($kind).to_string(), style_elem); + $crate::style!{ _style; $($($rest)*)? } + }}; + ( + $style:expr; $kind:ident {$($inner:tt)*} $(, $($rest:tt)*)? + ) => {{ + let mut style_elem = $crate::ui::StyleElement::default(); + $crate::__css!(style_elem, $($inner)*); + $style.1.insert(stringify!($kind).to_string(), style_elem); + $crate::style!{ $style; $($($rest)*)? } + }}; + + ( + $style:expr; + ) => {{ + $style + }}; + + () => {{ + $crate::ui::Style::default() + }}; +} diff --git a/src/lib.rs b/src/lib.rs index 040d1da..189db7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,11 +29,11 @@ use std::{ use crossterm::event::Event; pub mod app; +pub mod css; pub mod macros; +pub mod rsx; pub mod ui; pub mod utils; -pub mod rsx; -pub mod css; pub use app::run; @@ -43,7 +43,7 @@ pub use app::run; pub mod prelude { pub use crate::ui::Color::*; pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; - pub use crate::{Element, Value}; + pub use crate::{Element, Value, style}; // useful for Element making pub use crate::{run_handler, Children, ElementCore, ElementWidget}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; @@ -139,6 +139,24 @@ impl std::fmt::Debug for Handler { /// Children Enum ////////////////////////////////////////////////////////////////////////////////////////////////// +/// Child enum of an `Element` +/// +/// # Example +/// ``` +/// // Check if it's text +/// if let Children::Text(text) { +/// // do something +/// } +/// // Check if it's inner elements +/// if let Children::Children(text) { +/// // do something +/// } +/// ``` +/// +/// # Useful for +/// - `Element::event` +/// - `Element::render` +/// - `Handler` #[derive(Debug)] pub enum Children { None, @@ -165,4 +183,10 @@ impl Children { _ => String::new(), } } + pub fn get_text_mut(&mut self) -> Option<&mut String> { + match self { + Children::Text(text) => Some(text), + _ => None, + } + } } diff --git a/src/main.rs b/src/main.rs index 6b078c6..2962035 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,17 @@ +// use std::sync::Arc; + use osui::prelude::*; +pub struct App {} + fn main() { + // let something = Arc::new(App {}); while osui::run(&mut app()) {} } fn app() -> Element { rsx! { styling: Some(styles()), - button { class: "btn", id: "test", "This is a button!" } + button { class: "btn", "Hello, World!" } } } From ec4b42e978fda84dbccfa0aac01d658d6a5dde16 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Thu, 21 Nov 2024 19:53:47 +0100 Subject: [PATCH 04/18] small change --- Cargo.toml | 2 +- src/lib.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 890fa69..c87093a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,5 @@ crossterm = "0.28.1" regex = "1.11.1" lazy_static = "1.4" paste = "1.0.15" -osui-element = "0.1.0" +osui-element = "0.1.1" dyn-clone = "1.0.17" diff --git a/src/lib.rs b/src/lib.rs index 189db7f..ece152a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ pub trait ElementCore: Send + Sync { 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 type_id(&self) -> std::any::TypeId; } pub trait ElementWidget: ElementCore + std::fmt::Debug { From 533ec30597eff526d4159d5e2b412416f9d5d752 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 05:29:56 +0100 Subject: [PATCH 05/18] small change --- Cargo.toml | 3 ++- src/lib.rs | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c87093a..ae01d54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,6 @@ crossterm = "0.28.1" regex = "1.11.1" lazy_static = "1.4" paste = "1.0.15" -osui-element = "0.1.1" +osui-element = "0.1.5" dyn-clone = "1.0.17" +flume = "=0.10.12" diff --git a/src/lib.rs b/src/lib.rs index ece152a..00c65a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ pub use app::run; pub mod prelude { pub use crate::ui::Color::*; pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; - pub use crate::{Element, Value, style}; + pub use crate::{style, Element, Value}; // useful for Element making pub use crate::{run_handler, Children, ElementCore, ElementWidget}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; @@ -93,7 +93,6 @@ pub trait ElementCore: Send + Sync { 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 type_id(&self) -> std::any::TypeId; } pub trait ElementWidget: ElementCore + std::fmt::Debug { @@ -141,7 +140,7 @@ impl std::fmt::Debug for Handler { ////////////////////////////////////////////////////////////////////////////////////////////////// /// Child enum of an `Element` -/// +/// /// # Example /// ``` /// // Check if it's text @@ -153,7 +152,7 @@ impl std::fmt::Debug for Handler { /// // do something /// } /// ``` -/// +/// /// # Useful for /// - `Element::event` /// - `Element::render` @@ -191,3 +190,8 @@ impl Children { } } } + +/// Converts a `Element` into the struct +pub fn convert(widget: &mut Box) -> &mut Box { + unsafe { &mut *(widget as *mut _ as *mut Box) } +} \ No newline at end of file From 10c8f6d0cad0fe5b2a547332a8563dcc0ae0be0f Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 05:59:14 +0100 Subject: [PATCH 06/18] command stage 1 --- src/app.c | 23 ++++++++++++--- src/app.rs | 86 ++++++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 16 +++++++++- 3 files changed, 95 insertions(+), 30 deletions(-) diff --git a/src/app.c b/src/app.c index 50a5475..027523a 100644 --- a/src/app.c +++ b/src/app.c @@ -4,8 +4,16 @@ #include #include +typedef struct { + void* element; + void* sender; + void* receiver; + boolean_t running; +} LArgs; + extern boolean_t render(void* element); extern void *event_loop(void* element); +extern void *cmd_loop(void* element); void sleep_ms(long milliseconds) { @@ -15,16 +23,23 @@ void sleep_ms(long milliseconds) nanosleep(&ts, NULL); } -boolean_t c_run(void* element) +boolean_t c_run(void* element, void* sender, void* receiver) { - pthread_t thread; + pthread_t event_thread; + pthread_t cmd_thread; + LArgs args = { .element = element, .sender = sender, .receiver = receiver, .running = 1 }; - if (pthread_create(&thread, NULL, event_loop, element) != 0) + if (pthread_create(&event_thread, NULL, event_loop, &args) != 0) { perror("Failed to create thread"); } - while (render(element)) + if (pthread_create(&cmd_thread, NULL, cmd_loop, &args) != 0) + { + perror("Failed to create thread"); + } + + while (render(element) && args.running) { sleep_ms(20); } diff --git a/src/app.rs b/src/app.rs index 86eafac..d20ecd1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,14 +3,48 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode}, }; -use crate::{ - utils::{clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor}, - Element, +use crate::prelude::*; +use crate::utils::{ + clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor, }; -use std::{ffi::c_void, ptr::null}; +use std::{ffi::c_void, ptr::null, sync::mpsc}; + +pub struct LArgs { + pub element: *mut c_void, + pub sender: *const c_void, + pub receiver: *const c_void, + pub running: bool, +} extern "C" { - fn c_run(element: *mut c_void) -> bool; + fn c_run(element: *mut c_void, sender: *const c_void, receiver: *const c_void) -> bool; +} + +fn event(element: &mut Element, event: Event) { + if let crossterm::event::Event::Resize(width, height) = event { + element.update_data(width as usize, height as usize); + } + element.event(event); +} + +pub fn run(element: &mut Element) -> bool { + hide_cursor(); + enable_raw_mode().unwrap(); + clear(); + unsafe { + let (tx, rx) = mpsc::channel::(); + let c = c_run( + element as *mut Element as *mut c_void, + &tx as *const mpsc::Sender as *const c_void, + &rx as *const mpsc::Receiver as *const c_void, + ); + if !c { + show_cursor(); + disable_raw_mode().unwrap(); + clear(); + } + return c; + } } #[no_mangle] @@ -33,37 +67,39 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { if ptr.is_null() { return null() as *const c_void; } - let element = unsafe { &mut *(ptr as *mut Element) }; + let args = unsafe { &mut *(ptr as *mut LArgs) }; + let element = unsafe { &mut *(args.element as *mut Element) }; + let tx = unsafe { &*(args.sender as *const mpsc::Sender) }; + let (width, height) = get_term_size(); element.update_data(width, height); event(element, crossterm::event::Event::FocusGained); - loop { + + while args.running { if ptr.is_null() { break; } - event(element, crossterm::event::read().unwrap()) + event(element, crossterm::event::read().unwrap()); + tx.send(Command::Exit).unwrap(); } null() as *const c_void } -fn event(element: &mut Element, event: Event) { - if let crossterm::event::Event::Resize(width, height) = event { - element.update_data(width as usize, height as usize); +#[no_mangle] +extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { + if ptr.is_null() { + return null() as *const c_void; } - element.event(event); -} - -pub fn run(element: &mut Element) -> bool { - hide_cursor(); - enable_raw_mode().unwrap(); - clear(); - unsafe { - let c = c_run(element as *mut Element as *mut c_void); - if !c { - show_cursor(); - disable_raw_mode().unwrap(); - clear(); + let args = unsafe { &mut *(ptr as *mut LArgs) }; + let _element = unsafe { &mut *(args.element as *mut Element) }; + let rx = unsafe { &*(args.receiver as *const mpsc::Receiver) }; + loop { + match rx.recv().unwrap() { + Command::Exit => { + args.running = false; + break; + } } - return c; } + null() } diff --git a/src/lib.rs b/src/lib.rs index 00c65a9..3cead75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ pub use app::run; pub mod prelude { pub use crate::ui::Color::*; pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; - pub use crate::{style, Element, Value}; + pub use crate::{style, Command, Element, Value, Document}; // useful for Element making pub use crate::{run_handler, Children, ElementCore, ElementWidget}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; @@ -194,4 +194,18 @@ impl Children { /// Converts a `Element` into the struct pub fn convert(widget: &mut Box) -> &mut Box { unsafe { &mut *(widget as *mut _ as *mut Box) } +} + +pub enum Command { + Exit, +} + +pub struct Document { + cmd_sender: std::sync::mpsc::Sender, +} + +impl Document { + pub fn exit(&self) { + self.cmd_sender.send(Command::Exit).unwrap(); + } } \ No newline at end of file From cc0ea4a9f5159c2b6def1c786301bd1818d3ff45 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 09:33:02 +0100 Subject: [PATCH 07/18] commands and get_element_by_id --- Cargo.toml | 2 +- src/app.c | 34 +++++--------------- src/app.rs | 87 +++++++++++++++++++++++++++++++++------------------ src/lib.rs | 46 ++++++++++++++++++++++----- src/main.rs | 15 ++++++--- src/ui/mod.rs | 16 +++++----- 6 files changed, 123 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae01d54..223a066 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.5" +osui-element = "0.1.8" dyn-clone = "1.0.17" flume = "=0.10.12" diff --git a/src/app.c b/src/app.c index 027523a..81b63ac 100644 --- a/src/app.c +++ b/src/app.c @@ -4,44 +4,24 @@ #include #include -typedef struct { - void* element; - void* sender; - void* receiver; - boolean_t running; -} LArgs; +extern boolean_t render_loop(void* args); +extern void *event_loop(void* args); +extern void *cmd_loop(void* args); -extern boolean_t render(void* element); -extern void *event_loop(void* element); -extern void *cmd_loop(void* element); - -void sleep_ms(long milliseconds) -{ - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = (milliseconds % 1000) * 1000000; - nanosleep(&ts, NULL); -} - -boolean_t c_run(void* element, void* sender, void* receiver) +boolean_t c_run(void* args) { pthread_t event_thread; pthread_t cmd_thread; - LArgs args = { .element = element, .sender = sender, .receiver = receiver, .running = 1 }; - if (pthread_create(&event_thread, NULL, event_loop, &args) != 0) + if (pthread_create(&event_thread, NULL, event_loop, args) != 0) { perror("Failed to create thread"); } - if (pthread_create(&cmd_thread, NULL, cmd_loop, &args) != 0) + if (pthread_create(&cmd_thread, NULL, cmd_loop, args) != 0) { perror("Failed to create thread"); } - while (render(element) && args.running) - { - sleep_ms(20); - } - return 0; + return render_loop(args); } \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index d20ecd1..5863f13 100644 --- a/src/app.rs +++ b/src/app.rs @@ -9,22 +9,28 @@ use crate::utils::{ }; use std::{ffi::c_void, ptr::null, sync::mpsc}; -pub struct LArgs { - pub element: *mut c_void, - pub sender: *const c_void, - pub receiver: *const c_void, +pub struct LArgs<'a> { + pub element: &'a mut Element, + + // for sending commands + pub stx: mpsc::Sender, + pub srx: mpsc::Receiver, + + // for returning command output + pub rtx: mpsc::Sender>, + pub rrx: mpsc::Receiver>, pub running: bool, } extern "C" { - fn c_run(element: *mut c_void, sender: *const c_void, receiver: *const c_void) -> bool; + fn c_run(args: *mut c_void) -> bool; } -fn event(element: &mut Element, event: Event) { +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); + element.event(event, document); } pub fn run(element: &mut Element) -> bool { @@ -32,12 +38,16 @@ pub fn run(element: &mut Element) -> bool { enable_raw_mode().unwrap(); clear(); unsafe { - let (tx, rx) = mpsc::channel::(); - let c = c_run( - element as *mut Element as *mut c_void, - &tx as *const mpsc::Sender as *const c_void, - &rx as *const mpsc::Receiver as *const c_void, - ); + let (stx, srx) = mpsc::channel(); + let (rtx, rrx) = mpsc::channel(); + let c = c_run(&mut LArgs { + running: true, + element, + stx, + srx, + rtx, + rrx, + } as *mut LArgs as *mut c_void); if !c { show_cursor(); disable_raw_mode().unwrap(); @@ -48,18 +58,20 @@ pub fn run(element: &mut Element) -> bool { } #[no_mangle] -extern "C" fn render(ptr: *mut c_void) -> bool { +extern "C" fn render_loop(ptr: *mut c_void) -> bool { if ptr.is_null() { return false; } - let element = unsafe { &mut *(ptr as *mut Element) }; - let (width, height) = get_term_size(); - let mut frame: Vec = create_frame(width, height); - render_to_frame(true, width, &mut frame, element); - clear(); - print!("{}", frame.join("")); - flush(); - true + let args = unsafe { &mut *(ptr as *mut LArgs) }; + while args.running { + let (width, height) = get_term_size(); + let mut frame: Vec = create_frame(width, height); + render_to_frame(true, width, &mut frame, args.element); + clear(); + print!("{}", frame.join("")); + flush(); + } + false } #[no_mangle] @@ -68,19 +80,24 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { return null() as *const c_void; } let args = unsafe { &mut *(ptr as *mut LArgs) }; - let element = unsafe { &mut *(args.element as *mut Element) }; - let tx = unsafe { &*(args.sender as *const mpsc::Sender) }; + let document = Document { + cmd_sender: args.stx.clone(), + cmd_recv: &args.rrx as *const mpsc::Receiver> as *const c_void, + }; let (width, height) = get_term_size(); - element.update_data(width, height); - event(element, crossterm::event::Event::FocusGained); + args.element.update_data(width, height); + event( + args.element, + crossterm::event::Event::FocusGained, + &document, + ); while args.running { if ptr.is_null() { break; } - event(element, crossterm::event::read().unwrap()); - tx.send(Command::Exit).unwrap(); + event(args.element, crossterm::event::read().unwrap(), &document); } null() as *const c_void } @@ -91,14 +108,22 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { return null() as *const c_void; } let args = unsafe { &mut *(ptr as *mut LArgs) }; - let _element = unsafe { &mut *(args.element as *mut Element) }; - let rx = unsafe { &*(args.receiver as *const mpsc::Receiver) }; + loop { - match rx.recv().unwrap() { + match args.srx.recv().unwrap() { Command::Exit => { args.running = false; break; } + Command::GetElementById(id) => { + args.rtx + .send(if let Some(e) = args.element.get_element_by_id(&id) { + Some(e as *mut Element) + } else { + None + }) + .unwrap(); + } } } null() diff --git a/src/lib.rs b/src/lib.rs index 3cead75..27b3449 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ pub use app::run; pub mod prelude { pub use crate::ui::Color::*; pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; - pub use crate::{style, Command, Element, Value, Document}; + pub use crate::{style, Command, Document, Element, Value}; // useful for Element making pub use crate::{run_handler, Children, ElementCore, ElementWidget}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; @@ -101,8 +101,8 @@ pub trait ElementWidget: ElementCore + std::fmt::Debug { String::new() } - fn event(&mut self, event: Event) { - _ = event + fn event(&mut self, event: Event, document: &Document) { + _ = (event, document) } } @@ -112,12 +112,12 @@ pub type Element = Box; /// Handler Struct ////////////////////////////////////////////////////////////////////////////////////////////////// -pub struct Handler(pub Arc>); +pub struct Handler(pub Arc>); impl Handler { pub fn new(handler_fn: F) -> Handler where - F: FnMut(&mut T, Event) + 'static + Send + Sync, + F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync, { Handler(Arc::new(Mutex::new(handler_fn))) } @@ -125,7 +125,7 @@ impl Handler { impl Default for Handler { fn default() -> Self { - Handler(Arc::new(Mutex::new(|_: &mut T, _: Event| {}))) + Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {}))) } } @@ -189,6 +189,24 @@ impl Children { _ => None, } } + pub fn set_text(&mut self, text: &str) { + match self { + Children::Text(t) => { + *t = text.to_string(); + } + _ => {} + } + } + pub fn set_text_force(&mut self, text: &str) { + match self { + Children::Text(t) => { + *t = text.to_string(); + } + _ => { + *self = Children::Text(text.to_string()); + } + } + } } /// Converts a `Element` into the struct @@ -198,14 +216,28 @@ pub fn convert(widget: &mut Box) -> &mut Box { pub enum Command { Exit, + GetElementById(String), } pub struct Document { cmd_sender: std::sync::mpsc::Sender, + cmd_recv: *const std::ffi::c_void, } impl Document { pub fn exit(&self) { self.cmd_sender.send(Command::Exit).unwrap(); } -} \ No newline at end of file + pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { + self.cmd_sender + .send(Command::GetElementById(id.to_string())) + .unwrap(); + let rx = + unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver>) }; + if let Ok(Some(e)) = rx.recv() { + Some(convert(unsafe { &mut *e })) + } else { + None + } + } +} diff --git a/src/main.rs b/src/main.rs index 2962035..c42bc85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,21 @@ -// use std::sync::Arc; - use osui::prelude::*; pub struct App {} fn main() { - // let something = Arc::new(App {}); while osui::run(&mut app()) {} } fn app() -> Element { rsx! { styling: Some(styles()), - button { class: "btn", "Hello, World!" } + button { class: "btn", on_click: Handler::new(|_, _, document| { + + if let Some(txt) = document.get_element_by_id::("txt") { + txt.children.set_text("edited"); + } + + }), "Hello, World!" } + text { id: "txt", "Some text!" } } } @@ -20,5 +24,8 @@ fn styles() -> Css { .btn: clicked { color: Blue } + .btn: hover { + color: Red + } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 981fdf8..6512cce 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -83,9 +83,11 @@ impl ElementWidget for Div<'_> { frame.join("\n") } - fn event(&mut self, event: Event) { - if let Some(styling) = self.styling.clone() { - self.set_styling(&styling); + fn event(&mut self, event: Event, document: &Document) { + if event == Event::FocusGained { + if let Some(styling) = self.styling.clone() { + self.set_styling(&styling); + } } match event { Event::Key(key) => { @@ -117,7 +119,7 @@ impl ElementWidget for Div<'_> { _ => { if let Some(c) = children.get_mut(*child) { - c.event(event); + c.event(event, document); } *child } @@ -126,7 +128,7 @@ impl ElementWidget for Div<'_> { } _ => { if let Some(child) = self.get_child() { - child.event(event); + child.event(event, document); } } } @@ -153,12 +155,12 @@ impl ElementWidget for Button<'_> { } } - fn event(&mut self, event: Event) { + fn event(&mut self, event: Event, document: &Document) { match event { Event::Key(key) => { if key.code == KeyCode::Enter { self.state = "clicked"; - run_handler!(self.on_click(event)); + run_handler!(self.on_click(event, document)); sleep(100); self.state = ""; } From 7f1437e779832b946d1c2fb50c31a858597f73c5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 11:29:00 +0100 Subject: [PATCH 08/18] perfomance changes --- src/app.rs | 17 +++++++++++------ src/lib.rs | 4 ++++ src/main.rs | 4 +--- src/ui/mod.rs | 2 ++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5863f13..efbeb82 100644 --- a/src/app.rs +++ b/src/app.rs @@ -64,12 +64,7 @@ extern "C" fn render_loop(ptr: *mut c_void) -> bool { } let args = unsafe { &mut *(ptr as *mut LArgs) }; while args.running { - let (width, height) = get_term_size(); - let mut frame: Vec = create_frame(width, height); - render_to_frame(true, width, &mut frame, args.element); - clear(); - print!("{}", frame.join("")); - flush(); + sleep(1000); } false } @@ -92,12 +87,14 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { crossterm::event::Event::FocusGained, &document, ); + document.render(); while args.running { if ptr.is_null() { break; } event(args.element, crossterm::event::read().unwrap(), &document); + document.render(); } null() as *const c_void } @@ -124,6 +121,14 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { }) .unwrap(); } + Command::Render => { + let (width, height) = get_term_size(); + let mut frame: Vec = create_frame(width, height); + render_to_frame(true, width, &mut frame, args.element); + clear(); + print!("{}", frame.join("")); + flush(); + } } } null() diff --git a/src/lib.rs b/src/lib.rs index 27b3449..8f0f97e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -216,6 +216,7 @@ pub fn convert(widget: &mut Box) -> &mut Box { pub enum Command { Exit, + Render, GetElementById(String), } @@ -240,4 +241,7 @@ impl Document { None } } + pub fn render(&self) { + self.cmd_sender.send(Command::Render).unwrap(); + } } diff --git a/src/main.rs b/src/main.rs index c42bc85..eddde5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,13 +9,11 @@ fn main() { fn app() -> Element { rsx! { styling: Some(styles()), button { class: "btn", on_click: Handler::new(|_, _, document| { - if let Some(txt) = document.get_element_by_id::("txt") { txt.children.set_text("edited"); } - }), "Hello, World!" } - text { id: "txt", "Some text!" } + text { id: "txt", y: 2, "Some text!" } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6512cce..de5b9a2 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -159,8 +159,10 @@ impl ElementWidget for Button<'_> { match event { Event::Key(key) => { if key.code == KeyCode::Enter { + document.render(); self.state = "clicked"; run_handler!(self.on_click(event, document)); + document.render(); sleep(100); self.state = ""; } From 129725dce2fd0b2d87cf6484cf0b0fdc33eae93a Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 12:42:03 +0100 Subject: [PATCH 09/18] some fine tuning --- src/examples.rs | 9 +++++++++ src/lib.rs | 3 ++- src/macros.rs | 4 ++-- src/main.rs | 27 ++------------------------- src/ui/mod.rs | 9 +++++++-- 5 files changed, 22 insertions(+), 30 deletions(-) create mode 100644 src/examples.rs diff --git a/src/examples.rs b/src/examples.rs new file mode 100644 index 0000000..c8225e4 --- /dev/null +++ b/src/examples.rs @@ -0,0 +1,9 @@ +use crate::prelude::*; + +pub fn event_logger() -> Element { + rsx_elem! { + text { on_event: Handler::new(|txt: &mut Text, e, _| { + txt.children.set_text(&format!("current event: {e:?}")); + }), "current event: None" } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 8f0f97e..abc6784 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ use crossterm::event::Event; pub mod app; pub mod css; +pub mod examples; pub mod macros; pub mod rsx; pub mod ui; @@ -45,7 +46,7 @@ pub mod prelude { pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler}; pub use crate::{style, Command, Document, Element, Value}; // useful for Element making - pub use crate::{run_handler, Children, ElementCore, ElementWidget}; + pub use crate::{call, Children, ElementCore, ElementWidget}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); diff --git a/src/macros.rs b/src/macros.rs index 02ecc4c..71694dd 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -6,7 +6,7 @@ macro_rules! val { } #[macro_export] -macro_rules! run_handler { +macro_rules! call { ($self:ident.$handler:ident ($($inner:tt)*)) => {{ let a = std::sync::Arc::clone(&$self.$handler.0); let mut o = a.lock().unwrap(); @@ -15,4 +15,4 @@ macro_rules! run_handler { ($self:ident.$handler:ident) => {{ &$self.$handler; }}; -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index eddde5d..c1682a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,6 @@ use osui::prelude::*; - -pub struct App {} +use osui::examples; fn main() { - while osui::run(&mut app()) {} -} - -fn app() -> Element { - rsx! { styling: Some(styles()), - button { class: "btn", on_click: Handler::new(|_, _, document| { - if let Some(txt) = document.get_element_by_id::("txt") { - txt.children.set_text("edited"); - } - }), "Hello, World!" } - text { id: "txt", y: 2, "Some text!" } - } -} - -fn styles() -> Css { - css! { - .btn: clicked { - color: Blue - } - .btn: hover { - color: Red - } - } + while osui::run(&mut examples::event_logger()) {} } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index de5b9a2..7006d83 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -26,7 +26,9 @@ use osui_element::{elem_fn, element}; #[element] #[elem_fn] #[derive(Default, Debug)] -pub struct Text {} +pub struct Text { + pub on_event: Handler>, +} impl ElementWidget for Text<'_> { fn render(&self, _: bool) -> String { @@ -36,6 +38,9 @@ impl ElementWidget for Text<'_> { String::new() } } + fn event(&mut self, event: Event, document: &Document) { + call!(self.on_event(event, document)); + } } /// The `Div` element represents a container for other UI elements. It can hold child components @@ -161,7 +166,7 @@ impl ElementWidget for Button<'_> { if key.code == KeyCode::Enter { document.render(); self.state = "clicked"; - run_handler!(self.on_click(event, document)); + call!(self.on_click(event, document)); document.render(); sleep(100); self.state = ""; From a804427c7e1d8bf362cd79106380f08bd6532064 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 22 Nov 2024 17:56:12 +0100 Subject: [PATCH 10/18] some small changes --- src/app.rs | 12 +++++++----- src/examples.rs | 12 +++++++----- src/lib.rs | 26 ++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/app.rs b/src/app.rs index efbeb82..5ef1b60 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,8 +17,8 @@ pub struct LArgs<'a> { pub srx: mpsc::Receiver, // for returning command output - pub rtx: mpsc::Sender>, - pub rrx: mpsc::Receiver>, + pub rtx: mpsc::Sender, + pub rrx: mpsc::Receiver, pub running: bool, } @@ -77,7 +77,7 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { let args = unsafe { &mut *(ptr as *mut LArgs) }; let document = Document { cmd_sender: args.stx.clone(), - cmd_recv: &args.rrx as *const mpsc::Receiver> as *const c_void, + cmd_recv: &args.rrx as *const mpsc::Receiver as *const c_void, }; let (width, height) = get_term_size(); @@ -115,9 +115,11 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { Command::GetElementById(id) => { args.rtx .send(if let Some(e) = args.element.get_element_by_id(&id) { - Some(e as *mut Element) + crate::CommandResult::Element(e as *mut Element) + } else if args.element.get_data().2 == id { + crate::CommandResult::Element(args.element as *mut Element) } else { - None + crate::CommandResult::None }) .unwrap(); } diff --git a/src/examples.rs b/src/examples.rs index c8225e4..5d4ab98 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -1,9 +1,11 @@ use crate::prelude::*; pub fn event_logger() -> Element { - rsx_elem! { - text { on_event: Handler::new(|txt: &mut Text, e, _| { - txt.children.set_text(&format!("current event: {e:?}")); - }), "current event: None" } + rsx! { + id: "root", + text { on_event: Handler::new(|_, _, document| { + let root = document.get_element_by_id::
("root").unwrap(); + root.children.add_child(rsx_elem!( text { "Ok" } )); + }), id: "txt", "current event: None" } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index abc6784..51926fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -208,6 +208,11 @@ impl Children { } } } + pub fn add_child(&mut self, element: Element) { + if let Children::Children(children, _) = self { + children.push(element); + } + } } /// Converts a `Element` into the struct @@ -221,6 +226,11 @@ pub enum Command { GetElementById(String), } +pub enum CommandResult { + Element(*mut Element), + None +} + pub struct Document { cmd_sender: std::sync::mpsc::Sender, cmd_recv: *const std::ffi::c_void, @@ -235,13 +245,25 @@ impl Document { .send(Command::GetElementById(id.to_string())) .unwrap(); let rx = - unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver>) }; - if let Ok(Some(e)) = rx.recv() { + unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; + if let Ok(CommandResult::Element(e)) = rx.recv() { Some(convert(unsafe { &mut *e })) } else { None } } + pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> { + self.cmd_sender + .send(Command::GetElementById(id.to_string())) + .unwrap(); + let rx = + unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; + if let Ok(CommandResult::Element(e)) = rx.recv() { + Some(e) + } else { + None + } + } pub fn render(&self) { self.cmd_sender.send(Command::Render).unwrap(); } From 203741c1f15e3cd6318b42154c36b6247ec2bde5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 24 Nov 2024 01:15:19 +0100 Subject: [PATCH 11/18] branding --- .DS_Store | Bin 0 -> 6148 bytes README.md | 2 +- github/OSUI.svg | 90 +++++++++++++++++++++++++++++++++ github/{OSUI.png => OSUIt.png} | Bin github/OSUIw.svg | 90 +++++++++++++++++++++++++++++++++ src/lib.rs | 12 ++--- styling.md | 9 ++++ 7 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 .DS_Store create mode 100644 github/OSUI.svg rename github/{OSUI.png => OSUIt.png} (100%) create mode 100644 github/OSUIw.svg create mode 100644 styling.md diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b4c51467bb0f93faeae1d267b2e19b70aefec959 GIT binary patch literal 6148 zcmeHKJxc>Y5S>kq5D`oo!Op8x5CSREpCMu+sHC*fB$5OYE@&d!-7m1x%3on;nL@1X zZ7lo)7J{X3c9&!~XDmf@26o@xeC*qMcii3*k*bbbWuhVx<;fuLo4Pdgo?r0(?8f?Wt=8zp4fyhR_t(oCmycV1y!CB- z&~e_4!WgBLGpy915j8w+Rmsi9UH;9zr-$yj=J)34$I~^;%kkEs_IVvyI-^r+dMlut z!)e6juB=||99(E_Tj#Q-qdp(g;l77-48}H{_?j=hm2PL_F89ayb$88hdvmxen3v - OSUI + OSUI

diff --git a/github/OSUI.svg b/github/OSUI.svg new file mode 100644 index 0000000..9339c50 --- /dev/null +++ b/github/OSUI.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/github/OSUI.png b/github/OSUIt.png similarity index 100% rename from github/OSUI.png rename to github/OSUIt.png diff --git a/github/OSUIw.svg b/github/OSUIw.svg new file mode 100644 index 0000000..79a536b --- /dev/null +++ b/github/OSUIw.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib.rs b/src/lib.rs index 51926fc..38bd8b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ pub enum Command { pub enum CommandResult { Element(*mut Element), - None + None, } pub struct Document { @@ -241,12 +241,7 @@ impl Document { self.cmd_sender.send(Command::Exit).unwrap(); } pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { - self.cmd_sender - .send(Command::GetElementById(id.to_string())) - .unwrap(); - let rx = - unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; - if let Ok(CommandResult::Element(e)) = rx.recv() { + if let Some(e) = self.get_element_by_id_raw(id) { Some(convert(unsafe { &mut *e })) } else { None @@ -256,8 +251,7 @@ impl Document { self.cmd_sender .send(Command::GetElementById(id.to_string())) .unwrap(); - let rx = - unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; + let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; if let Ok(CommandResult::Element(e)) = rx.recv() { Some(e) } else { diff --git a/styling.md b/styling.md new file mode 100644 index 0000000..be2752c --- /dev/null +++ b/styling.md @@ -0,0 +1,9 @@ +`color`, `background`: `$color{}\x1b[0m`; + +`font`: `$font{}\x1b[0m`; + +`width`, `height`: `n`, `n%`, `auto`; + +`x`, `y`: `n`, `n%`, `center`, `auto`; + +`display`: `row`, `column`, `overlap`; \ No newline at end of file From e463ca1e7c87b18888bedf241b71a996873a75fc Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 24 Nov 2024 01:20:39 +0100 Subject: [PATCH 12/18] branding --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4c90589..488b810 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ OSUI

+

osui

+

Crates.io Version From 33843688d848643cfbea282f6a306762f5a58929 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sun, 24 Nov 2024 20:00:29 +0100 Subject: [PATCH 13/18] styling --- src/app.rs | 10 ++--- src/css.rs | 25 ++++++++++++ src/examples.rs | 21 +++++++--- src/lib.rs | 11 +++-- src/ui/mod.rs | 52 +++++++++++++----------- src/ui/styles.rs | 77 ++++++++++++++++++++++++++++++++--- src/utils.rs | 102 ++++++++++++++++++++++++++++++++++------------- styling.md | 2 +- 8 files changed, 228 insertions(+), 72 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5ef1b60..501b0bb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,9 +4,7 @@ use crossterm::{ }; use crate::prelude::*; -use crate::utils::{ - clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor, -}; +use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame}; use std::{ffi::c_void, ptr::null, sync::mpsc}; pub struct LArgs<'a> { @@ -125,10 +123,10 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { } Command::Render => { let (width, height) = get_term_size(); - let mut frame: Vec = create_frame(width, height); - render_to_frame(true, width, &mut frame, args.element); + let mut frame = Frame::new(width, height); + frame.render(true, args.element); clear(); - print!("{}", frame.join("")); + print!("{}", frame.output_nnl()); flush(); } } diff --git a/src/css.rs b/src/css.rs index 71da4bf..a7e88d4 100644 --- a/src/css.rs +++ b/src/css.rs @@ -1,6 +1,31 @@ #[macro_export] macro_rules! __css { ($_style:expr,)=>{}; + // percentage (n%) + ( + $_style:expr, $name:ident: $value:literal% $(, $($other:tt)*)? + ) => {{ + $_style.$name = Number::Pe($value); + $crate::__css!($_style, $($($other)*)?); + }}; + ( + $_style:expr, ($name:ident): $value:literal% $(, $($other:tt)*)? + ) => {{ + $_style.other.insert(stringify!($name).to_string(), Box::new( + Number::Pe($value) + )); + $crate::__css!($_style, $($($other)*)?); + }}; + + // Number + ( + $_style:expr, $name:ident: $value:literal $(, $($other:tt)*)? + ) => {{ + $_style.$name = Number::Px($value); + $crate::__css!($_style, $($($other)*)?); + }}; + + // Normal ( $_style:expr, $name:ident: $value:expr $(, $($other:tt)*)? ) => {{ diff --git a/src/examples.rs b/src/examples.rs index 5d4ab98..9e1f807 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -2,10 +2,21 @@ use crate::prelude::*; pub fn event_logger() -> Element { rsx! { - id: "root", - text { on_event: Handler::new(|_, _, document| { - let root = document.get_element_by_id::

("root").unwrap(); - root.children.add_child(rsx_elem!( text { "Ok" } )); - }), id: "txt", "current event: None" } + styling: Some(styling()), + button { class: "txt", "Hello, World!" } + } +} + +pub fn styling() -> Css { + css! { + .txt { + x: Center, + y: Center, + color: Red, + } + + .txt: clicked { + color: Blue, + } } } diff --git a/src/lib.rs b/src/lib.rs index 38bd8b5..fe706c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ pub use app::run; /// the UI framework easier to use. 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::{style, Command, Document, Element, Value}; // useful for Element making @@ -88,6 +89,11 @@ impl Default for Value { /// ElementCore Traits ////////////////////////////////////////////////////////////////////////////////////////////////// +/// ``` +/// RenderResult(output, (x, y), (width, height)) +/// ``` +pub struct RenderResult(String, (ui::Number, ui::Number)); + pub trait ElementCore: Send + Sync { fn get_data(&self) -> (Value, usize, String); fn update_data(&mut self, width: usize, height: usize); @@ -97,10 +103,7 @@ pub trait ElementCore: Send + Sync { } pub trait ElementWidget: ElementCore + std::fmt::Debug { - fn render(&self, focused: bool) -> String { - _ = focused; - String::new() - } + fn render(&self, focused: bool) -> RenderResult; fn event(&mut self, event: Event, document: &Document) { _ = (event, document) diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 7006d83..6c64ef2 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -5,7 +5,7 @@ pub mod styles; pub use styles::*; -use crate::prelude::*; +use crate::{prelude::*, RenderResult}; use osui_element::{elem_fn, element}; /// The `Text` element represents a piece of static text in the terminal UI. @@ -31,12 +31,17 @@ pub struct Text { } impl ElementWidget for Text<'_> { - fn render(&self, _: bool) -> String { - if let Children::Text(text) = &self.children { - text.clone() - } else { - String::new() - } + fn render(&self, _: bool) -> RenderResult { + self.style.result( + "", + self.style.write("", &{ + if let Children::Text(text) = &self.children { + text.clone() + } else { + String::new() + } + }), + ) } fn event(&mut self, event: Event, document: &Document) { call!(self.on_event(event, document)); @@ -71,21 +76,16 @@ pub struct Div { } impl ElementWidget for Div<'_> { - fn render(&self, focused: bool) -> String { - let mut frame = crate::utils::create_frame(self.width.get_value(), self.height.get_value()); + fn render(&self, focused: bool) -> RenderResult { + let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12); if let Children::Children(children, child) = &self.children { for (i, elem) in children.iter().enumerate() { - crate::utils::render_to_frame( - focused && i == *child, - self.width.get_value(), - &mut frame, - elem, - ); + frame.render(focused && i == *child, elem); } } - frame.join("\n") + self.style.result("", frame.output()) } fn event(&mut self, event: Event, document: &Document) { @@ -149,14 +149,20 @@ pub struct Button { } impl ElementWidget for Button<'_> { - fn render(&self, focused: bool) -> String { - if self.state == "clicked" { - return self.style.write("clicked", &self.children.get_text()); - } - if focused { - self.style.write("hover", &self.children.get_text()) + fn render(&self, focused: bool) -> RenderResult { + if self.state != "" { + self.style.result( + self.state, + self.style.write(self.state, &self.children.get_text()), + ) + } else if focused { + self.style.result( + "hover", + self.style.write("hover", &self.children.get_text()), + ) } else { - self.style.write("", &self.children.get_text()) + self.style + .result("", self.style.write("", &self.children.get_text())) } } diff --git a/src/ui/styles.rs b/src/ui/styles.rs index 9498ff1..48a57d0 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -6,9 +6,12 @@ use std::{collections::HashMap, fmt::Debug}; use dyn_clone::{clone_trait_object, DynClone}; +use crate::RenderResult; + pub trait StyleCore: Debug + Send + Sync + DynClone { fn ansi(&self) -> String; fn ansi_bg(&self) -> String; + fn is_null(&self) -> bool; } clone_trait_object!(StyleCore); @@ -45,6 +48,9 @@ impl StyleCore for Font { fn ansi_bg(&self) -> String { self.ansi() } + fn is_null(&self) -> bool { + *self == Self::None + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -104,6 +110,9 @@ impl StyleCore for Color { Color::White => "\x1b[47m", }) } + fn is_null(&self) -> bool { + *self == Self::NoColor + } } impl Default for Color { @@ -144,6 +153,15 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) { (r, g, b) } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Number { + Px(usize), + Pe(usize), + Center, + Auto, + Default, +} + #[derive(PartialEq, Eq, Hash, Debug, Clone)] pub enum StyleName { Class(String), @@ -154,6 +172,12 @@ pub enum StyleName { #[derive(Debug, Clone)] pub struct StyleElement { pub color: Color, + pub background: Color, + pub font: Font, + pub x: Number, + pub y: Number, + pub width: Number, + pub height: Number, pub other: HashMap>, } @@ -161,11 +185,44 @@ impl Default for StyleElement { fn default() -> Self { StyleElement { color: Color::NoColor, + background: Color::NoColor, + font: Font::None, + x: Number::Default, + y: Number::Default, + width: Number::Default, + height: Number::Default, other: HashMap::new(), } } } +impl StyleElement { + pub fn prioritize(mut self, other: &Self) -> StyleElement { + if !other.color.is_null() { + self.color = other.color.clone(); + } + if !other.background.is_null() { + self.background = other.background.clone(); + } + if !other.font.is_null() { + self.font = other.font.clone(); + } + if other.x != Number::Default { + self.x = other.x.clone(); + } + if other.y != Number::Default { + self.y = other.y.clone(); + } + if other.width != Number::Default { + self.width = other.width.clone(); + } + if other.height != Number::Default { + self.height = other.height.clone(); + } + self + } +} + #[derive(Debug, Clone)] pub struct Style(pub StyleElement, pub HashMap); @@ -177,20 +234,30 @@ impl Default for Style { impl Style { pub fn write(&self, state: &str, s: &str) -> String { - if state == "" { - return self.0.write(s); - } if let Some(style_element) = self.1.get(state) { style_element.write(s) } else { - s.to_string() + return self.0.write(s); + } + } + pub fn result(&self, state: &str, s: String) -> RenderResult { + if let Some(style_element) = self.1.get(state) { + let style_element = self.0.clone().prioritize(&style_element.clone()); + RenderResult(s, (style_element.x.clone(), style_element.y.clone())) + } else { + RenderResult(s, (self.0.x.clone(), self.0.y.clone())) } } } impl StyleElement { pub fn write(&self, s: &str) -> String { - format!("{}{s}\x1b[0m", self.color.ansi()) + format!( + "{}{}{}{s}\x1b[0m", + self.color.ansi(), + self.background.ansi_bg(), + self.font.ansi() + ) } } diff --git a/src/utils.rs b/src/utils.rs index 388cf13..dd1a8bb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,11 +5,10 @@ /// # Example /// ```rust /// use crate::ui::utils::{create_frame, render_to_frame}; -/// +/// /// let frame = create_frame(50, 10); /// render_to_frame(crate::State::Normal, 50, &mut frame, &text_element); /// ``` - use crate::{Element, Value}; use lazy_static::lazy_static; use regex::Regex; @@ -49,7 +48,7 @@ pub fn compress_string(input: &str, re: &Regex) -> (String, HashMap String { +fn merge_line(frame_: &str, line_: &str, x: usize) -> (String, String) { let (frame_, fm) = compress_string(frame_, &ANSI); let (mut line_, lm) = compress_string(line_, &ANSI); @@ -82,35 +81,86 @@ fn merge_line(frame_: &str, line_: &str, x: usize) -> String { } } - res + (res, line_) } -/// Render to a frame -pub fn render_to_frame( - focused: bool, +pub struct Frame { + frame: Vec, width: usize, - frame: &mut Vec, - element: &Element, -) { - let data = element.get_data(); - for (i, line) in element.render(focused).split('\n').enumerate() { - if (data.1 + i) < frame.len() { - let frame_line = frame.get_mut(data.1 + i).unwrap(); - match data.0 { - Value::Custom(x) => { - *frame_line = merge_line(&frame_line, line, x); + used: Vec, +} + +impl Frame { + pub fn new(width: usize, height: usize) -> Self { + Self { + frame: vec![" ".repeat(width); height], + width, + used: vec![0; height], + } + } + 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 .1 { + 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; + } } - _ => { - for (x, c) in frame_line.chars().rev().enumerate() { - if c != ' ' { - *frame_line = merge_line(&frame_line, line, width - x); - return; - } + } + crate::ui::Number::Default => { + for (i, n) in self.used.iter().enumerate() { + if *n == 0 { + y = i; + break; } - *frame_line = merge_line(&frame_line, line, 0); } } } + match output.1 .0 { + 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; + } + } + } + pub fn output(&self) -> String { + self.frame.join("\n") + } + pub fn output_nnl(&self) -> String { + self.frame.join("") } } @@ -180,10 +230,6 @@ pub fn closest_component( .unwrap_or(current_index) // If no component is found, return the current index } -pub fn create_frame(width: usize, height: usize) -> Vec { - vec![" ".repeat(width); height] -} - pub fn get_term_size() -> (usize, usize) { let (width, height) = crossterm::terminal::size().unwrap(); (width as usize, height as usize) diff --git a/styling.md b/styling.md index be2752c..14b2dcd 100644 --- a/styling.md +++ b/styling.md @@ -6,4 +6,4 @@ `x`, `y`: `n`, `n%`, `center`, `auto`; -`display`: `row`, `column`, `overlap`; \ No newline at end of file + \ No newline at end of file From f8fd990c671300272d7456cad23db3e80564cee9 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 25 Nov 2024 03:10:28 +0100 Subject: [PATCH 14/18] some refractoring --- src/app.c | 10 +- src/app.rs | 20 +--- src/examples.rs | 16 +-- src/lib.rs | 278 ++++++++++++++++++++++++-------------------- src/main.rs | 4 +- src/rsx.rs | 37 +++--- src/ui/mod.rs | 51 ++++----- src/ui/styles.rs | 293 +++++++++++++++++++++++++---------------------- src/utils.rs | 4 +- 9 files changed, 370 insertions(+), 343 deletions(-) diff --git a/src/app.c b/src/app.c index 81b63ac..b5df522 100644 --- a/src/app.c +++ b/src/app.c @@ -4,24 +4,18 @@ #include #include -extern boolean_t render_loop(void* args); extern void *event_loop(void* args); extern void *cmd_loop(void* args); boolean_t c_run(void* args) { pthread_t event_thread; - pthread_t cmd_thread; if (pthread_create(&event_thread, NULL, event_loop, args) != 0) { perror("Failed to create thread"); } - if (pthread_create(&cmd_thread, NULL, cmd_loop, args) != 0) - { - perror("Failed to create thread"); - } - - return render_loop(args); + cmd_loop(args); + return FALSE; } \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 501b0bb..41f421e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -7,7 +7,7 @@ use crate::prelude::*; use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame}; use std::{ffi::c_void, ptr::null, sync::mpsc}; -pub struct LArgs<'a> { +struct LArgs<'a> { pub element: &'a mut Element, // for sending commands @@ -55,18 +55,6 @@ pub fn run(element: &mut Element) -> bool { } } -#[no_mangle] -extern "C" fn render_loop(ptr: *mut c_void) -> bool { - if ptr.is_null() { - return false; - } - let args = unsafe { &mut *(ptr as *mut LArgs) }; - while args.running { - sleep(1000); - } - false -} - #[no_mangle] extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { if ptr.is_null() { @@ -87,10 +75,7 @@ extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { ); document.render(); - while args.running { - if ptr.is_null() { - break; - } + while args.running || !ptr.is_null() { event(args.element, crossterm::event::read().unwrap(), &document); document.render(); } @@ -102,6 +87,7 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { if ptr.is_null() { return null() as *const c_void; } + let args = unsafe { &mut *(ptr as *mut LArgs) }; loop { diff --git a/src/examples.rs b/src/examples.rs index 9e1f807..0bfd708 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -1,21 +1,23 @@ use crate::prelude::*; -pub fn event_logger() -> Element { +pub fn examples() -> Element { rsx! { styling: Some(styling()), - button { class: "txt", "Hello, World!" } + if (2 == 2) { + rsx_elem! { button { class: "btn", "Hello, World!" } } + } } } pub fn styling() -> Css { css! { - .txt { - x: Center, - y: Center, + .btn { + x: 30%, + y: Auto, color: Red, } - - .txt: clicked { + + .btn: clicked { color: Blue, } } diff --git a/src/lib.rs b/src/lib.rs index fe706c5..ce09ac5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,16 +38,12 @@ pub mod utils; pub use app::run; -/// The `prelude` module provides commonly used imports and macros to simplify the development -/// of UI elements. It re-exports commonly used traits, structs, and utility functions to make -/// the UI framework easier to use. 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::{style, Command, Document, Element, Value}; - // useful for Element making pub use crate::{call, Children, ElementCore, ElementWidget}; + pub use crate::{style, Command, Document, Element, Value}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); @@ -55,7 +51,54 @@ pub mod prelude { } ////////////////////////////////////////////////////////////////////////////////////////////////// -/// Value Enum +// Type aliases +////////////////////////////////////////////////////////////////////////////////////////////////// + +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); + 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); +} + +pub trait ElementWidget: ElementCore + std::fmt::Debug { + fn render(&self, focused: bool) -> RenderResult; + + fn event(&mut self, event: Event, document: &Document) { + _ = (event, document) + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +/// Structs +////////////////////////////////////////////////////////////////////////////////////////////////// + +pub struct Handler(pub Arc>); + +pub struct Document { + cmd_sender: std::sync::mpsc::Sender, + cmd_recv: *const std::ffi::c_void, +} + +/// ``` +/// RenderResult(output, (x, y)) +/// ``` +pub struct RenderResult(String, ui::StyleElement); +pub struct RenderWriter { + w: String, + style: ui::Style, + focused: bool, +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +/// Enums ////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Clone, Copy)] @@ -64,6 +107,48 @@ pub enum Value { Custom(T), } +/// Child enum of an `Element` +/// +/// # Example +/// ``` +/// // Check if it's text +/// if let Children::Text(text) { +/// // do something +/// } +/// // Check if it's inner elements +/// if let Children::Children(text) { +/// // do something +/// } +/// // recommended: use get_text +/// children.get_text() // returns a empty string if it's not text +/// ``` +/// +/// # Useful for +/// - `Element::event` +/// - `Element::render` +/// - `Handler` +#[derive(Debug)] +pub enum Children { + None, + Text(String), + Children(Vec, usize), +} + +pub enum Command { + Exit, + Render, + GetElementById(String), +} + +pub enum CommandResult { + Element(*mut Element), + None, +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +/// Implementations +////////////////////////////////////////////////////////////////////////////////////////////////// + impl Value { pub fn get_value(&self) -> T { match self { @@ -85,89 +170,33 @@ impl Default for Value { } } -////////////////////////////////////////////////////////////////////////////////////////////////// -/// ElementCore Traits -////////////////////////////////////////////////////////////////////////////////////////////////// - -/// ``` -/// RenderResult(output, (x, y), (width, height)) -/// ``` -pub struct RenderResult(String, (ui::Number, ui::Number)); - -pub trait ElementCore: Send + Sync { - fn get_data(&self) -> (Value, usize, String); - fn update_data(&mut self, width: usize, height: usize); - 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); -} - -pub trait ElementWidget: ElementCore + std::fmt::Debug { - fn render(&self, focused: bool) -> RenderResult; - - fn event(&mut self, event: Event, document: &Document) { - _ = (event, document) +impl Document { + pub fn exit(&self) { + self.cmd_sender.send(Command::Exit).unwrap(); } -} - -pub type Element = Box; - -////////////////////////////////////////////////////////////////////////////////////////////////// -/// Handler Struct -////////////////////////////////////////////////////////////////////////////////////////////////// - -pub struct Handler(pub Arc>); - -impl Handler { - pub fn new(handler_fn: F) -> Handler - where - F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync, - { - Handler(Arc::new(Mutex::new(handler_fn))) + pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { + if let Some(e) = self.get_element_by_id_raw(id) { + Some(convert(unsafe { &mut *e })) + } else { + None + } } -} - -impl Default for Handler { - fn default() -> Self { - Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {}))) + pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> { + self.cmd_sender + .send(Command::GetElementById(id.to_string())) + .unwrap(); + let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; + if let Ok(CommandResult::Element(e)) = rx.recv() { + Some(e) + } else { + None + } } -} - -impl std::fmt::Debug for Handler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Handler()") + pub fn render(&self) { + self.cmd_sender.send(Command::Render).unwrap(); } } -////////////////////////////////////////////////////////////////////////////////////////////////// -/// Children Enum -////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Child enum of an `Element` -/// -/// # Example -/// ``` -/// // Check if it's text -/// if let Children::Text(text) { -/// // do something -/// } -/// // Check if it's inner elements -/// if let Children::Children(text) { -/// // do something -/// } -/// ``` -/// -/// # Useful for -/// - `Element::event` -/// - `Element::render` -/// - `Handler` -#[derive(Debug)] -pub enum Children { - None, - Text(String), - Children(Vec, usize), -} - impl Default for Children { fn default() -> Self { Self::None @@ -218,50 +247,45 @@ impl Children { } } -/// Converts a `Element` into the struct +impl Handler { + pub fn new(handler_fn: F) -> Handler + where + F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync, + { + Handler(Arc::new(Mutex::new(handler_fn))) + } +} + +impl RenderWriter { + pub fn new(focused: bool, style: ui::Style) -> RenderWriter { + RenderWriter { + w: String::new(), + style, + focused, + } + } + + pub fn write(&mut self, s: &str) { + self.w += self.style.get(self.focused).write(s).as_str(); + } + + pub fn result(&self) -> RenderResult { + RenderResult(self.w.clone(), self.style.get(self.focused).clone()) + } +} + +impl Default for Handler { + fn default() -> Self { + Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {}))) + } +} + +impl std::fmt::Debug for Handler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Handler()") + } +} + pub fn convert(widget: &mut Box) -> &mut Box { unsafe { &mut *(widget as *mut _ as *mut Box) } } - -pub enum Command { - Exit, - Render, - GetElementById(String), -} - -pub enum CommandResult { - Element(*mut Element), - None, -} - -pub struct Document { - cmd_sender: std::sync::mpsc::Sender, - cmd_recv: *const std::ffi::c_void, -} - -impl Document { - pub fn exit(&self) { - self.cmd_sender.send(Command::Exit).unwrap(); - } - pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { - if let Some(e) = self.get_element_by_id_raw(id) { - Some(convert(unsafe { &mut *e })) - } else { - None - } - } - pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> { - self.cmd_sender - .send(Command::GetElementById(id.to_string())) - .unwrap(); - let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; - if let Ok(CommandResult::Element(e)) = rx.recv() { - Some(e) - } else { - None - } - } - pub fn render(&self) { - self.cmd_sender.send(Command::Render).unwrap(); - } -} diff --git a/src/main.rs b/src/main.rs index c1682a3..83d651b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -use osui::prelude::*; use osui::examples; +use osui::prelude::*; fn main() { - while osui::run(&mut examples::event_logger()) {} + while osui::run(&mut examples::examples()) {} } diff --git a/src/rsx.rs b/src/rsx.rs index ea010bf..0e261af 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -1,6 +1,23 @@ #[macro_export] macro_rules! parse_rsx_param { - ($elem:expr, $($k:ident).+: $v:expr) => { + ($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 { + for $($for)* { + children.push($code) + } + } + 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; }; @@ -29,17 +46,7 @@ macro_rules! parse_rsx_param { osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, for ($($for:tt)*) { $($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 { - for $($for)* { - children.push({$($inner)*}) - } - } - osui::parse_rsx_param!($elem, $($rest)*); - }; - - ($elem:expr, {$inner_elem:expr} $($rest:tt)*) => { + ($elem:expr, {$inner_elem:expr} $($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}); @@ -47,7 +54,7 @@ macro_rules! parse_rsx_param { osui::parse_rsx_param!($elem, $($rest)*); }; - ($elem:expr, $elem_path:path { $($inner:tt)* } $($rest:tt)*) => { + ($elem:expr, $elem_path:path { $($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::rsx_elem!($elem_path { $($inner)* })); @@ -76,14 +83,14 @@ macro_rules! rsx_elem { } /// Makes a div and puts elements into it -/// +/// /// # Example /// ``` /// rsx! { /// button { class: "btn", "Click me!" } /// } /// ``` -/// +/// /// # Returns /// A `osui::Element` - Which is a `Box` #[macro_export] diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6c64ef2..2f4daf1 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -5,7 +5,7 @@ pub mod styles; pub use styles::*; -use crate::{prelude::*, RenderResult}; +use crate::{prelude::*, RenderResult, RenderWriter}; use osui_element::{elem_fn, element}; /// The `Text` element represents a piece of static text in the terminal UI. @@ -31,17 +31,18 @@ pub struct Text { } impl ElementWidget for Text<'_> { - fn render(&self, _: bool) -> RenderResult { - self.style.result( - "", - self.style.write("", &{ - if let Children::Text(text) = &self.children { - text.clone() - } else { - String::new() - } - }), - ) + fn render(&self, focused: bool) -> RenderResult { + let mut writer = RenderWriter::new(focused, self.style.clone()); + + writer.write(&{ + if let Children::Text(text) = &self.children { + text.clone() + } else { + String::new() + } + }); + + writer.result() } fn event(&mut self, event: Event, document: &Document) { call!(self.on_event(event, document)); @@ -77,6 +78,7 @@ pub struct Div { impl ElementWidget for Div<'_> { fn render(&self, focused: bool) -> RenderResult { + let mut writer = RenderWriter::new(focused, self.style.clone()); let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12); if let Children::Children(children, child) = &self.children { @@ -84,8 +86,9 @@ impl ElementWidget for Div<'_> { frame.render(focused && i == *child, elem); } } + writer.write(&frame.output()); - self.style.result("", frame.output()) + writer.result() } fn event(&mut self, event: Event, document: &Document) { @@ -145,25 +148,13 @@ impl ElementWidget for Div<'_> { #[derive(Default, Debug)] pub struct Button { pub on_click: Handler>, - pub state: &'a str, } impl ElementWidget for Button<'_> { fn render(&self, focused: bool) -> RenderResult { - if self.state != "" { - self.style.result( - self.state, - self.style.write(self.state, &self.children.get_text()), - ) - } else if focused { - self.style.result( - "hover", - self.style.write("hover", &self.children.get_text()), - ) - } else { - self.style - .result("", self.style.write("", &self.children.get_text())) - } + let mut writer = RenderWriter::new(focused, self.style.clone()); + writer.write(&self.children.get_text()); + writer.result() } fn event(&mut self, event: Event, document: &Document) { @@ -171,11 +162,11 @@ impl ElementWidget for Button<'_> { Event::Key(key) => { if key.code == KeyCode::Enter { document.render(); - self.state = "clicked"; + self.style.set_state("clicked"); call!(self.on_click(event, document)); document.render(); sleep(100); - self.state = ""; + self.style.set_state(""); } } _ => {} diff --git a/src/ui/styles.rs b/src/ui/styles.rs index 48a57d0..7573daf 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -6,16 +6,27 @@ use std::{collections::HashMap, fmt::Debug}; use dyn_clone::{clone_trait_object, DynClone}; -use crate::RenderResult; +////////////////////////////////////////////////////////////////////////////////////////////////// +// Type aliases +////////////////////////////////////////////////////////////////////////////////////////////////// + +pub type Css = HashMap; + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Traits +////////////////////////////////////////////////////////////////////////////////////////////////// pub trait StyleCore: Debug + Send + Sync + DynClone { fn ansi(&self) -> String; fn ansi_bg(&self) -> String; fn is_null(&self) -> bool; } - clone_trait_object!(StyleCore); +////////////////////////////////////////////////////////////////////////////////////////////////// +// Enums +////////////////////////////////////////////////////////////////////////////////////////////////// + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Font { None, @@ -27,32 +38,6 @@ pub enum Font { Mul(Vec), } -impl StyleCore for Font { - fn ansi(&self) -> String { - String::from(match self { - Font::None => "", - Font::Bold => "\x1b[1m", - Font::Underline => "\x1b[4m", - Font::Italic => "\x1b[3m", - Font::Reverse => "\x1b[7m", - Font::Strike => "\x1b[9m", - Font::Mul(v) => { - let mut s = String::new(); - for n in v { - s += n.ansi().as_str(); - } - return s; - } - }) - } - fn ansi_bg(&self) -> String { - self.ansi() - } - fn is_null(&self) -> bool { - *self == Self::None - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Color { NoColor, @@ -68,6 +53,128 @@ pub enum Color { White, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Number { + Px(usize), + Pe(usize), + Center, + Auto, + Default, +} + +#[derive(PartialEq, Eq, Hash, Debug, Clone)] +pub enum StyleName { + Class(String), + Id(String), + Component(String), +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Structs +////////////////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Debug, Clone)] +pub struct StyleElement { + pub color: Color, + pub background: Color, + pub font: Font, + pub x: Number, + pub y: Number, + pub width: Number, + pub height: Number, + pub other: HashMap>, +} + +#[derive(Debug, Clone)] +pub struct Style( + pub StyleElement, + pub HashMap, + pub String, +); + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Implementations +////////////////////////////////////////////////////////////////////////////////////////////////// + +impl Default for Style { + fn default() -> Self { + Style(StyleElement::default(), HashMap::new(), String::new()) + } +} + +impl Style { + pub fn get(&self, hover: bool) -> StyleElement { + if let Some(style_element) = self.1.get(&self.2) { + self.0.clone().prioritize(style_element) + } else if hover { + if let Some(style_element) = self.1.get("hover") { + self.0.clone().prioritize(style_element) + } else { + self.0.clone() + } + } else { + self.0.clone() + } + } + + pub fn set_state(&mut self, state: &str) { + self.2 = state.to_string(); + } +} + +impl StyleElement { + pub fn write(&self, s: &str) -> String { + format!( + "{}{}{}{s}\x1b[0m", + self.color.ansi(), + self.background.ansi_bg(), + self.font.ansi() + ) + } +} + +impl Default for StyleElement { + fn default() -> Self { + StyleElement { + color: Color::NoColor, + background: Color::NoColor, + font: Font::None, + x: Number::Default, + y: Number::Default, + width: Number::Default, + height: Number::Default, + other: HashMap::new(), + } + } +} + +impl StyleElement { + pub fn prioritize(mut self, other: &Self) -> StyleElement { + if !other.color.is_null() { + self.color = other.color.clone(); + } + if !other.background.is_null() { + self.background = other.background.clone(); + } + if !other.font.is_null() { + self.font = other.font.clone(); + } + if other.x != Number::Default { + self.x = other.x.clone(); + } + if other.y != Number::Default { + self.y = other.y.clone(); + } + if other.width != Number::Default { + self.width = other.width.clone(); + } + if other.height != Number::Default { + self.height = other.height.clone(); + } + self + } +} + impl StyleCore for Color { fn ansi(&self) -> String { String::from(match self { @@ -153,112 +260,28 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) { (r, g, b) } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Number { - Px(usize), - Pe(usize), - Center, - Auto, - Default, -} - -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -pub enum StyleName { - Class(String), - Id(String), - Component(String), -} - -#[derive(Debug, Clone)] -pub struct StyleElement { - pub color: Color, - pub background: Color, - pub font: Font, - pub x: Number, - pub y: Number, - pub width: Number, - pub height: Number, - pub other: HashMap>, -} - -impl Default for StyleElement { - fn default() -> Self { - StyleElement { - color: Color::NoColor, - background: Color::NoColor, - font: Font::None, - x: Number::Default, - y: Number::Default, - width: Number::Default, - height: Number::Default, - other: HashMap::new(), - } +impl StyleCore for Font { + fn ansi(&self) -> String { + String::from(match self { + Font::None => "", + Font::Bold => "\x1b[1m", + Font::Underline => "\x1b[4m", + Font::Italic => "\x1b[3m", + Font::Reverse => "\x1b[7m", + Font::Strike => "\x1b[9m", + Font::Mul(v) => { + let mut s = String::new(); + for n in v { + s += n.ansi().as_str(); + } + return s; + } + }) + } + fn ansi_bg(&self) -> String { + self.ansi() + } + fn is_null(&self) -> bool { + *self == Self::None } } - -impl StyleElement { - pub fn prioritize(mut self, other: &Self) -> StyleElement { - if !other.color.is_null() { - self.color = other.color.clone(); - } - if !other.background.is_null() { - self.background = other.background.clone(); - } - if !other.font.is_null() { - self.font = other.font.clone(); - } - if other.x != Number::Default { - self.x = other.x.clone(); - } - if other.y != Number::Default { - self.y = other.y.clone(); - } - if other.width != Number::Default { - self.width = other.width.clone(); - } - if other.height != Number::Default { - self.height = other.height.clone(); - } - self - } -} - -#[derive(Debug, Clone)] -pub struct Style(pub StyleElement, pub HashMap); - -impl Default for Style { - fn default() -> Self { - Style(StyleElement::default(), HashMap::new()) - } -} - -impl Style { - pub fn write(&self, state: &str, s: &str) -> String { - if let Some(style_element) = self.1.get(state) { - style_element.write(s) - } else { - return self.0.write(s); - } - } - pub fn result(&self, state: &str, s: String) -> RenderResult { - if let Some(style_element) = self.1.get(state) { - let style_element = self.0.clone().prioritize(&style_element.clone()); - RenderResult(s, (style_element.x.clone(), style_element.y.clone())) - } else { - RenderResult(s, (self.0.x.clone(), self.0.y.clone())) - } - } -} - -impl StyleElement { - pub fn write(&self, s: &str) -> String { - format!( - "{}{}{}{s}\x1b[0m", - self.color.ansi(), - self.background.ansi_bg(), - self.font.ansi() - ) - } -} - -pub type Css = HashMap; diff --git a/src/utils.rs b/src/utils.rs index dd1a8bb..eb29c27 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -103,7 +103,7 @@ impl Frame { let output = element.render(focused); let x; let mut y = 0; - match output.1 .1 { + match output.1.y { crate::ui::Number::Px(_y) => { y = _y; } @@ -128,7 +128,7 @@ impl Frame { } } } - match output.1 .0 { + match output.1.x { crate::ui::Number::Px(_x) => { x = _x; } From 0266c26d344f85533f13a897d646ad22123fb5df Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 25 Nov 2024 14:30:59 +0100 Subject: [PATCH 15/18] some refractoring and dataholder --- Cargo.toml | 2 +- src/app.rs | 25 +++----- src/examples.rs | 12 +++- src/lib.rs | 22 +++++--- src/rsx.rs | 11 +--- src/ui/mod.rs | 81 +++++++++++--------------- src/utils.rs | 147 ++++++++++++++++-------------------------------- 7 files changed, 116 insertions(+), 184 deletions(-) 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) From 6d86e8a42baf5a9a780af3017521be881c1ae943 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 25 Nov 2024 22:15:32 +0100 Subject: [PATCH 16/18] huge improvedments --- .DS_Store | Bin 6148 -> 6148 bytes build.rs | 4 -- build.sh | 4 -- src/app.c | 21 --------- src/app.rs | 112 ------------------------------------------------ src/examples.rs | 14 +----- src/lib.rs | 93 +++++++++++++++++++++++++++++----------- src/macros.rs | 7 +++ src/main.rs | 2 +- src/rsx.rs | 6 +-- src/ui/mod.rs | 36 +--------------- 11 files changed, 82 insertions(+), 217 deletions(-) delete mode 100644 build.rs delete mode 100755 build.sh delete mode 100644 src/app.c delete mode 100644 src/app.rs diff --git a/.DS_Store b/.DS_Store index b4c51467bb0f93faeae1d267b2e19b70aefec959..3b35e3e03a7796aecbd1fac04779c75fe628b60a 100644 GIT binary patch delta 32 ocmZoMXfc@J&&abeU^gQp&t@K`V#dv49D*zp3(7aMbNuB80G&z+fB*mh delta 163 zcmZoMXfc@J&nU1lU^gS9z-At%V#azlh7yKEh9ZV^hE#@ -#include -#include -#include -#include - -extern void *event_loop(void* args); -extern void *cmd_loop(void* args); - -boolean_t c_run(void* args) -{ - pthread_t event_thread; - - if (pthread_create(&event_thread, NULL, event_loop, args) != 0) - { - perror("Failed to create thread"); - } - - cmd_loop(args); - return FALSE; -} \ No newline at end of file diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index 89d288f..0000000 --- a/src/app.rs +++ /dev/null @@ -1,112 +0,0 @@ -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}; -use std::{ffi::c_void, ptr::null, sync::mpsc}; - -struct LArgs<'a> { - pub element: &'a mut Element, - - // for sending commands - pub stx: mpsc::Sender, - pub srx: mpsc::Receiver, - - // for returning command output - pub rtx: mpsc::Sender, - pub rrx: mpsc::Receiver, - pub running: bool, -} - -extern "C" { - fn c_run(args: *mut c_void) -> bool; -} - -pub fn run(element: &mut Element) -> bool { - hide_cursor(); - enable_raw_mode().unwrap(); - clear(); - unsafe { - let (stx, srx) = mpsc::channel(); - let (rtx, rrx) = mpsc::channel(); - let c = c_run(&mut LArgs { - running: true, - element, - stx, - srx, - rtx, - rrx, - } as *mut LArgs as *mut c_void); - if !c { - show_cursor(); - disable_raw_mode().unwrap(); - clear(); - } - return c; - } -} - -#[no_mangle] -extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void { - if ptr.is_null() { - return null() as *const c_void; - } - let args = unsafe { &mut *(ptr as *mut LArgs) }; - let document = Document { - cmd_sender: args.stx.clone(), - cmd_recv: &args.rrx as *const mpsc::Receiver as *const c_void, - }; - - let (width, height) = get_term_size(); - 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() { - args.element - .event(crossterm::event::read().unwrap(), &document); - document.render(); - } - null() as *const c_void -} - -#[no_mangle] -extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void { - if ptr.is_null() { - return null() as *const c_void; - } - - let args = unsafe { &mut *(ptr as *mut LArgs) }; - - loop { - match args.srx.recv().unwrap() { - Command::Exit => { - args.running = false; - break; - } - Command::GetElementById(id) => { - 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_id() == id { - crate::CommandResult::Element(args.element as *mut Element) - } else { - crate::CommandResult::None - }) - .unwrap(); - } - Command::Render => { - let (width, height) = get_term_size(); - let mut frame = Frame::new(width, height); - frame.render(true, args.element); - clear(); - print!("{}", frame.output_nnl()); - flush(); - } - } - } - null() -} diff --git a/src/examples.rs b/src/examples.rs index 28e1910..f08fbc4 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -3,24 +3,14 @@ use crate::prelude::*; pub fn examples() -> Element { rsx! { styling: Some(styling()), - 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 - } + button { class: "btn", "Hello, World!" } } } pub fn styling() -> Css { css! { .btn { - x: 30%, + x: Center, y: Auto, color: Red, } diff --git a/src/lib.rs b/src/lib.rs index 6252a67..be7e663 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,9 +26,6 @@ use std::{ sync::{Arc, Mutex}, }; -use crossterm::event::Event; - -pub mod app; pub mod css; pub mod examples; pub mod macros; @@ -36,13 +33,11 @@ pub mod rsx; pub mod ui; pub mod utils; -pub use app::run; - 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, Element}; + pub use crate::{self as osui, css, ersx, rsx, ui::*, Handler, launch}; + pub use crate::{call, Children, Element, ElementCore, ElementWidget}; pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { @@ -68,7 +63,7 @@ pub trait ElementCore { pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync { fn render(&self, focused: bool) -> Option; - fn event(&mut self, event: Event, document: &Document) { + fn event(&mut self, event: crossterm::event::Event, document: &Document) { _ = (event, document) } } @@ -77,11 +72,13 @@ pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync { /// Structs ////////////////////////////////////////////////////////////////////////////////////////////////// -pub struct Handler(pub Arc>); +pub struct Handler( + pub Arc>, +); pub struct Document { - cmd_sender: std::sync::mpsc::Sender, - cmd_recv: *const std::ffi::c_void, + element: *mut Element, + running: *mut Option, } /// ``` @@ -169,28 +166,48 @@ impl Default for Value { impl Document { pub fn exit(&self) { - self.cmd_sender.send(Command::Exit).unwrap(); + if !self.running.is_null() { + unsafe { + *self.running = Some(false); + } + } + } + pub fn rebuild(&self) { + if !self.running.is_null() { + unsafe { + *self.running = Some(true); + } + } } pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { if let Some(e) = self.get_element_by_id_raw(id) { - Some(convert(unsafe { &mut *e })) + Some(unsafe { &mut *(e as *mut Element as *mut Box) }) } else { None } } - pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> { - self.cmd_sender - .send(Command::GetElementById(id.to_string())) - .unwrap(); - let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver) }; - if let Ok(CommandResult::Element(e)) = rx.recv() { - Some(e) + pub fn get_element_by_id_raw(&self, id: &str) -> Option<&mut Element> { + if !self.element.is_null() { + unsafe { + if (*self.element).get_id() == id { + Some(&mut *self.element) + } else { + (*self.element).get_element_by_id(id) + } + } } else { None } } pub fn render(&self) { - self.cmd_sender.send(Command::Render).unwrap(); + if !self.running.is_null() { + let (width, height) = utils::get_term_size(); + let mut frame = utils::Frame::new(width, height); + frame.render(true, unsafe { &*self.element }); + utils::clear(); + print!("{}", frame.output_nnl()); + utils::flush(); + } } } @@ -254,7 +271,7 @@ impl Children { impl Handler { pub fn new(handler_fn: F) -> Handler where - F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync, + F: FnMut(&mut T, crossterm::event::Event, &Document) + 'static + Send + Sync, { Handler(Arc::new(Mutex::new(handler_fn))) } @@ -280,7 +297,9 @@ impl RenderWriter { impl Default for Handler { fn default() -> Self { - Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {}))) + Handler(Arc::new(Mutex::new( + |_: &mut T, _: crossterm::event::Event, _: &Document| {}, + ))) } } @@ -290,6 +309,30 @@ impl std::fmt::Debug for Handler { } } -pub fn convert(widget: &mut Box) -> &mut Box { - unsafe { &mut *(widget as *mut _ as *mut Box) } +pub fn run(element: &mut Element) -> bool { + let document = Document { + element: element, + running: &mut None, + }; + let (width, height) = crate::utils::get_term_size(); + element.event( + crossterm::event::Event::Resize(width as u16, height as u16), + &document, + ); + element.event(crossterm::event::Event::FocusGained, &document); + + utils::hide_cursor(); + crossterm::terminal::enable_raw_mode().unwrap(); + utils::clear(); + + while unsafe { *document.running } == None { + document.render(); + element.event(crossterm::event::read().unwrap(), &document); + } + + utils::show_cursor(); + crossterm::terminal::disable_raw_mode().unwrap(); + utils::clear(); + + unsafe { (*document.running).unwrap() } } diff --git a/src/macros.rs b/src/macros.rs index 71694dd..9804bd6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,3 +16,10 @@ macro_rules! call { &$self.$handler; }}; } + +#[macro_export] +macro_rules! launch { + ($expr:expr) => { + while $crate::run(&mut $expr) {} + }; +} diff --git a/src/main.rs b/src/main.rs index 83d651b..6a2f33b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,5 +2,5 @@ use osui::examples; use osui::prelude::*; fn main() { - while osui::run(&mut examples::examples()) {} + launch!(examples::examples()); } diff --git a/src/rsx.rs b/src/rsx.rs index ce3f97b..8015222 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -50,7 +50,7 @@ macro_rules! parse_rsx_param { ($elem:expr, $elem_path:path { $($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::rsx_elem!($elem_path { $($inner)* })); + children.push(osui::ersx!($elem_path { $($inner)* })); } osui::parse_rsx_param!($elem, $($rest)*) }; @@ -66,7 +66,7 @@ macro_rules! parse_rsx_param { } #[macro_export] -macro_rules! rsx_elem { +macro_rules! ersx { ($elem:path { $($inner:tt)* }) => {{ #[allow(unused_mut)] let mut elem = $elem(); @@ -89,7 +89,7 @@ macro_rules! rsx_elem { #[macro_export] macro_rules! rsx { ($($inner:tt)*) => { - $crate::rsx_elem!{ $crate::ui::div { $($inner)* } } + $crate::ersx!{ $crate::ui::div { $($inner)* } } }; } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 23b8a79..f4813d2 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -9,21 +9,6 @@ pub use styles::*; use crate::prelude::*; use osui_element::{elem_fn, element}; -/// The `Text` element represents a piece of static text in the terminal UI. -/// -/// It is used to render plain text within a UI component. The `Text` element does not interact -/// with events and only outputs the text when rendered. -/// -/// # Example -/// ```rust -/// let text = Text::default(); -/// let rendered_text = text.render(crate::State::Normal); -/// println!("{}", rendered_text); -/// ``` -/// -/// ## Methods -/// - `render(state: crate::State) -> String`: Renders the text to a string, depending on the state. -/// If the element contains text as a child, it returns that text. Otherwise, it returns an empty string. #[element] #[elem_fn] #[derive(Default, Debug)] @@ -50,26 +35,6 @@ impl ElementWidget for Text<'_> { } } -/// The `Div` element represents a container for other UI elements. It can hold child components -/// and render them in a specific layout. The `Div` element also handles keyboard navigation through -/// its children. -/// -/// The `Div` element supports rendering a list of children and allows interactive navigation using -/// arrow keys (Up, Down, Left, Right). It renders the selected child element differently based on the state. -/// -/// # Example -/// ```rust -/// let div = Div::default(); -/// let rendered_div = div.render(crate::State::Normal); -/// println!("{}", rendered_div); -/// ``` -/// -/// ## Methods -/// - `render(state: crate::State) -> String`: Renders the `Div` and its child elements to a string. -/// The child elements are rendered according to the current selection (`child` index). -/// - `event(event: Event, state: &crate::StateChanger)`: Handles keyboard events for -/// navigation through child elements. Arrow keys (Up, Down, Left, Right) change the currently selected child. - #[element] #[elem_fn] #[derive(Default, Debug)] @@ -128,6 +93,7 @@ impl ElementWidget for Button<'_> { document.render(); sleep(100); self.style.set_state(""); + document.rebuild(); } } _ => {} From fd4f905f22b31647a1e4be2a885217802b644631 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 25 Nov 2024 23:45:32 +0100 Subject: [PATCH 17/18] small refractor --- README.md | 7 ++--- src/css.rs | 9 +++++++ src/examples.rs | 7 +++-- src/lib.rs | 66 +++--------------------------------------------- src/macros.rs | 7 ----- src/ui/styles.rs | 44 ++++++++++++++++++-------------- 6 files changed, 45 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 488b810..b62090d 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,8 @@ ## Getting Started -To use OSUI in your project, include it in your `Cargo.toml`: - -```toml -[dependencies] -osui = "0.2" # Replace with the latest version +```bash +cargo add osui ``` ## Example Usage diff --git a/src/css.rs b/src/css.rs index a7e88d4..19eddcc 100644 --- a/src/css.rs +++ b/src/css.rs @@ -25,6 +25,14 @@ macro_rules! __css { $crate::__css!($_style, $($($other)*)?); }}; + // Vector + ( + $_style:expr, $name:ident: [$($inner:tt),*] $(, $($other:tt)*)? + ) => {{ + $_style.$name = vec![$($inner),*]; + $crate::__css!($_style, $($($other)*)?); + }}; + // Normal ( $_style:expr, $name:ident: $value:expr $(, $($other:tt)*)? @@ -32,6 +40,7 @@ macro_rules! __css { $_style.$name = $value; $crate::__css!($_style, $($($other)*)?); }}; + ( $_style:expr, ($name:ident): $value:expr $(, $($other:tt)*)? ) => {{ diff --git a/src/examples.rs b/src/examples.rs index f08fbc4..24deab3 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -3,7 +3,10 @@ use crate::prelude::*; pub fn examples() -> Element { rsx! { styling: Some(styling()), - button { class: "btn", "Hello, World!" } + + button { class: "btn", "Click me!" } + + text { "Welcome!" } } } @@ -11,7 +14,7 @@ pub fn styling() -> Css { css! { .btn { x: Center, - y: Auto, + y: Center, color: Red, } diff --git a/src/lib.rs b/src/lib.rs index be7e663..a28f81c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,12 @@ //! ```rust //! use osui::prelude::*; //! -//! run(&mut rsx! { +//! launch(rsx! { //! text { "Hello, World!" } //! }); //! ``` //! //! ## Modules -//! - `app` - The main function for rendering. //! - `ui` - Contains all user interface components, enabling rich CLI experiences. //! - `utils` - Utility functions for common TUI tasks such as clearing the screen. @@ -36,9 +35,10 @@ pub mod utils; pub mod prelude { pub use crate::ui::Color::*; pub use crate::ui::Number::*; - pub use crate::{self as osui, css, ersx, rsx, ui::*, Handler, launch}; + pub use crate::ui::Font::*; + pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler}; pub use crate::{call, Children, Element, ElementCore, ElementWidget}; - pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value}; + pub use crate::{style, Document, RenderResult, RenderWriter}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); @@ -95,32 +95,6 @@ pub struct RenderWriter { /// Enums ////////////////////////////////////////////////////////////////////////////////////////////////// -#[derive(Debug, Clone, Copy)] -pub enum Value { - Default(T), - Custom(T), -} - -/// Child enum of an `Element` -/// -/// # Example -/// ``` -/// // Check if it's text -/// if let Children::Text(text) { -/// // do something -/// } -/// // Check if it's inner elements -/// if let Children::Children(text) { -/// // do something -/// } -/// // recommended: use get_text -/// children.get_text() // returns a empty string if it's not text -/// ``` -/// -/// # Useful for -/// - `Element::event` -/// - `Element::render` -/// - `Handler` #[derive(Debug)] pub enum Children { None, @@ -128,42 +102,10 @@ pub enum Children { Children(Vec, usize), } -pub enum Command { - Exit, - Render, - GetElementById(String), -} - -pub enum CommandResult { - Element(*mut Element), - None, -} - ////////////////////////////////////////////////////////////////////////////////////////////////// /// Implementations ////////////////////////////////////////////////////////////////////////////////////////////////// -impl Value { - pub fn get_value(&self) -> T { - match self { - Value::Custom(s) => *s, - Value::Default(s) => *s, - } - } - - pub fn try_set_value(&mut self, value: T) { - if let Value::Default(_) = *self { - *self = Value::Default(value); - } - } -} - -impl Default for Value { - fn default() -> Self { - Self::Default(T::default()) - } -} - impl Document { pub fn exit(&self) { if !self.running.is_null() { diff --git a/src/macros.rs b/src/macros.rs index 9804bd6..b5db83a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,10 +1,3 @@ -#[macro_export] -macro_rules! val { - ($a:expr) => { - $crate::Value::new($a) - }; -} - #[macro_export] macro_rules! call { ($self:ident.$handler:ident ($($inner:tt)*)) => {{ diff --git a/src/ui/styles.rs b/src/ui/styles.rs index 7573daf..68eac40 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -29,13 +29,11 @@ clone_trait_object!(StyleCore); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Font { - None, Bold, Underline, Italic, Reverse, Strike, - Mul(Vec), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -77,7 +75,7 @@ pub enum StyleName { pub struct StyleElement { pub color: Color, pub background: Color, - pub font: Font, + pub font: Vec, pub x: Number, pub y: Number, pub width: Number, @@ -138,7 +136,7 @@ impl Default for StyleElement { StyleElement { color: Color::NoColor, background: Color::NoColor, - font: Font::None, + font: Vec::new(), x: Number::Default, y: Number::Default, width: Number::Default, @@ -228,12 +226,6 @@ impl Default for Color { } } -impl Default for Font { - fn default() -> Self { - Font::None - } -} - fn hex_to_rgb(hex: &str) -> (u8, u8, u8) { let hex = hex.trim_start_matches('#'); @@ -263,25 +255,39 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) { impl StyleCore for Font { fn ansi(&self) -> String { String::from(match self { - Font::None => "", Font::Bold => "\x1b[1m", Font::Underline => "\x1b[4m", Font::Italic => "\x1b[3m", Font::Reverse => "\x1b[7m", Font::Strike => "\x1b[9m", - Font::Mul(v) => { - let mut s = String::new(); - for n in v { - s += n.ansi().as_str(); - } - return s; - } }) } fn ansi_bg(&self) -> String { self.ansi() } fn is_null(&self) -> bool { - *self == Self::None + false + } +} + +impl StyleCore for Vec { + fn ansi(&self) -> String { + let mut a = String::new(); + for i in self { + a += i.ansi().as_str(); + } + a + } + + fn ansi_bg(&self) -> String { + let mut a = String::new(); + for i in self { + a += i.ansi_bg().as_str(); + } + a + } + + fn is_null(&self) -> bool { + self.len() == 0 } } From 96f75a5fcebd20b3ec5e95a6d0d791627b2a6cc5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Tue, 26 Nov 2024 10:34:06 +0100 Subject: [PATCH 18/18] v0.0.6 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9561a3e..56edcc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "osui" -version = "0.0.5" +version = "0.0.6" edition = "2021" license = "Apache-2.0" description = "A rsx TUI library"