From f7db3333c5714d5d3ec3606c682b7c3f4cc3595a Mon Sep 17 00:00:00 2001 From: JMARyA Date: Mon, 20 Jan 2025 05:51:22 +0100 Subject: [PATCH] add filters --- src/lib.rs | 2 + src/ui/mod.rs | 3 + src/ui/primitives/filter.rs | 265 +++++++++++++++++++++++++++++++++++ src/ui/primitives/mod.rs | 3 + src/ui/primitives/rounded.rs | 2 +- 5 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/ui/primitives/filter.rs diff --git a/src/lib.rs b/src/lib.rs index 1985369..809b519 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(const_vec_string_slice)] + use tokio::sync::OnceCell; pub mod auth; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 602b811..90bb54f 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -26,6 +26,9 @@ pub mod prelude { pub use super::primitives::container::Container; pub use super::primitives::cursor::Cursor; pub use super::primitives::div::Div; + pub use super::primitives::filter::{ + Blur, Brightness, Contrast, Grayscale, HueRotate, Invert, Saturate, Sepia, + }; pub use super::primitives::flex::{ Direction, Flex, FlexBasis, FlexGrow, Justify, Order, Strategy, Wrap, }; diff --git a/src/ui/primitives/filter.rs b/src/ui/primitives/filter.rs new file mode 100644 index 0000000..7598bc9 --- /dev/null +++ b/src/ui/primitives/filter.rs @@ -0,0 +1,265 @@ +use crate::ui::UIWidget; +use maud::{Markup, Render, html}; + +use super::Size; + +#[allow(non_snake_case)] +pub fn Blur(amount: Size, inner: T) -> BlurWidget { + BlurWidget(Box::new(inner), amount, false) +} + +pub struct BlurWidget(Box, Size, bool); + +impl BlurWidget { + pub fn backdrop(mut self) -> Self { + self.2 = true; + self + } +} + +impl Render for BlurWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for BlurWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = match &self.1 { + Size::Custom(s) => &format!(" blur-[{s}]"), + Size::None => "blur-none", + Size::Small => "blur-sm", + Size::Regular => "blur", + Size::Medium => "blur-md", + Size::Large => "blur-lg", + Size::XL => "blur-xl", + Size::_2XL => "blur-2xl", + Size::_3XL => "blur-3xl", + Size::Full => "blur-3xl", + }; + + if self.2 { + return vec![format!("backdrop-{class}")]; + } + + vec![class.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()) + } + } + } + } +} + +macro_rules! build_value_widget { + ($constr:ident, $widget:ident, $class:literal) => { + #[allow(non_snake_case)] + pub fn $constr(value: f64, inner: T) -> $widget { + $widget(Box::new(inner), value, false) + } + + pub struct $widget(Box, f64, bool); + + impl $widget { + pub fn backdrop(mut self) -> Self { + self.2 = true; + self + } + } + + impl Render for $widget { + fn render(&self) -> Markup { + self.render_with_class("") + } + } + + impl UIWidget for $widget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let mut ret = $class.to_string(); + ret.push_str(&format!("-[{:.2}]", self.1)); + + if self.2 { + return vec![format!("backdrop-{ret}")]; + } + + vec![ret] + } + + 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()) + } + } + } + } + } + }; +} + +build_value_widget!(Brightness, BrightnessWidget, "brightness"); +build_value_widget!(Contrast, ConstrastWidget, "contrast"); +build_value_widget!(Saturate, SaturationWidget, "saturate"); + +macro_rules! build_on_off_widget { + ($constr:ident, $widget:ident, $class:literal) => { + #[allow(non_snake_case)] + pub fn $constr(inner: T) -> $widget { + $widget(Box::new(inner), true, false) + } + + pub struct $widget(Box, bool, bool); + + impl $widget { + pub fn none(mut self) -> Self { + self.1 = false; + self + } + + pub fn backdrop(mut self) -> Self { + self.2 = true; + self + } + } + + impl Render for $widget { + fn render(&self) -> Markup { + self.render_with_class("") + } + } + + impl UIWidget for $widget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = if self.1 { + $class.to_string() + } else { + concat!($class, "-0").to_string() + }; + + if self.2 { + return vec![format!("backdrop-{class}")]; + } + + vec![class] + } + + 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()) + } + } + } + } + } + }; +} + +build_on_off_widget!(Grayscale, GrayscaleWidget, "grayscale"); +build_on_off_widget!(Invert, InvertWidget, "invert"); +build_on_off_widget!(Sepia, SepiaWidget, "sepia"); + +#[allow(non_snake_case)] +pub fn HueRotate(deg: u32, inner: T) -> HueRotateWidget { + HueRotateWidget(Box::new(inner), deg, false) +} + +pub struct HueRotateWidget(Box, u32, bool); + +impl HueRotateWidget { + pub fn backdrop(mut self) -> Self { + self.2 = true; + self + } +} + +impl Render for HueRotateWidget { + fn render(&self) -> Markup { + self.render_with_class("") + } +} + +impl UIWidget for HueRotateWidget { + fn can_inherit(&self) -> bool { + true + } + + fn base_class(&self) -> Vec { + let class = format!("hue-rotate-[{:.2}deg]", self.1); + + if self.2 { + return vec![format!("backdrop-{class}")]; + } + + vec![class] + } + + 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()) + } + } + } + } +} diff --git a/src/ui/primitives/mod.rs b/src/ui/primitives/mod.rs index 0e1c096..25fa8b7 100644 --- a/src/ui/primitives/mod.rs +++ b/src/ui/primitives/mod.rs @@ -8,6 +8,7 @@ pub mod background; pub mod container; pub mod cursor; pub mod div; +pub mod filter; pub mod flex; pub mod header; pub mod height; @@ -62,6 +63,7 @@ pub fn script(script: &str) -> PreEscaped { } pub enum Size { + Custom(String), None, Small, Regular, @@ -77,6 +79,7 @@ impl Size { #[must_use] pub const fn to_value(&self) -> &str { match self { + Self::Custom(str) => str.as_str(), Self::None => "none", Self::Small => "sm", Self::Regular => "", diff --git a/src/ui/primitives/rounded.rs b/src/ui/primitives/rounded.rs index f716814..d448fe3 100644 --- a/src/ui/primitives/rounded.rs +++ b/src/ui/primitives/rounded.rs @@ -12,7 +12,7 @@ pub struct RoundedWidget(Box, Option, Option); impl RoundedWidget { #[must_use] - pub const fn size(mut self, size: Size) -> Self { + pub fn size(mut self, size: Size) -> Self { self.1 = Some(size); self }