finally fixed style merging

This commit is contained in:
2024-12-17 11:11:55 +01:00
parent f753a27bbc
commit 317542097c
7 changed files with 137 additions and 122 deletions
+1 -1
View File
@@ -26,4 +26,4 @@ lazy_static = "1.4"
paste = "1.0.15" paste = "1.0.15"
dyn-clone = "1.0.17" dyn-clone = "1.0.17"
flume = "=0.10.12" flume = "=0.10.12"
osui-element = "0.1.13" osui-element = "0.1.14"
+14 -30
View File
@@ -5,7 +5,7 @@ macro_rules! __css {
( (
$_style:expr, $name:ident: $value:literal% $(, $($rest:tt)*)? $_style:expr, $name:ident: $value:literal% $(, $($rest:tt)*)?
) => {{ ) => {{
$_style.$name = Number::Pe($value); $_style.$name = (true, Number::Pe($value));
$crate::__css!($_style, $($($rest)*)?); $crate::__css!($_style, $($($rest)*)?);
}}; }};
@@ -13,7 +13,7 @@ macro_rules! __css {
( (
$_style:expr, $name:ident: $value:literal px $(, $($rest:tt)*)? $_style:expr, $name:ident: $value:literal px $(, $($rest:tt)*)?
) => {{ ) => {{
$_style.$name = Number::Px($value); $_style.$name = (true, Number::Px($value));
$crate::__css!($_style, $($($rest)*)?); $crate::__css!($_style, $($($rest)*)?);
}}; }};
@@ -21,7 +21,7 @@ macro_rules! __css {
( (
$_style:expr, $name:ident: [$($inner:tt),*] $(, $($rest:tt)*)? $_style:expr, $name:ident: [$($inner:tt),*] $(, $($rest:tt)*)?
) => {{ ) => {{
$_style.$name = vec![$($inner),*]; $_style.$name = (true, vec![$($inner),*]);
$crate::__css!($_style, $($($rest)*)?); $crate::__css!($_style, $($($rest)*)?);
}}; }};
@@ -29,7 +29,7 @@ macro_rules! __css {
( (
$_style:expr, $name:ident: $value:expr $(, $($rest:tt)*)? $_style:expr, $name:ident: $value:expr $(, $($rest:tt)*)?
) => {{ ) => {{
$_style.$name = $value; $_style.$name = (true, $value);
$crate::__css!($_style, $($($rest)*)?); $crate::__css!($_style, $($($rest)*)?);
}}; }};
} }
@@ -40,55 +40,39 @@ macro_rules! _css {
( (
$hm:expr, $name:literal { $($inner:tt)* } $($rest:tt)* $hm:expr, $name:literal { $($inner:tt)* } $($rest:tt)*
) => {{ ) => {{
let name = $crate::ui::StyleName::Class(String::from($name)); let mut _style = $crate::ui::StyleElement::default();
if let Some(_style) = $hm.get_mut(&name) { $crate::__css!(_style, $($inner)*);
$crate::__css!(_style.0, $($inner)*);
} else {
let mut _style = $crate::ui::Style::default();
$crate::__css!(_style.0, $($inner)*);
$hm.insert( $hm.insert(
name, $crate::ui::StyleName::Class(String::from($name)),
_style, _style,
); );
}
$crate::_css!($hm, $($rest)*); $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)); let mut _style = $crate::ui::StyleElement::default();
if let Some(_style) = $hm.get_mut(&name) { $crate::__css!(_style, $($inner)*);
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( $hm.insert(
name, $crate::ui::StyleName::ClassState(String::from($name), String::from($state)),
_style, _style,
); );
}
$crate::_css!($hm, $($rest)*); $crate::_css!($hm, $($rest)*);
}}; }};
} }
/// Creates a styling map `Css` /// Creates a styling map `Css`
/// ///
/// # Examples /// # Example
/// ``` /// ```
/// css! {
/// "btn" { /// "btn" {
/// color: Red, /// color: Red,
/// } /// }
/// "btn": "clicked" { /// "btn": "clicked" {
/// color: Blue, /// color: Blue,
/// } /// }
/// }
/// ``` /// ```
/// ///
/// # Returns /// # Returns
+2 -2
View File
@@ -4,8 +4,8 @@ pub fn todo_app() -> Element {
let todo = vec!["Foo", "Bar"]; let todo = vec!["Foo", "Bar"];
rsx! { rsx! {
// text { class: "title", "Todo list app made with OSUI" } text { class: "title", "Todo list app made with OSUI" }
// text { class: "title", "{}", "-".repeat(crossterm::terminal::size().unwrap().0 as usize) } text { class: "title", "{}", "-".repeat(crossterm::terminal::size().unwrap().0 as usize) }
for (t in todo) { for (t in todo) {
ersx!( button{ class: "todo", on_click: fn(btn: &mut Button, _, _) { ersx!( button{ class: "todo", on_click: fn(btn: &mut Button, _, _) {
btn.class = if btn.class == "todo" { btn.class = if btn.class == "todo" {
+17 -10
View File
@@ -59,8 +59,8 @@ pub type Element = Box<dyn ElementWidget>;
pub trait ElementCore { pub trait ElementCore {
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>; fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>;
fn set_styling(&mut self, styling: &HashMap<crate::ui::StyleName, crate::ui::Style>);
fn get_id(&self) -> String; fn get_id(&self) -> String;
fn get_class(&self) -> String;
fn get_style(&self) -> &Style; fn get_style(&self) -> &Style;
} }
@@ -85,6 +85,7 @@ pub struct Document {
#[derive(Debug)] #[derive(Debug)]
pub struct Writer { pub struct Writer {
css: *const ui::Css,
style: ui::StyleElement, style: ui::StyleElement,
focused: bool, focused: bool,
size: (u16, u16), size: (u16, u16),
@@ -162,11 +163,11 @@ impl Document {
} }
pub fn render(&self) { pub fn render(&self) {
if !self.element.is_null() { if !self.element.is_null() {
unsafe { // unsafe {
(*self.element).set_styling(&self.css); // (*self.element).set_styling(&self.css);
} // }
let (width, height) = crossterm::terminal::size().unwrap(); 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(); utils::clear();
frame.render(true, unsafe { &*self.element }); frame.render(true, unsafe { &*self.element });
utils::flush(); utils::flush();
@@ -304,8 +305,10 @@ impl Writer {
style: ui::StyleElement, style: ui::StyleElement,
absolute: (u16, u16), absolute: (u16, u16),
size: (u16, u16), size: (u16, u16),
css: &ui::Css,
) -> Writer { ) -> Writer {
Writer { Writer {
css,
style, style,
focused, focused,
size, size,
@@ -338,10 +341,12 @@ impl Writer {
( (
self.style self.style
.width .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 self.style
.height .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 self.style
.width .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 self.style
.height .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 { 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 })
} }
} }
+4 -5
View File
@@ -39,11 +39,6 @@ impl ElementWidget for Div<'_> {
} }
fn event(&mut self, event: Event, document: &Document) { 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() { if let (Some((child, i)), l) = self.children.get_child_idx() {
match event { match event {
Event::Key(k) => match k.code { Event::Key(k) => match k.code {
@@ -78,6 +73,7 @@ impl ElementWidget for Div<'_> {
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Button { pub struct Button {
pub on_click: Handler<Button<'a>>, pub on_click: Handler<Button<'a>>,
pub on_hover: Handler<Button<'a>>,
} }
impl ElementWidget for Button<'_> { impl ElementWidget for Button<'_> {
@@ -97,6 +93,9 @@ impl ElementWidget for Button<'_> {
self.style.set_state(""); self.style.set_state("");
} }
} }
Event::FocusGained => {
call!(self.on_hover(event, document));
}
_ => {} _ => {}
} }
} }
+56 -56
View File
@@ -10,7 +10,7 @@ use dyn_clone::{clone_trait_object, DynClone};
// Type aliases // Type aliases
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
pub type Css = HashMap<StyleName, Style>; pub type Css = HashMap<StyleName, StyleElement>;
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
// Traits // Traits
@@ -19,7 +19,6 @@ pub type Css = HashMap<StyleName, Style>;
pub trait StyleCore: Debug + Send + Sync + DynClone { pub trait StyleCore: Debug + Send + Sync + DynClone {
fn ansi(&self) -> String; fn ansi(&self) -> String;
fn ansi_bg(&self) -> String; fn ansi_bg(&self) -> String;
fn is_null(&self) -> bool;
} }
clone_trait_object!(StyleCore); clone_trait_object!(StyleCore);
@@ -65,6 +64,7 @@ pub enum StyleName {
Class(String), Class(String),
Id(String), Id(String),
Component(String), Component(String),
ClassState(String, String),
} }
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
@@ -73,16 +73,16 @@ pub enum StyleName {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StyleElement { pub struct StyleElement {
pub color: Color, pub color: (bool, Color),
pub background: Color, pub background: (bool, Color),
pub outline_color: Color, pub outline_color: (bool, Color),
pub font: Vec<Font>, pub font: (bool, Vec<Font>),
pub x: Number, pub x: (bool, Number),
pub y: Number, pub y: (bool, Number),
pub width: Number, pub width: (bool, Number),
pub height: Number, pub height: (bool, Number),
pub visible: bool, pub visible: (bool, bool),
pub outline: bool, pub outline: (bool, bool),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -103,17 +103,19 @@ impl Default for Style {
} }
impl 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) { 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 { } else if hover {
if let Some(style_element) = self.1.get("hover") { if let Some(style_element) = self.1.get("hover") {
self.0.clone().prioritize(style_element) self.0.merge(style_element);
self.0
} else { } else {
self.0.clone() self.0
} }
} else { } else {
self.0.clone() self.0
} }
} }
@@ -129,9 +131,9 @@ impl StyleElement {
.map(|p| { .map(|p| {
format!( format!(
"{}{}{}{p}\x1b[0m", "{}{}{}{p}\x1b[0m",
self.color.ansi(), self.color.1.ansi(),
self.background.ansi_bg(), self.background.1.ansi_bg(),
self.font.ansi() self.font.1.ansi()
) )
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
@@ -142,44 +144,52 @@ impl StyleElement {
impl Default for StyleElement { impl Default for StyleElement {
fn default() -> Self { fn default() -> Self {
StyleElement { StyleElement {
color: Color::NoColor, color: (false, Color::NoColor),
background: Color::NoColor, background: (false, Color::NoColor),
font: Vec::new(), font: (false, Vec::new()),
outline_color: Color::NoColor, outline_color: (false, Color::NoColor),
x: Number::Default, x: (false, Number::Default),
y: Number::Px(0), y: (false, Number::Px(0)),
width: Number::Default, width: (false, Number::Default),
height: Number::Default, height: (false, Number::Default),
visible: true, visible: (false, true),
outline: false, outline: (false, false),
} }
} }
} }
impl StyleElement { impl StyleElement {
pub fn prioritize(mut self, other: &Self) -> StyleElement { pub fn merge(&mut self, upper: &Self) {
if !other.color.is_null() { if upper.color.0 {
self.color = other.color.clone(); self.color = upper.color.clone();
} }
if !other.background.is_null() { if upper.background.0 {
self.background = other.background.clone(); self.background = upper.background.clone();
} }
if !other.font.is_null() { if upper.font.0 {
self.font = other.font.clone(); self.font = upper.font.clone();
} }
if other.x != Number::Default { if upper.outline_color.0 {
self.x = other.x.clone(); self.outline_color = upper.outline_color.clone();
} }
if other.y != Number::Default { if upper.x.0 {
self.y = other.y.clone(); self.x = upper.x.clone();
} }
if other.width != Number::Default { if upper.y.0 {
self.width = other.width.clone(); self.y = upper.y.clone();
} }
if other.height != Number::Default { if upper.width.0 {
self.height = other.height.clone(); 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", Color::White => "\x1b[47m",
}) })
} }
fn is_null(&self) -> bool {
*self == Self::NoColor
}
} }
impl Default for Color { impl Default for Color {
@@ -275,9 +282,6 @@ impl StyleCore for Font {
fn ansi_bg(&self) -> String { fn ansi_bg(&self) -> String {
self.ansi() self.ansi()
} }
fn is_null(&self) -> bool {
false
}
} }
impl StyleCore for Vec<Font> { impl StyleCore for Vec<Font> {
@@ -296,10 +300,6 @@ impl StyleCore for Vec<Font> {
} }
a a
} }
fn is_null(&self) -> bool {
self.len() == 0
}
} }
impl Number { impl Number {
+32 -7
View File
@@ -2,28 +2,52 @@ use std::io::{stdout, Write};
use crossterm::terminal::disable_raw_mode; use crossterm::terminal::disable_raw_mode;
use crate::ui;
pub struct Frame { pub struct Frame {
used: Vec<u16>, used: Vec<u16>,
width: u16, width: u16,
height: u16, height: u16,
x: u16, x: u16,
y: u16, y: u16,
css: *const ui::Css,
} }
impl Frame { 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 { Frame {
used: vec![0; height as usize], used: vec![0; height as usize],
width, width,
height, height,
x, x,
y, y,
css,
} }
} }
pub fn render(&mut self, focused: bool, element: &crate::Element) { pub fn render(&mut self, focused: bool, element: &crate::Element) {
let style = element.get_style().get(focused); let style = element.get_style();
if style.visible { let mut style_element = style.clone().get(focused);
let y = style.y.as_position(
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; let mut y = 0;
while y < self.used.len() && self.used[y] != 0 { while y < self.used.len() && self.used[y] != 0 {
@@ -34,16 +58,17 @@ impl Frame {
self.height, self.height,
); );
if let Some(y_) = self.used.get(y as usize) { 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( let mut writer = crate::Writer::new(
focused, focused,
style.clone(), style_element.clone(),
(x + self.x, y + self.y), (x + self.x, y + self.y),
(self.width, self.height), (self.width, self.height),
unsafe { &*self.css },
); );
element.render(&mut writer); element.render(&mut writer);
let (width, height) = writer.get_size_root(); let (width, height) = writer.get_size_root();
if style.outline { if style_element.outline.1 {
clear(); clear();
show_cursor(); show_cursor();
disable_raw_mode().unwrap(); disable_raw_mode().unwrap();