ferroui_base/media/effects/
drop_shadow_effect.rs1use crate::media::effects::{
2 Effect, IImmutableEffect, ImmutableDropShadowDirectionEffect, ImmutableDropShadowEffect,
3};
4use crate::media::{Color, Colors};
5use crate::{
6 ferro_class, ferro_property, instantiate, FerroObjectImpl, FerroProperty, Ref, StyledProperty,
7};
8use std::f64::consts::PI;
9
10#[repr(C)]
12pub struct DropShadowEffectBase {
13 base: Effect,
14}
15
16ferro_class!(DropShadowEffectBase: Effect);
17
18crate::ferro_impl_classes!(DropShadowEffectBase: FerroObjectImpl);
19
20crate::ferro_properties! { impl DropShadowEffectBase {
21 ferro_property!(pub fn blur_radius_property() -> StyledProperty<f64> {
22 FerroProperty::register::<DropShadowEffectBase, _>("BlurRadius", 5.0)
23 });
24
25 ferro_property!(pub fn color_property() -> StyledProperty<Color> {
26 FerroProperty::register::<DropShadowEffectBase, _>("Color", Colors::BLACK)
27 });
28
29 ferro_property!(pub fn opacity_property() -> StyledProperty<f64> {
30 FerroProperty::register::<DropShadowEffectBase, _>("Opacity", 1.0)
31 });
32} }
33
34impl DropShadowEffectBase {
35 fn static_constructor() {
36 Effect::affects_render::<DropShadowEffectBase>(&[
37 Self::blur_radius_property().as_property(),
38 Self::color_property().as_property(),
39 Self::opacity_property().as_property(),
40 ]);
41 }
42
43 pub fn construct() -> Self {
45 Self { base: Effect::construct() }
46 }
47
48 pub fn blur_radius(&self) -> f64 {
50 self.get_value(Self::blur_radius_property())
51 }
52
53 pub fn set_blur_radius(&self, value: f64) {
54 self.set_value(Self::blur_radius_property(), value)
55 }
56
57 pub fn color(&self) -> Color {
59 self.get_value(Self::color_property())
60 }
61
62 pub fn set_color(&self, value: Color) {
63 self.set_value(Self::color_property(), value)
64 }
65
66 pub fn opacity(&self) -> f64 {
68 self.get_value(Self::opacity_property())
69 }
70
71 pub fn set_opacity(&self, value: f64) {
72 self.set_value(Self::opacity_property(), value)
73 }
74}
75
76#[repr(C)]
79pub struct DropShadowEffect {
80 base: DropShadowEffectBase,
81}
82
83ferro_class!(DropShadowEffect: DropShadowEffectBase);
84crate::ferro_class_info!(DropShadowEffect { new: DropShadowEffect::new });
85
86crate::ferro_impl_classes!(DropShadowEffect: FerroObjectImpl);
87
88crate::ferro_properties! { impl DropShadowEffect {
89 ferro_property!(pub fn offset_x_property() -> StyledProperty<f64> {
90 FerroProperty::register::<DropShadowEffect, _>("OffsetX", 3.5355)
91 });
92
93 ferro_property!(pub fn offset_y_property() -> StyledProperty<f64> {
94 FerroProperty::register::<DropShadowEffect, _>("OffsetY", 3.5355)
95 });
96} }
97
98impl DropShadowEffect {
99 fn static_constructor() {
100 Effect::affects_render::<DropShadowEffect>(&[
101 Self::offset_x_property().as_property(),
102 Self::offset_y_property().as_property(),
103 ]);
104 }
105
106 pub fn construct() -> Self {
108 Self { base: DropShadowEffectBase::construct() }
109 }
110
111 pub fn new() -> Ref<Self> {
112 instantiate(Self::construct())
113 }
114
115 pub fn offset_x(&self) -> f64 {
117 self.get_value(Self::offset_x_property())
118 }
119
120 pub fn set_offset_x(&self, value: f64) {
121 self.set_value(Self::offset_x_property(), value)
122 }
123
124 pub fn offset_y(&self) -> f64 {
126 self.get_value(Self::offset_y_property())
127 }
128
129 pub fn set_offset_y(&self, value: f64) {
130 self.set_value(Self::offset_y_property(), value)
131 }
132
133 pub fn to_immutable(&self) -> std::sync::Arc<dyn IImmutableEffect> {
135 std::sync::Arc::new(ImmutableDropShadowEffect::new(
136 self.offset_x(),
137 self.offset_y(),
138 self.blur_radius(),
139 self.color(),
140 self.opacity(),
141 ))
142 }
143}
144
145#[repr(C)]
148pub struct DropShadowDirectionEffect {
149 base: DropShadowEffectBase,
150}
151
152ferro_class!(DropShadowDirectionEffect: DropShadowEffectBase);
153crate::ferro_class_info!(DropShadowDirectionEffect { new: DropShadowDirectionEffect::new });
154
155crate::ferro_impl_classes!(DropShadowDirectionEffect: FerroObjectImpl);
156
157crate::ferro_properties! { impl DropShadowDirectionEffect {
158 ferro_property!(pub fn shadow_depth_property() -> StyledProperty<f64> {
159 FerroProperty::register::<DropShadowDirectionEffect, _>("ShadowDepth", 5.0)
160 });
161
162 ferro_property!(pub fn direction_property() -> StyledProperty<f64> {
163 FerroProperty::register::<DropShadowDirectionEffect, _>("Direction", 315.0)
164 });
165} }
166
167impl DropShadowDirectionEffect {
168 fn static_constructor() {
169 Effect::affects_render::<DropShadowDirectionEffect>(&[
170 Self::shadow_depth_property().as_property(),
171 Self::direction_property().as_property(),
172 ]);
173 }
174
175 pub fn construct() -> Self {
177 Self { base: DropShadowEffectBase::construct() }
178 }
179
180 pub fn new() -> Ref<Self> {
181 instantiate(Self::construct())
182 }
183
184 pub fn shadow_depth(&self) -> f64 {
186 self.get_value(Self::shadow_depth_property())
187 }
188
189 pub fn set_shadow_depth(&self, value: f64) {
190 self.set_value(Self::shadow_depth_property(), value)
191 }
192
193 pub fn direction(&self) -> f64 {
195 self.get_value(Self::direction_property())
196 }
197
198 pub fn set_direction(&self, value: f64) {
199 self.set_value(Self::direction_property(), value)
200 }
201
202 pub fn offset_x(&self) -> f64 {
204 (self.direction() * PI / 180.0).cos() * self.shadow_depth()
205 }
206
207 pub fn offset_y(&self) -> f64 {
209 (self.direction() * PI / 180.0).sin() * self.shadow_depth()
210 }
211
212 pub fn to_immutable(&self) -> std::sync::Arc<dyn IImmutableEffect> {
217 std::sync::Arc::new(ImmutableDropShadowDirectionEffect::new(
218 self.offset_x(),
219 self.offset_y(),
220 self.blur_radius(),
221 self.color(),
222 self.opacity(),
223 ))
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use std::rc::Rc;
230 use super::*;
232 use std::cell::Cell;
233 use crate::media::effects::{BlurEffect, EffectExtensions, IEffect};
234
235 #[test]
236 fn changing_a_property_raises_invalidated() {
237 let count = Rc::new(Cell::new(0));
238
239 let blur = BlurEffect::new();
240 let c = count.clone();
241 let subscription = blur.invalidated(move || c.set(c.get() + 1));
242 blur.set_radius(10.0);
243 assert_eq!(1, count.get());
244 subscription.dispose();
245 blur.set_radius(11.0);
246 assert_eq!(1, count.get());
247
248 let shadow = DropShadowEffect::new();
249 let c = count.clone();
250 shadow.invalidated(move || c.set(c.get() + 1));
251 shadow.set_offset_x(1.0);
252 shadow.set_blur_radius(2.0);
253 shadow.set_color(Colors::RED);
254 assert_eq!(4, count.get());
255
256 let direction = DropShadowDirectionEffect::new();
257 let c = count.clone();
258 direction.invalidated(move || c.set(c.get() + 1));
259 direction.set_direction(0.0);
260 direction.set_opacity(0.5);
261 assert_eq!(6, count.get());
262 }
263
264 #[test]
265 fn defaults_interfaces_and_immutable_copies() {
266 let shadow = DropShadowEffect::new();
267 assert_eq!(3.5355, shadow.offset_x());
268 assert_eq!(5.0, shadow.blur_radius());
269 assert_eq!(Colors::BLACK, shadow.color());
270
271 let effect: Rc<dyn IEffect> = shadow.clone().into();
272 assert!(effect.as_blur_effect().is_none());
273 assert!(effect.as_direction_drop_shadow_effect().is_none());
274 assert_eq!(3.5355, effect.as_drop_shadow_effect().unwrap().offset_y());
275 assert!(effect.as_object().unwrap().is::<DropShadowEffect>());
276
277 let immutable = EffectExtensions::to_immutable(&effect);
278 assert!(immutable.equals(Some(&*effect)));
279 shadow.set_opacity(0.5);
280 assert!(!immutable.equals(Some(&*effect)));
281 assert!(!immutable.equals(None));
282 let as_effect: Rc<dyn IEffect> = Rc::new(*immutable.as_any().downcast_ref::<ImmutableDropShadowEffect>().unwrap());
285 let same = EffectExtensions::to_immutable(&as_effect);
286 assert!(same.equals(Some(&*as_effect)));
287
288 let direction = DropShadowDirectionEffect::new();
289 direction.set_direction(0.0);
290 direction.set_shadow_depth(4.0);
291 assert_eq!(4.0, direction.offset_x());
292 assert_eq!(0.0, direction.offset_y());
293 let effect: Rc<dyn IEffect> = direction.into();
294 assert_eq!(0.0, effect.as_direction_drop_shadow_effect().unwrap().direction());
295
296 let blur: Rc<dyn IEffect> = BlurEffect::new().into();
297 assert_eq!(5.0, blur.as_blur_effect().unwrap().radius());
298 let other: Rc<dyn IEffect> = BlurEffect::new().into();
299 assert!(*blur != *other);
300 assert!(*blur == *blur.clone());
301 }
302}