Skip to main content

cranpose_ui_graphics/
shadow.rs

1//! Shadow configuration models used by drop/inner shadow modifiers.
2
3use crate::{BlendMode, Brush, Color, Density, Dp, Point};
4
5/// Density-independent offset for shadows.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct DpOffset {
8    pub x: Dp,
9    pub y: Dp,
10}
11
12impl DpOffset {
13    pub const fn new(x: Dp, y: Dp) -> Self {
14        Self { x, y }
15    }
16
17    pub const ZERO: DpOffset = DpOffset {
18        x: Dp(0.0),
19        y: Dp(0.0),
20    };
21
22    pub fn to_px(self, density: Density) -> Point {
23        Point::new(self.x.to_px(density).0, self.y.to_px(density).0)
24    }
25}
26
27impl Default for DpOffset {
28    fn default() -> Self {
29        Self::ZERO
30    }
31}
32
33/// Static shadow configuration.
34///
35/// This mirrors Compose's static `Shadow` object where radius/spread/offset
36/// are provided in density-independent units and converted to pixels at draw time.
37#[derive(Clone, Debug, PartialEq)]
38pub struct Shadow {
39    pub radius: Dp,
40    pub spread: Dp,
41    pub offset: DpOffset,
42    pub color: Color,
43    pub brush: Option<Brush>,
44    pub alpha: f32,
45    pub blend_mode: BlendMode,
46    /// Knocks the unoffset, unspread shape out of the shadow silhouette
47    /// before blurring, leaving shadow only OUTSIDE the element. Required
48    /// for translucent surfaces (glass): a backdrop-sampling material must
49    /// not refract its own shadow.
50    pub cutout: bool,
51}
52
53impl Default for Shadow {
54    fn default() -> Self {
55        Self {
56            radius: Dp(0.0),
57            spread: Dp(0.0),
58            offset: DpOffset::ZERO,
59            color: Color::BLACK,
60            brush: None,
61            alpha: 1.0,
62            blend_mode: BlendMode::SrcOver,
63            cutout: false,
64        }
65    }
66}
67
68impl Shadow {
69    pub fn to_scope(&self, density: Density) -> ShadowScope {
70        ShadowScope {
71            radius: self.radius.to_px(density).0,
72            spread: self.spread.to_px(density).0,
73            offset: self.offset.to_px(density),
74            color: self.color,
75            brush: self.brush.clone(),
76            alpha: self.alpha,
77            blend_mode: self.blend_mode,
78            cutout: self.cutout,
79        }
80    }
81}
82
83/// Pixel-space shadow scope used by block-based APIs.
84#[derive(Clone, Debug, PartialEq)]
85pub struct ShadowScope {
86    pub radius: f32,
87    pub spread: f32,
88    pub offset: Point,
89    pub color: Color,
90    pub brush: Option<Brush>,
91    pub alpha: f32,
92    pub blend_mode: BlendMode,
93    /// See [`Shadow::cutout`]: knock the element's own shape out of the
94    /// silhouette so the shadow exists only outside it.
95    pub cutout: bool,
96}
97
98impl Default for ShadowScope {
99    fn default() -> Self {
100        Self {
101            radius: 0.0,
102            spread: 0.0,
103            offset: Point::ZERO,
104            color: Color::BLACK,
105            brush: None,
106            alpha: 1.0,
107            blend_mode: BlendMode::SrcOver,
108            cutout: false,
109        }
110    }
111}
112
113#[cfg(test)]
114#[path = "tests/shadow_tests.rs"]
115mod tests;