diff --git a/src/ui/color.rs b/src/ui/color.rs index 13ba7cb..c42e898 100644 --- a/src/ui/color.rs +++ b/src/ui/color.rs @@ -12,6 +12,8 @@ pub trait ColorCircle { fn next(&self) -> Self; } +// todo : specific colors rgb + macro_rules! color_map { ($name:ident, $id:literal) => { #[derive(Debug, Clone)] diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0f5427a..602b811 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,6 @@ use components::Shell; use maud::{Markup, PreEscaped, Render}; +use prelude::Text; // UI @@ -45,6 +46,9 @@ pub mod prelude { TextDecoration, TextHyphens, TextOverflow, TextTransform, TextWhitespace, TextWordBreak, TextWrap, UnderlineOffset, VerticalTextAlignment, }; + pub use super::primitives::transform::{ + RenderTransformCPU, RenderTransformGPU, Rotate, Scale, Skew, Transform, TransformOrigin, + }; pub use super::primitives::visibility::Visibility; pub use super::primitives::width::{MaxWidth, MinWidth, Width}; pub use super::primitives::zindex::ZIndex; @@ -133,6 +137,42 @@ impl UIWidget for PreEscaped { } } +impl UIWidget for String { + fn can_inherit(&self) -> bool { + Text(&self).can_inherit() + } + + fn base_class(&self) -> Vec { + Text(&self).base_class() + } + + fn extended_class(&self) -> Vec { + Text(&self).extended_class() + } + + fn render_with_class(&self, class: &str) -> Markup { + Text(&self).render_with_class(class) + } +} + +impl UIWidget for &str { + fn can_inherit(&self) -> bool { + Text(&self).can_inherit() + } + + fn base_class(&self) -> Vec { + Text(&self).base_class() + } + + fn extended_class(&self) -> Vec { + Text(&self).extended_class() + } + + fn render_with_class(&self, class: &str) -> Markup { + Text(&self).render_with_class(class) + } +} + /// Trait for an element which can add new `attrs` pub trait AttrExtendable { #[must_use] diff --git a/src/ui/primitives/div.rs b/src/ui/primitives/div.rs index 3175d57..0d1c8d6 100644 --- a/src/ui/primitives/div.rs +++ b/src/ui/primitives/div.rs @@ -116,11 +116,7 @@ impl UIWidget for DivWidget { } fn extended_class(&self) -> Vec { - if self.1 { - self.extended_class_() - } else { - vec![] - } + vec![] } fn render_with_class(&self, class: &str) -> Markup { diff --git a/src/ui/primitives/mod.rs b/src/ui/primitives/mod.rs index d99d291..0e1c096 100644 --- a/src/ui/primitives/mod.rs +++ b/src/ui/primitives/mod.rs @@ -21,6 +21,7 @@ pub mod shadow; pub mod sized; pub mod space; pub mod text; +pub mod transform; pub mod visibility; pub mod width; pub mod zindex; @@ -104,6 +105,7 @@ pub enum Side { TopRight, BottomRight, BottomLeft, + Center, } impl Side { @@ -124,6 +126,7 @@ impl Side { Self::TopRight => "tr", Self::BottomRight => "br", Self::BottomLeft => "bl", + Self::Center => "center", } } } diff --git a/src/ui/primitives/text.rs b/src/ui/primitives/text.rs index 82d9b4f..5230a1f 100644 --- a/src/ui/primitives/text.rs +++ b/src/ui/primitives/text.rs @@ -348,7 +348,7 @@ impl TextWidget { #[must_use] pub fn title(mut self, title: &str) -> Self { - self.title = Some(title.to_string()); + self.title = Some(title.replace('\'', "\\'")); self } diff --git a/src/ui/primitives/transform.rs b/src/ui/primitives/transform.rs new file mode 100644 index 0000000..ff7a512 --- /dev/null +++ b/src/ui/primitives/transform.rs @@ -0,0 +1,384 @@ +use crate::ui::UIWidget; +use maud::{Markup, Render, html}; + +use super::{ + Side, + flex::Either, + space::{Fraction, ScreenValue}, +}; + +#[allow(non_snake_case)] +pub fn Scale(size: f64, inner: T) -> ScaleWidget { + ScaleWidget(Box::new(inner), size, 0) +} + +pub struct ScaleWidget(Box, f64, u8); + +impl ScaleWidget { + pub fn x(mut self) -> Self { + self.2 = 1; + self + } + + pub fn y(mut self) -> Self { + self.2 = 2; + self + } +} + +impl Render for ScaleWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for ScaleWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + match self.2 { + 1 => vec![format!("scale-x-[{:.2}]", self.1)], + 2 => vec![format!("scale-y-[{:.2}]", self.1)], + _ => vec![format!("scale-[{:.2}]", self.1)], + } + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.0.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.0.as_ref().can_inherit() { + self.0 + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.0.as_ref()) + } + } + } + } +} + +#[allow(non_snake_case)] +pub fn Rotate(deg: u32, inner: T) -> RotateWidget { + RotateWidget(Box::new(inner), deg) +} + +pub struct RotateWidget(Box, u32); + +impl Render for RotateWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for RotateWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + vec![format!("rotate-[{:.2}deg]", self.1)] + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.0.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.0.as_ref().can_inherit() { + self.0 + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.0.as_ref()) + } + } + } + } +} + +#[allow(non_snake_case)] +pub fn RenderTransformCPU(inner: T) -> HardwareAccelerationWidget { + HardwareAccelerationWidget(Box::new(inner), 0) +} + +#[allow(non_snake_case)] +pub fn RenderTransformGPU(inner: T) -> HardwareAccelerationWidget { + HardwareAccelerationWidget(Box::new(inner), 1) +} + +pub struct HardwareAccelerationWidget(Box, u8); + +impl Render for HardwareAccelerationWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for HardwareAccelerationWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + match self.1 { + 1 => vec!["transform-gpu".to_string()], + _ => vec!["transform-cpu".to_string()], + } + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.0.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.0.as_ref().can_inherit() { + self.0 + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.0.as_ref()) + } + } + } + } +} + +#[allow(non_snake_case)] +pub fn Transform< + T: UIWidget + 'static, + X: Into>, + Y: Into>, +>( + x: Option, + y: Option, + inner: T, +) -> TransformWidget { + TransformWidget { + inner: Box::new(inner), + x: x.map(|x| x.into()), + y: y.map(|y| y.into()), + } +} + +pub struct TransformWidget { + inner: Box, + x: Option>, + y: Option>, +} + +impl Render for TransformWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for TransformWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let mut ret = Vec::new(); + + if let Some(x) = &self.x { + ret.push(format!( + "translate-x-{}", + x.map(|x| x.to_value().to_string(), |x| x.to_value().to_string()) + )); + } + + if let Some(y) = &self.y { + ret.push(format!( + "translate-y-{}", + y.map(|y| y.to_value().to_string(), |y| y.to_value().to_string()) + )); + } + + ret + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.inner.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.inner.as_ref().can_inherit() { + self.inner + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.inner.as_ref()) + } + } + } + } +} + +pub enum SkewValue { + _0, + _1, + _2, + _3, + _6, + _12, +} + +impl SkewValue { + pub const fn to_value(&self) -> &str { + match self { + SkewValue::_0 => "0", + SkewValue::_1 => "1", + SkewValue::_2 => "2", + SkewValue::_3 => "3", + SkewValue::_6 => "6", + SkewValue::_12 => "12", + } + } +} + +#[allow(non_snake_case)] +pub fn Skew( + x: Option, + y: Option, + inner: T, +) -> SkewWidget { + SkewWidget { + inner: Box::new(inner), + x, + y, + } +} + +pub struct SkewWidget { + inner: Box, + x: Option, + y: Option, +} + +impl Render for SkewWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for SkewWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let mut ret = Vec::new(); + + if let Some(x) = &self.x { + ret.push(format!("skew-x-{}", x.to_value())); + } + + if let Some(y) = &self.y { + ret.push(format!("skew-y-{}", y.to_value())); + } + + ret + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.inner.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.inner.as_ref().can_inherit() { + self.inner + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.inner.as_ref()) + } + } + } + } +} + +#[allow(non_snake_case)] +pub fn TransformOrigin(origin: Side, inner: T) -> TransformOriginWidget { + TransformOriginWidget(Box::new(inner), origin) +} + +pub struct TransformOriginWidget(Box, Side); + +impl Render for TransformOriginWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for TransformOriginWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let side = match self.1 { + Side::Start => "top", + Side::End => "bottom", + Side::Top => "top", + Side::Right => "right", + Side::Bottom => "bottom", + Side::Left => "left", + Side::StartStart => "top-left", + Side::StartEnd => "top-right", + Side::EndEnd => "bottom-right", + Side::EndStart => "bottom-left", + Side::TopLeft => "top-left", + Side::TopRight => "top-right", + Side::BottomRight => "bottom-right", + Side::BottomLeft => "bottom-left", + Side::Center => "center", + }; + + vec![format!("origin-{side}")] + } + + fn extended_class(&self) -> Vec { + let mut c = self.base_class(); + c.extend_from_slice(&self.0.extended_class()); + c + } + + fn render_with_class(&self, class: &str) -> Markup { + if self.0.as_ref().can_inherit() { + self.0 + .as_ref() + .render_with_class(&format!("{} {class}", self.base_class().join(" "))) + } else { + html! { + div class=(format!("{} {class}", self.base_class().join(" "))) { + (self.0.as_ref()) + } + } + } + } +}