ferroui_base/media/
i_affects_render.rs1use crate::media::effects::{Effect, IEffect};
2use crate::media::imaging::CroppedBitmap;
3use crate::media::{Brush, DrawingImage, Geometry, IBrush, IImage, IPen, ITransform, Pen, Transform};
4use crate::reactive::IDisposable;
5use crate::Ref;
6use std::any::{Any, TypeId};
7use std::rc::Rc;
8
9pub trait IAffectsRender {
15 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable>;
18}
19
20impl IAffectsRender for Brush {
21 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
22 Brush::invalidated(self, move || handler())
23 }
24}
25
26impl IAffectsRender for Pen {
27 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
28 Pen::invalidated(self, move || handler())
29 }
30}
31
32impl IAffectsRender for Geometry {
33 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
34 self.changed(move || handler())
35 }
36}
37
38impl IAffectsRender for Transform {
39 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
40 self.changed(move || handler())
41 }
42}
43
44impl IAffectsRender for Effect {
45 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
46 Effect::invalidated(self, move || handler())
47 }
48}
49
50impl IAffectsRender for CroppedBitmap {
51 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
52 CroppedBitmap::invalidated(self, move || handler())
53 }
54}
55
56impl IAffectsRender for DrawingImage {
57 fn invalidated(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
58 DrawingImage::invalidated(self, move || handler())
59 }
60}
61
62pub fn subscribe_invalidated(value: &dyn Any, handler: Rc<dyn Fn()>) -> Option<Rc<dyn IDisposable>> {
76 if let Some(value) = value.downcast_ref::<Option<Rc<dyn IBrush>>>() {
77 let brush = value.as_ref()?.as_object()?.downcast_ref::<Brush>()?;
78 return Some(IAffectsRender::invalidated(brush, handler));
79 }
80 if let Some(value) = value.downcast_ref::<Option<Rc<dyn IPen>>>() {
81 let pen = value.as_ref()?.as_object()?.downcast_ref::<Pen>()?;
82 return Some(IAffectsRender::invalidated(pen, handler));
83 }
84 if let Some(value) = value.downcast_ref::<Option<Rc<dyn ITransform>>>() {
85 let transform = value.as_ref()?.as_object()?.downcast_ref::<Transform>()?;
86 return Some(IAffectsRender::invalidated(transform, handler));
87 }
88 if let Some(value) = value.downcast_ref::<Option<Ref<Transform>>>() {
89 let transform: &Transform = value.as_ref()?;
90 return Some(IAffectsRender::invalidated(transform, handler));
91 }
92 if let Some(value) = value.downcast_ref::<Option<Ref<Geometry>>>() {
93 let geometry: &Geometry = value.as_ref()?;
94 return Some(IAffectsRender::invalidated(geometry, handler));
95 }
96 if let Some(value) = value.downcast_ref::<Option<Rc<dyn IEffect>>>() {
97 let effect = value.as_ref()?.as_object()?.downcast_ref::<Effect>()?;
98 return Some(IAffectsRender::invalidated(effect, handler));
99 }
100 if let Some(value) = value.downcast_ref::<Option<Rc<dyn IImage>>>() {
101 return Some(value.as_ref()?.as_affects_render()?.invalidated(handler));
102 }
103 None
104}
105
106pub fn can_value_affect_render(property_type: TypeId) -> bool {
110 property_type == TypeId::of::<Option<Rc<dyn IBrush>>>()
111 || property_type == TypeId::of::<Option<Rc<dyn IPen>>>()
112 || property_type == TypeId::of::<Option<Rc<dyn ITransform>>>()
113 || property_type == TypeId::of::<Option<Ref<Transform>>>()
114 || property_type == TypeId::of::<Option<Ref<Geometry>>>()
115 || property_type == TypeId::of::<Option<Rc<dyn IEffect>>>()
116 || property_type == TypeId::of::<Option<Rc<dyn IImage>>>()
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
123 use crate::media::effects::{BlurEffect, ImmutableBlurEffect};
124 use crate::media::immutable::ImmutableSolidColorBrush;
125 use crate::media::{Colors, DrawingImage, RectangleGeometry, SolidColorBrush, TranslateTransform};
126 use crate::Rect;
127 use std::cell::Cell;
128
129 fn counting() -> (Rc<Cell<i32>>, Rc<dyn Fn()>) {
130 let count = Rc::new(Cell::new(0));
131 let c = count.clone();
132 (count, Rc::new(move || c.set(c.get() + 1)))
133 }
134
135 #[test]
136 fn subscribes_to_mutable_media_values() {
137 let (count, handler) = counting();
138
139 let brush = SolidColorBrush::new();
140 let value: Option<Rc<dyn IBrush>> = Some(brush.clone().into());
141 let subscription = subscribe_invalidated(&value, handler.clone()).unwrap();
142 brush.set_color(Colors::RED);
143 assert_eq!(1, count.get());
144 subscription.dispose();
145 brush.set_color(Colors::BLUE);
146 assert_eq!(1, count.get());
147
148 let pen = Pen::new();
149 let value: Option<Rc<dyn IPen>> = Some(pen.clone().into());
150 subscribe_invalidated(&value, handler.clone()).unwrap();
151 pen.set_thickness(3.0);
152 assert_eq!(2, count.get());
153
154 let transform = TranslateTransform::new();
155 let value: Option<Rc<dyn ITransform>> = Some(transform.clone().into());
156 subscribe_invalidated(&value, handler.clone()).unwrap();
157 let value: Option<Ref<Transform>> = Some(transform.clone().upcast());
158 subscribe_invalidated(&value, handler.clone()).unwrap();
159 transform.set_x(1.0);
160 assert_eq!(4, count.get());
161
162 let geometry = RectangleGeometry::new();
163 let value: Option<Ref<Geometry>> = Some(geometry.clone().upcast());
164 subscribe_invalidated(&value, handler.clone()).unwrap();
165 geometry.set_rect(Rect::new(0.0, 0.0, 1.0, 1.0));
166 assert_eq!(5, count.get());
167
168 let effect = BlurEffect::new();
169 let value: Option<Rc<dyn IEffect>> = Some(effect.clone().into());
170 subscribe_invalidated(&value, handler.clone()).unwrap();
171 effect.set_radius(1.0);
172 assert_eq!(6, count.get());
173
174 let image = DrawingImage::new();
175 let value: Option<Rc<dyn IImage>> = Some(image.clone().into());
176 subscribe_invalidated(&value, handler).unwrap();
177 image.set_viewbox(Some(Rect::new(0.0, 0.0, 1.0, 1.0)));
178 assert_eq!(7, count.get());
179 }
180
181 #[test]
182 fn ignores_immutable_absent_and_unrelated_values() {
183 let (_, handler) = counting();
184
185 let value: Option<Rc<dyn IBrush>> = Some(Rc::new(ImmutableSolidColorBrush::new(Colors::RED)));
186 assert!(subscribe_invalidated(&value, handler.clone()).is_none());
187 let value: Option<Rc<dyn IBrush>> = None;
188 assert!(subscribe_invalidated(&value, handler.clone()).is_none());
189 let value: Option<Rc<dyn IEffect>> = Some(Rc::new(ImmutableBlurEffect::new(1.0)));
190 assert!(subscribe_invalidated(&value, handler.clone()).is_none());
191 assert!(subscribe_invalidated(&1.0f64, handler.clone()).is_none());
192 assert!(subscribe_invalidated(&Rect::default(), handler).is_none());
193
194 assert!(can_value_affect_render(TypeId::of::<Option<Rc<dyn IBrush>>>()));
195 assert!(can_value_affect_render(TypeId::of::<Option<Ref<Geometry>>>()));
196 assert!(!can_value_affect_render(TypeId::of::<f64>()));
197 assert!(!can_value_affect_render(TypeId::of::<Rect>()));
198 }
199}