diff --git a/Cargo.toml b/Cargo.toml index ec7e5c9..1fbef0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,4 +26,4 @@ lazy_static = "1.4" paste = "1.0.15" dyn-clone = "1.0.17" flume = "=0.10.12" -osui-element = "0.1.13" +osui-element = "0.1.14" diff --git a/src/css.rs b/src/css.rs index 0feef17..013ca9d 100644 --- a/src/css.rs +++ b/src/css.rs @@ -5,7 +5,7 @@ macro_rules! __css { ( $_style:expr, $name:ident: $value:literal% $(, $($rest:tt)*)? ) => {{ - $_style.$name = Number::Pe($value); + $_style.$name = (true, Number::Pe($value)); $crate::__css!($_style, $($($rest)*)?); }}; @@ -13,7 +13,7 @@ macro_rules! __css { ( $_style:expr, $name:ident: $value:literal px $(, $($rest:tt)*)? ) => {{ - $_style.$name = Number::Px($value); + $_style.$name = (true, Number::Px($value)); $crate::__css!($_style, $($($rest)*)?); }}; @@ -21,7 +21,7 @@ macro_rules! __css { ( $_style:expr, $name:ident: [$($inner:tt),*] $(, $($rest:tt)*)? ) => {{ - $_style.$name = vec![$($inner),*]; + $_style.$name = (true, vec![$($inner),*]); $crate::__css!($_style, $($($rest)*)?); }}; @@ -29,7 +29,7 @@ macro_rules! __css { ( $_style:expr, $name:ident: $value:expr $(, $($rest:tt)*)? ) => {{ - $_style.$name = $value; + $_style.$name = (true, $value); $crate::__css!($_style, $($($rest)*)?); }}; } @@ -40,54 +40,38 @@ macro_rules! _css { ( $hm:expr, $name:literal { $($inner:tt)* } $($rest:tt)* ) => {{ - let name = $crate::ui::StyleName::Class(String::from($name)); - 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, - ); - } + let mut _style = $crate::ui::StyleElement::default(); + $crate::__css!(_style, $($inner)*); + $hm.insert( + $crate::ui::StyleName::Class(String::from($name)), + _style, + ); $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, $name:literal: $kind:literal { $($inner:tt)* } $($rest:tt)* + $hm:expr, $name:literal: $state:literal { $($inner:tt)* } $($rest:tt)* ) => {{ - let name = $crate::ui::StyleName::Class(String::from($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($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($kind.to_string(), style_elem); - - $hm.insert( - name, - _style, - ); - } + let mut _style = $crate::ui::StyleElement::default(); + $crate::__css!(_style, $($inner)*); + $hm.insert( + $crate::ui::StyleName::ClassState(String::from($name), String::from($state)), + _style, + ); $crate::_css!($hm, $($rest)*); }}; } /// Creates a styling map `Css` /// -/// # Examples +/// # Example /// ``` -/// "btn" { -/// color: Red, -/// } -/// "btn": "clicked" { -/// color: Blue, +/// css! { +/// "btn" { +/// color: Red, +/// } +/// "btn": "clicked" { +/// color: Blue, +/// } /// } /// ``` /// diff --git a/src/examples.rs b/src/examples.rs index 4185172..0e85165 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -4,8 +4,8 @@ pub fn todo_app() -> Element { let todo = vec!["Foo", "Bar"]; rsx! { - // text { class: "title", "Todo list app made with OSUI" } - // text { class: "title", "{}", "-".repeat(crossterm::terminal::size().unwrap().0 as usize) } + text { class: "title", "Todo list app made with OSUI" } + text { class: "title", "{}", "-".repeat(crossterm::terminal::size().unwrap().0 as usize) } for (t in todo) { ersx!( button{ class: "todo", on_click: fn(btn: &mut Button, _, _) { btn.class = if btn.class == "todo" { diff --git a/src/lib.rs b/src/lib.rs index 1c8d872..3d624ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,8 +59,8 @@ pub type Element = Box; pub trait ElementCore { fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>; - fn set_styling(&mut self, styling: &HashMap); fn get_id(&self) -> String; + fn get_class(&self) -> String; fn get_style(&self) -> &Style; } @@ -85,6 +85,7 @@ pub struct Document { #[derive(Debug)] pub struct Writer { + css: *const ui::Css, style: ui::StyleElement, focused: bool, size: (u16, u16), @@ -162,11 +163,11 @@ impl Document { } pub fn render(&self) { if !self.element.is_null() { - unsafe { - (*self.element).set_styling(&self.css); - } + // unsafe { + // (*self.element).set_styling(&self.css); + // } let (width, height) = crossterm::terminal::size().unwrap(); - let mut frame = utils::Frame::new((0, 0), (width, height)); + let mut frame = utils::Frame::new((0, 0), (width, height), &self.css); utils::clear(); frame.render(true, unsafe { &*self.element }); utils::flush(); @@ -304,8 +305,10 @@ impl Writer { style: ui::StyleElement, absolute: (u16, u16), size: (u16, u16), + css: &ui::Css, ) -> Writer { Writer { + css, style, focused, size, @@ -338,10 +341,12 @@ impl Writer { ( self.style .width - .as_size(self.size.0, self.size.0, self.style.outline), + .1 + .as_size(self.size.0, self.size.0, self.style.outline.1), self.style .height - .as_size(self.size.1, self.size.1, self.style.outline), + .1 + .as_size(self.size.1, self.size.1, self.style.outline.1), ) } @@ -349,15 +354,17 @@ impl Writer { ( self.style .width - .as_size(self.written.0, self.size.0, self.style.outline), + .1 + .as_size(self.written.0, self.size.0, self.style.outline.1), self.style .height - .as_size(self.written.1, self.size.1, self.style.outline), + .1 + .as_size(self.written.1, self.size.1, self.style.outline.1), ) } pub fn new_frame(&self) -> crate::utils::Frame { - crate::utils::Frame::new(self.absolute, self.get_size()) + crate::utils::Frame::new(self.absolute, self.get_size(), unsafe { &*self.css }) } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 76fa2f2..41d53b9 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -39,11 +39,6 @@ impl ElementWidget for Div<'_> { } fn event(&mut self, event: Event, document: &Document) { - if event == Event::FocusGained { - if let Some(styling) = self.styling.clone() { - self.set_styling(&styling); - } - } if let (Some((child, i)), l) = self.children.get_child_idx() { match event { Event::Key(k) => match k.code { @@ -78,6 +73,7 @@ impl ElementWidget for Div<'_> { #[derive(Default, Debug)] pub struct Button { pub on_click: Handler>, + pub on_hover: Handler>, } impl ElementWidget for Button<'_> { @@ -97,6 +93,9 @@ impl ElementWidget for Button<'_> { self.style.set_state(""); } } + Event::FocusGained => { + call!(self.on_hover(event, document)); + } _ => {} } } diff --git a/src/ui/styles.rs b/src/ui/styles.rs index 4a5fb22..50dc162 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -10,7 +10,7 @@ use dyn_clone::{clone_trait_object, DynClone}; // Type aliases ////////////////////////////////////////////////////////////////////////////////////////////////// -pub type Css = HashMap; +pub type Css = HashMap; ////////////////////////////////////////////////////////////////////////////////////////////////// // Traits @@ -19,7 +19,6 @@ pub type Css = HashMap; 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); @@ -65,6 +64,7 @@ pub enum StyleName { Class(String), Id(String), Component(String), + ClassState(String, String), } ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,16 +73,16 @@ pub enum StyleName { #[derive(Debug, Clone)] pub struct StyleElement { - pub color: Color, - pub background: Color, - pub outline_color: Color, - pub font: Vec, - pub x: Number, - pub y: Number, - pub width: Number, - pub height: Number, - pub visible: bool, - pub outline: bool, + pub color: (bool, Color), + pub background: (bool, Color), + pub outline_color: (bool, Color), + pub font: (bool, Vec), + pub x: (bool, Number), + pub y: (bool, Number), + pub width: (bool, Number), + pub height: (bool, Number), + pub visible: (bool, bool), + pub outline: (bool, bool), } #[derive(Debug, Clone)] @@ -103,17 +103,19 @@ impl Default for Style { } impl Style { - pub fn get(&self, hover: bool) -> StyleElement { + pub fn get(mut self, hover: bool) -> StyleElement { if let Some(style_element) = self.1.get(&self.2) { - self.0.clone().prioritize(style_element) + self.0.merge(style_element); + self.0 } else if hover { if let Some(style_element) = self.1.get("hover") { - self.0.clone().prioritize(style_element) + self.0.merge(style_element); + self.0 } else { - self.0.clone() + self.0 } } else { - self.0.clone() + self.0 } } @@ -129,9 +131,9 @@ impl StyleElement { .map(|p| { format!( "{}{}{}{p}\x1b[0m", - self.color.ansi(), - self.background.ansi_bg(), - self.font.ansi() + self.color.1.ansi(), + self.background.1.ansi_bg(), + self.font.1.ansi() ) }) .collect::>() @@ -142,44 +144,52 @@ impl StyleElement { impl Default for StyleElement { fn default() -> Self { StyleElement { - color: Color::NoColor, - background: Color::NoColor, - font: Vec::new(), - outline_color: Color::NoColor, - x: Number::Default, - y: Number::Px(0), - width: Number::Default, - height: Number::Default, - visible: true, - outline: false, + color: (false, Color::NoColor), + background: (false, Color::NoColor), + font: (false, Vec::new()), + outline_color: (false, Color::NoColor), + x: (false, Number::Default), + y: (false, Number::Px(0)), + width: (false, Number::Default), + height: (false, Number::Default), + visible: (false, true), + outline: (false, false), } } } impl StyleElement { - pub fn prioritize(mut self, other: &Self) -> StyleElement { - if !other.color.is_null() { - self.color = other.color.clone(); + pub fn merge(&mut self, upper: &Self) { + if upper.color.0 { + self.color = upper.color.clone(); } - if !other.background.is_null() { - self.background = other.background.clone(); + if upper.background.0 { + self.background = upper.background.clone(); } - if !other.font.is_null() { - self.font = other.font.clone(); + if upper.font.0 { + self.font = upper.font.clone(); } - if other.x != Number::Default { - self.x = other.x.clone(); + if upper.outline_color.0 { + self.outline_color = upper.outline_color.clone(); } - if other.y != Number::Default { - self.y = other.y.clone(); + if upper.x.0 { + self.x = upper.x.clone(); } - if other.width != Number::Default { - self.width = other.width.clone(); + if upper.y.0 { + self.y = upper.y.clone(); } - if other.height != Number::Default { - self.height = other.height.clone(); + if upper.width.0 { + self.width = upper.width.clone(); + } + if upper.height.0 { + self.height = upper.height.clone(); + } + if upper.visible.0 { + self.visible = upper.visible.clone(); + } + if upper.outline.0 { + self.outline = upper.outline.clone(); } - self } } @@ -225,9 +235,6 @@ impl StyleCore for Color { Color::White => "\x1b[47m", }) } - fn is_null(&self) -> bool { - *self == Self::NoColor - } } impl Default for Color { @@ -275,9 +282,6 @@ impl StyleCore for Font { fn ansi_bg(&self) -> String { self.ansi() } - fn is_null(&self) -> bool { - false - } } impl StyleCore for Vec { @@ -296,10 +300,6 @@ impl StyleCore for Vec { } a } - - fn is_null(&self) -> bool { - self.len() == 0 - } } impl Number { diff --git a/src/utils.rs b/src/utils.rs index 9caa5ae..265e8a5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,28 +2,52 @@ use std::io::{stdout, Write}; use crossterm::terminal::disable_raw_mode; +use crate::ui; + pub struct Frame { used: Vec, width: u16, height: u16, x: u16, y: u16, + css: *const ui::Css, } impl Frame { - pub fn new((x, y): (u16, u16), (width, height): (u16, u16)) -> Self { + pub fn new((x, y): (u16, u16), (width, height): (u16, u16), css: &crate::ui::Css) -> Self { Frame { used: vec![0; height as usize], width, height, x, y, + css, } } pub fn render(&mut self, focused: bool, element: &crate::Element) { - let style = element.get_style().get(focused); - if style.visible { - let y = style.y.as_position( + let style = element.get_style(); + let mut style_element = style.clone().get(focused); + + if let Some(upper) = unsafe { (*self.css).get(&ui::StyleName::Class(element.get_class())) } + { + style_element.merge(upper); + } + + if let Some(upper) = unsafe { + (*self.css).get(&ui::StyleName::ClassState( + element.get_class(), + if style.2 == "" && focused { + "hover".to_string() + } else { + style.2.clone() + }, + )) + } { + style_element.merge(upper); + } + + if style_element.visible.1 { + let y = style_element.y.1.as_position( &{ let mut y = 0; while y < self.used.len() && self.used[y] != 0 { @@ -34,16 +58,17 @@ impl Frame { self.height, ); if let Some(y_) = self.used.get(y as usize) { - let x = style.x.as_position(y_, self.width); + let x = style_element.x.1.as_position(y_, self.width); let mut writer = crate::Writer::new( focused, - style.clone(), + style_element.clone(), (x + self.x, y + self.y), (self.width, self.height), + unsafe { &*self.css }, ); element.render(&mut writer); let (width, height) = writer.get_size_root(); - if style.outline { + if style_element.outline.1 { clear(); show_cursor(); disable_raw_mode().unwrap();