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;