diff --git a/src/css.rs b/src/css.rs index 013ca9d..6622ad4 100644 --- a/src/css.rs +++ b/src/css.rs @@ -38,25 +38,55 @@ macro_rules! __css { macro_rules! _css { ($_style:expr,) => {}; ( - $hm:expr, $name:literal { $($inner:tt)* } $($rest:tt)* + $hm:expr, $($($name:ident)-+),* { $($inner:tt)* } $($rest:tt)* ) => {{ let mut _style = $crate::ui::StyleElement::default(); $crate::__css!(_style, $($inner)*); - $hm.insert( - $crate::ui::StyleName::Class(String::from($name)), - _style, - ); + $( + $hm.insert( + $crate::ui::StyleName::Class(String::from(stringify!($($name)-+).replace(' ', ""))), + _style.clone(), + ); + )* $crate::_css!($hm, $($rest)*); }}; ( - $hm:expr, $name:literal: $state:literal { $($inner:tt)* } $($rest:tt)* + $hm:expr, $($($name:ident)-+),*: $state:literal { $($inner:tt)* } $($rest:tt)* ) => {{ let mut _style = $crate::ui::StyleElement::default(); $crate::__css!(_style, $($inner)*); - $hm.insert( - $crate::ui::StyleName::ClassState(String::from($name), String::from($state)), - _style, - ); + $( + $hm.insert( + $crate::ui::StyleName::ClassState(String::from(stringify!($($name)-+).replace(' ', "")), String::from($state)), + _style.clone(), + ); + )* + $crate::_css!($hm, $($rest)*); + }}; + ( + $hm:expr, $($name:literal),+ { $($inner:tt)* } $($rest:tt)* + ) => {{ + 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),+: $state:literal { $($inner:tt)* } $($rest:tt)* + ) => {{ + 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)*); }}; } diff --git a/src/examples.rs b/src/examples.rs index 15d5989..fc938cc 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -22,7 +22,7 @@ pub mod counter_example { pub fn styling() -> Css { css! { - "btn" { + btn { outline: true, outline_color: Red } @@ -39,6 +39,7 @@ pub mod todo_example { rsx! { @SetStyle(styling()) + @SetChild(2) text { class: "title", static "Todo list app made with OSUI" } text { class: "title", "{}", "-".repeat(crossterm::terminal::size().unwrap().0 as usize) } @@ -56,27 +57,27 @@ pub mod todo_example { pub fn styling() -> Css { css! { - "todo" { + todo { x: 0 px, y: Auto, color: Red, } - "todo": "hover" { + todo: "hover" { color: Blue, } - "complete" { + complete { x: 0 px, y: Auto, color: Green, } - "complete": "hover" { + complete: "hover" { color: Cyan } - "title" { + title { x: 0 px, y: Auto, } - "title": "hover" { + title: "hover" { color: Blue } } @@ -115,14 +116,14 @@ pub mod login_example { pub fn styling() -> Css { css! { - "input" { + input { width: 50 px, outline: true, y: Auto, x: 0 px, caret: String::new(), } - "input": "hover" { + input: "hover" { outline_color: Red, caret: String::from("█"), } diff --git a/src/main.rs b/src/main.rs index f127dda..ef3cb7f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,20 @@ fn main() { #[component] pub fn App() { rsx! { - @SetStyle(app_style()) + @SetChild(1) + @SetStyle(css! { + example { + outline: true, + y: Auto, + x: 0 px, + } + example: "hover" { + color: Blue, + outline_color: Red, + } + }) - text { static "Select an example" } + %text { class: "red-bg", static "Select an example" } button { class: "example", on_click: fn(_, _, document) { document.clear_css(); @@ -26,17 +37,3 @@ pub fn App() { }, static "Login" } } } - -pub fn app_style() -> Css { - css! { - "example" { - outline: true, - y: Auto, - x: 0 px, - } - "example": "hover" { - color: Blue, - outline_color: Red, - } - } -} diff --git a/src/rsx.rs b/src/rsx.rs index b180b0d..9fb8d59 100644 --- a/src/rsx.rs +++ b/src/rsx.rs @@ -12,6 +12,16 @@ macro_rules! check_expr { } #[macro_export] macro_rules! parse_rsx_param { + ($elem:expr, %$elem_path:path { $($inner:tt)* } $($rest:tt)*) => { + $elem.ghosts.push($elem.ghosts.len()); + if let $crate::Children::Children(children, _) = &mut $elem.children { + children.push($crate::ersx!($elem_path { $($inner)* })); + } else { + $elem.children = $crate::Children::Children(vec![$crate::ersx!($elem_path { $($inner)* })], 0) + } + osui::parse_rsx_param!($elem, $($rest)*) + }; + ($elem:expr, @ $($i:ident)::+($($inner:tt)*) $($rest:tt)*) => { $elem.instructions.push($($i)::+($($inner)*)); osui::parse_rsx_param!($elem, $($rest)*); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index c0562c9..5134e53 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -32,12 +32,15 @@ pub fn text<'a>(class: &'a str, id: &'a str, children: Children, style: Style) - #[derive(Debug)] pub enum Instruction { SetStyle(Css), + SetChild(usize), + Ghost(usize), } #[element] #[derive(Default, Debug)] pub struct Div { pub instructions: Vec, + pub ghosts: Vec, } impl ElementWidget for Div<'_> { @@ -63,6 +66,13 @@ impl ElementWidget for Div<'_> { } else { *i = 0; } + while self.ghosts.get(*i).is_some() { + if *i < l - 1 { + *i += 1; + } else { + *i = 0; + } + } child.event(Event::FocusGained, document); } KeyCode::BackTab => { @@ -72,6 +82,13 @@ impl ElementWidget for Div<'_> { } else { *i = l - 1; } + while self.ghosts.get(*i).is_some() { + if *i > 0 { + *i -= 1; + } else { + *i = l - 1; + } + } child.event(Event::FocusGained, document); } _ => child.event(event, document), @@ -87,6 +104,12 @@ impl ElementWidget for Div<'_> { Instruction::SetStyle(s) => { document.css.extend(s.clone().into_iter()); } + Instruction::SetChild(c) => { + self.children.set_index(*c); + } + Instruction::Ghost(idx) => { + self.ghosts.push(*idx); + } } } } @@ -99,6 +122,7 @@ pub fn div<'a>( children: Children, style: Style, instructions: Vec, + ghosts: Vec, ) -> Box> { Box::new(Div { children: self.children, @@ -106,6 +130,7 @@ pub fn div<'a>( id: self.id, style: self.style, instructions: self.instructions, + ghosts: self.ghosts, }) } diff --git a/src/utils.rs b/src/utils.rs index 38098e6..549f584 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -26,19 +26,21 @@ impl<'a> Frame<'a> { let style = element.get_style(); let mut style_element = style.clone().get(focused); - if let Some(upper) = self.css.get(&ui::StyleName::Class(element.get_class())) { - style_element.merge(upper); - } + for class in element.get_class().split(" ") { + if let Some(upper) = self.css.get(&ui::StyleName::Class(class.to_string())) { + style_element.merge(upper); + } - if let Some(upper) = 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 let Some(upper) = self.css.get(&ui::StyleName::ClassState( + class.to_string(), + if style.2 == "" && focused { + "hover".to_string() + } else { + style.2.clone() + }, + )) { + style_element.merge(upper); + } } if style_element.visible.1 {