diff --git a/src/ui/mod.rs b/src/ui/mod.rs index acf089f..602b811 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -46,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; 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/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()) + } + } + } + } +}