use crate::view::theme::ColorSpec;
use crate::core::{Point, px};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BoxShadow {
pub offset: Point,
pub blur: f32,
pub spread: f32,
pub color: Option<ColorSpec>,
pub strength: f32,
}
impl BoxShadow {
pub const fn new(x: f32, y: f32, blur: f32) -> Self {
Self { offset: Point { x: px(x), y: px(y) }, blur, spread: 0.0, color: None, strength: 1.0 }
}
pub const fn spread(mut self, spread: f32) -> Self {
self.spread = spread;
self
}
pub const fn strength(mut self, strength: f32) -> Self {
self.strength = strength;
self
}
pub fn color(mut self, color: impl Into<ColorSpec>) -> Self {
self.color = Some(color.into());
self
}
}
pub const MAX_SHADOWS: usize = 3;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Shadows {
layers: [BoxShadow; MAX_SHADOWS],
len: u8,
}
impl Shadows {
pub const NONE: Self = Self { layers: [BoxShadow::new(0.0, 0.0, 0.0); MAX_SHADOWS], len: 0 };
pub const fn one(shadow: BoxShadow) -> Self {
let mut shadows = Self::NONE;
shadows.layers[0] = shadow;
shadows.len = 1;
shadows
}
pub const fn with(mut self, shadow: BoxShadow) -> Self {
if (self.len as usize) < MAX_SHADOWS {
self.layers[self.len as usize] = shadow;
self.len += 1;
}
self
}
pub fn layers(&self) -> &[BoxShadow] {
&self.layers[..self.len as usize]
}
pub fn tinted(mut self, color: ColorSpec) -> Self {
for layer in &mut self.layers[..self.len as usize] {
layer.color = Some(color);
}
self
}
}
impl From<BoxShadow> for Shadows {
fn from(shadow: BoxShadow) -> Self {
Self::one(shadow)
}
}
impl From<ShadowPreset> for Shadows {
fn from(preset: ShadowPreset) -> Self {
preset.shadows()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ShadowPreset {
Xs,
Sm,
Md,
Lg,
Xl,
Xxl,
}
impl ShadowPreset {
pub const fn parameters(self) -> (f32, f32, f32) {
match self {
ShadowPreset::Xs => (1.0, 2.0, 0.5),
ShadowPreset::Sm => (1.0, 3.0, 0.6),
ShadowPreset::Md => (4.0, 8.0, 0.7),
ShadowPreset::Lg => (10.0, 20.0, 0.8),
ShadowPreset::Xl => (20.0, 32.0, 0.9),
ShadowPreset::Xxl => (25.0, 50.0, 1.0),
}
}
pub const fn shadows(self) -> Shadows {
let (y, blur, strength) = self.parameters();
let main = BoxShadow::new(0.0, y, blur).strength(strength);
match self {
ShadowPreset::Xxl => Shadows::one(main.spread(-12.0)),
_ => Shadows::one(main),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layers_stop_at_the_maximum() {
let layer = BoxShadow::new(0.0, 1.0, 2.0);
let shadows = Shadows::one(layer).with(layer).with(layer).with(layer);
assert_eq!(shadows.layers().len(), MAX_SHADOWS);
assert!(Shadows::NONE.layers().is_empty());
}
#[test]
fn presets_keep_their_previous_look() {
let md = Shadows::from(ShadowPreset::Md);
assert_eq!(md.layers(), &[BoxShadow::new(0.0, 4.0, 8.0).strength(0.7)]);
}
}