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    /// Horizontal distance, positive to the right.
9    pub x: Dp,
10    /// Vertical distance, positive downward.
11    pub y: Dp,
12}
13
14impl DpOffset {
15    /// Creates an offset from a horizontal and a vertical distance.
16    pub const fn new(x: Dp, y: Dp) -> Self {
17        Self { x, y }
18    }
19
20    /// An offset of zero in both directions.
21    pub const ZERO: DpOffset = DpOffset {
22        x: Dp(0.0),
23        y: Dp(0.0),
24    };
25
26    /// Converts both components to pixels at `density`.
27    pub fn to_px(self, density: Density) -> Point {
28        Point::new(self.x.to_px(density).0, self.y.to_px(density).0)
29    }
30}
31
32impl Default for DpOffset {
33    fn default() -> Self {
34        Self::ZERO
35    }
36}
37
38/// Static shadow configuration.
39///
40/// This mirrors Compose's static `Shadow` object where radius/spread/offset
41/// are provided in density-independent units and converted to pixels at draw time.
42#[derive(Clone, Debug, PartialEq)]
43pub struct Shadow {
44    /// Blur radius. Negative values count as 0.
45    pub radius: Dp,
46    /// Growth of the shadow shape before blurring. Negative values shrink it.
47    pub spread: Dp,
48    /// Shift of the shadow relative to the element.
49    pub offset: DpOffset,
50    /// Shadow color, used when `brush` is `None`.
51    pub color: Color,
52    /// Paint for the shadow, used instead of `color` when set.
53    pub brush: Option<Brush>,
54    /// Opacity from 0.0 to 1.0. A value of 0.0 or less draws no shadow.
55    pub alpha: f32,
56    /// How the shadow combines with what is already drawn.
57    pub blend_mode: BlendMode,
58    /// Knocks the unoffset, unspread shape out of the shadow silhouette
59    /// before blurring, leaving shadow only OUTSIDE the element. Required
60    /// for translucent surfaces (glass): a backdrop-sampling material must
61    /// not refract its own shadow.
62    pub cutout: bool,
63}
64
65impl Default for Shadow {
66    fn default() -> Self {
67        Self {
68            radius: Dp(0.0),
69            spread: Dp(0.0),
70            offset: DpOffset::ZERO,
71            color: Color::BLACK,
72            brush: None,
73            alpha: 1.0,
74            blend_mode: BlendMode::SrcOver,
75            cutout: false,
76        }
77    }
78}
79
80impl Shadow {
81    /// Converts the lengths to pixels at `density`.
82    pub fn to_scope(&self, density: Density) -> ShadowScope {
83        ShadowScope {
84            radius: self.radius.to_px(density).0,
85            spread: self.spread.to_px(density).0,
86            offset: self.offset.to_px(density),
87            color: self.color,
88            brush: self.brush.clone(),
89            alpha: self.alpha,
90            blend_mode: self.blend_mode,
91            cutout: self.cutout,
92        }
93    }
94}
95
96/// Pixel-space shadow scope used by block-based APIs.
97#[derive(Clone, Debug, PartialEq)]
98pub struct ShadowScope {
99    /// Blur radius, in pixels. Negative values count as 0.0.
100    pub radius: f32,
101    /// Growth of the shadow shape before blurring, in pixels. Negative values shrink it.
102    pub spread: f32,
103    /// Shift of the shadow relative to the element, in pixels.
104    pub offset: Point,
105    /// Shadow color, used when `brush` is `None`.
106    pub color: Color,
107    /// Paint for the shadow, used instead of `color` when set.
108    pub brush: Option<Brush>,
109    /// Opacity from 0.0 to 1.0. A value of 0.0 or less draws no shadow.
110    pub alpha: f32,
111    /// How the shadow combines with what is already drawn.
112    pub blend_mode: BlendMode,
113    /// See [`Shadow::cutout`]: knock the element's own shape out of the
114    /// silhouette so the shadow exists only outside it.
115    pub cutout: bool,
116}
117
118impl Default for ShadowScope {
119    fn default() -> Self {
120        Self {
121            radius: 0.0,
122            spread: 0.0,
123            offset: Point::ZERO,
124            color: Color::BLACK,
125            brush: None,
126            alpha: 1.0,
127            blend_mode: BlendMode::SrcOver,
128            cutout: false,
129        }
130    }
131}
132
133#[cfg(test)]
134#[path = "tests/shadow_tests.rs"]
135mod tests;