1use super::observable_property_factory::{when_changed_of, when_property_changed_of};
5use super::{IntoPropertyChain, PropertyValue};
6use ferroui_reactive::{Observable, ObservableExt};
7use ferroui_system::component_model::INotifyPropertyChanged;
8use ferroui_system::{IObservable, IObserver};
9use std::rc::Rc;
10
11pub type Fallback<V> = Option<Rc<dyn Fn() -> V>>;
13
14pub trait NotifyPropertyChangedEx: Clone + 'static {
22 fn when_any_property_changed(&self, properties_to_monitor: &[&str]) -> Rc<dyn IObservable<Self>>;
25
26 fn when_property_changed<P: IntoPropertyChain<Self>>(&self, property_accessor: P) -> Rc<dyn IObservable<PropertyValue<Self, P::Value>>>
30 where
31 P::Value: 'static,
32 {
33 self.when_property_changed_with(property_accessor, true)
34 }
35
36 fn when_property_changed_with<P: IntoPropertyChain<Self>>(
40 &self,
41 property_accessor: P,
42 notify_on_initial_value: bool,
43 ) -> Rc<dyn IObservable<PropertyValue<Self, P::Value>>>
44 where
45 P::Value: 'static,
46 {
47 when_property_changed_of(property_accessor.into_property_chain(), self.clone(), notify_on_initial_value, false)
48 }
49
50 fn when_property_changed_with_fallback<P: IntoPropertyChain<Self>>(
56 &self,
57 property_accessor: P,
58 notify_on_initial_value: bool,
59 _fallback_value: impl Fn() -> P::Value + 'static,
60 ) -> Rc<dyn IObservable<PropertyValue<Self, P::Value>>>
61 where
62 P::Value: 'static,
63 {
64 when_property_changed_of(property_accessor.into_property_chain(), self.clone(), notify_on_initial_value, true)
65 }
66
67 fn when_value_changed<P: IntoPropertyChain<Self>>(&self, property_accessor: P) -> Rc<dyn IObservable<P::Value>>
71 where
72 P::Value: 'static,
73 {
74 self.when_value_changed_with(property_accessor, true)
75 }
76
77 fn when_value_changed_with<P: IntoPropertyChain<Self>>(&self, property_accessor: P, notify_on_initial_value: bool) -> Rc<dyn IObservable<P::Value>>
81 where
82 P::Value: 'static,
83 {
84 when_changed_of(property_accessor.into_property_chain(), self.clone(), notify_on_initial_value, None)
85 }
86
87 fn when_value_changed_with_fallback<P: IntoPropertyChain<Self>>(
91 &self,
92 property_accessor: P,
93 notify_on_initial_value: bool,
94 fallback_value: impl Fn() -> P::Value + 'static,
95 ) -> Rc<dyn IObservable<P::Value>>
96 where
97 P::Value: 'static,
98 {
99 when_changed_of(property_accessor.into_property_chain(), self.clone(), notify_on_initial_value, Some(Rc::new(fallback_value)))
100 }
101
102 fn when_changed<P1: IntoPropertyChain<Self>, R: 'static>(&self, p1: P1, result_selector: impl Fn(&Self, P1::Value) -> R + 'static) -> Rc<dyn IObservable<R>>
105 where
106 P1::Value: Clone + 'static,
107 {
108 self.when_changed_with(p1, result_selector, None)
109 }
110
111 fn when_changed_with<P1: IntoPropertyChain<Self>, R: 'static>(
115 &self,
116 p1: P1,
117 result_selector: impl Fn(&Self, P1::Value) -> R + 'static,
118 p1_fallback: Fallback<P1::Value>,
119 ) -> Rc<dyn IObservable<R>>
120 where
121 P1::Value: Clone + 'static,
122 {
123 let source = self.clone();
124 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback).select(move |v1| result_selector(&source, v1))
125 }
126
127 fn when_changed2<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, R: 'static>(
130 &self,
131 p1: P1,
132 p2: P2,
133 result_selector: impl Fn(&Self, P1::Value, P2::Value) -> R + 'static,
134 ) -> Rc<dyn IObservable<R>>
135 where
136 P1::Value: Clone + 'static,
137 P2::Value: Clone + 'static,
138 {
139 self.when_changed2_with(p1, p2, result_selector, None, None)
140 }
141
142 fn when_changed2_with<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, R: 'static>(
145 &self,
146 p1: P1,
147 p2: P2,
148 result_selector: impl Fn(&Self, P1::Value, P2::Value) -> R + 'static,
149 p1_fallback: Fallback<P1::Value>,
150 p2_fallback: Fallback<P2::Value>,
151 ) -> Rc<dyn IObservable<R>>
152 where
153 P1::Value: Clone + 'static,
154 P2::Value: Clone + 'static,
155 {
156 let source = self.clone();
157 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback)
158 .combine_latest(when_changed_of(p2.into_property_chain(), self.clone(), true, p2_fallback), move |v1, v2| result_selector(&source, v1, v2))
159 }
160
161 fn when_changed3<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, R: 'static>(
164 &self,
165 p1: P1,
166 p2: P2,
167 p3: P3,
168 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value) -> R + 'static,
169 ) -> Rc<dyn IObservable<R>>
170 where
171 P1::Value: Clone + 'static,
172 P2::Value: Clone + 'static,
173 P3::Value: Clone + 'static,
174 {
175 self.when_changed3_with(p1, p2, p3, result_selector, None, None, None)
176 }
177
178 #[allow(clippy::too_many_arguments)]
180 fn when_changed3_with<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, R: 'static>(
181 &self,
182 p1: P1,
183 p2: P2,
184 p3: P3,
185 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value) -> R + 'static,
186 p1_fallback: Fallback<P1::Value>,
187 p2_fallback: Fallback<P2::Value>,
188 p3_fallback: Fallback<P3::Value>,
189 ) -> Rc<dyn IObservable<R>>
190 where
191 P1::Value: Clone + 'static,
192 P2::Value: Clone + 'static,
193 P3::Value: Clone + 'static,
194 {
195 let source = self.clone();
196 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback).combine_latest3(
197 when_changed_of(p2.into_property_chain(), self.clone(), true, p2_fallback),
198 when_changed_of(p3.into_property_chain(), self.clone(), true, p3_fallback),
199 move |v1, v2, v3| result_selector(&source, v1, v2, v3),
200 )
201 }
202
203 fn when_changed4<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, R: 'static>(
206 &self,
207 p1: P1,
208 p2: P2,
209 p3: P3,
210 p4: P4,
211 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value) -> R + 'static,
212 ) -> Rc<dyn IObservable<R>>
213 where
214 P1::Value: Clone + 'static,
215 P2::Value: Clone + 'static,
216 P3::Value: Clone + 'static,
217 P4::Value: Clone + 'static,
218 {
219 self.when_changed4_with(p1, p2, p3, p4, result_selector, None, None, None, None)
220 }
221
222 #[allow(clippy::too_many_arguments)]
224 fn when_changed4_with<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, R: 'static>(
225 &self,
226 p1: P1,
227 p2: P2,
228 p3: P3,
229 p4: P4,
230 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value) -> R + 'static,
231 p1_fallback: Fallback<P1::Value>,
232 p2_fallback: Fallback<P2::Value>,
233 p3_fallback: Fallback<P3::Value>,
234 p4_fallback: Fallback<P4::Value>,
235 ) -> Rc<dyn IObservable<R>>
236 where
237 P1::Value: Clone + 'static,
238 P2::Value: Clone + 'static,
239 P3::Value: Clone + 'static,
240 P4::Value: Clone + 'static,
241 {
242 let source = self.clone();
243 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback).combine_latest4(
244 when_changed_of(p2.into_property_chain(), self.clone(), true, p2_fallback),
245 when_changed_of(p3.into_property_chain(), self.clone(), true, p3_fallback),
246 when_changed_of(p4.into_property_chain(), self.clone(), true, p4_fallback),
247 move |v1, v2, v3, v4| result_selector(&source, v1, v2, v3, v4),
248 )
249 }
250
251 #[allow(clippy::too_many_arguments)]
254 fn when_changed5<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, P5: IntoPropertyChain<Self>, R: 'static>(
255 &self,
256 p1: P1,
257 p2: P2,
258 p3: P3,
259 p4: P4,
260 p5: P5,
261 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value, P5::Value) -> R + 'static,
262 ) -> Rc<dyn IObservable<R>>
263 where
264 P1::Value: Clone + 'static,
265 P2::Value: Clone + 'static,
266 P3::Value: Clone + 'static,
267 P4::Value: Clone + 'static,
268 P5::Value: Clone + 'static,
269 {
270 self.when_changed5_with(p1, p2, p3, p4, p5, result_selector, None, None, None, None, None)
271 }
272
273 #[allow(clippy::too_many_arguments)]
275 fn when_changed5_with<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, P5: IntoPropertyChain<Self>, R: 'static>(
276 &self,
277 p1: P1,
278 p2: P2,
279 p3: P3,
280 p4: P4,
281 p5: P5,
282 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value, P5::Value) -> R + 'static,
283 p1_fallback: Fallback<P1::Value>,
284 p2_fallback: Fallback<P2::Value>,
285 p3_fallback: Fallback<P3::Value>,
286 p4_fallback: Fallback<P4::Value>,
287 p5_fallback: Fallback<P5::Value>,
288 ) -> Rc<dyn IObservable<R>>
289 where
290 P1::Value: Clone + 'static,
291 P2::Value: Clone + 'static,
292 P3::Value: Clone + 'static,
293 P4::Value: Clone + 'static,
294 P5::Value: Clone + 'static,
295 {
296 let source = self.clone();
297 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback).combine_latest5(
298 when_changed_of(p2.into_property_chain(), self.clone(), true, p2_fallback),
299 when_changed_of(p3.into_property_chain(), self.clone(), true, p3_fallback),
300 when_changed_of(p4.into_property_chain(), self.clone(), true, p4_fallback),
301 when_changed_of(p5.into_property_chain(), self.clone(), true, p5_fallback),
302 move |v1, v2, v3, v4, v5| result_selector(&source, v1, v2, v3, v4, v5),
303 )
304 }
305
306 #[allow(clippy::too_many_arguments)]
309 fn when_changed6<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, P5: IntoPropertyChain<Self>, P6: IntoPropertyChain<Self>, R: 'static>(
310 &self,
311 p1: P1,
312 p2: P2,
313 p3: P3,
314 p4: P4,
315 p5: P5,
316 p6: P6,
317 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value, P5::Value, P6::Value) -> R + 'static,
318 ) -> Rc<dyn IObservable<R>>
319 where
320 P1::Value: Clone + 'static,
321 P2::Value: Clone + 'static,
322 P3::Value: Clone + 'static,
323 P4::Value: Clone + 'static,
324 P5::Value: Clone + 'static,
325 P6::Value: Clone + 'static,
326 {
327 self.when_changed6_with(p1, p2, p3, p4, p5, p6, result_selector, None, None, None, None, None, None)
328 }
329
330 #[allow(clippy::too_many_arguments)]
332 fn when_changed6_with<P1: IntoPropertyChain<Self>, P2: IntoPropertyChain<Self>, P3: IntoPropertyChain<Self>, P4: IntoPropertyChain<Self>, P5: IntoPropertyChain<Self>, P6: IntoPropertyChain<Self>, R: 'static>(
333 &self,
334 p1: P1,
335 p2: P2,
336 p3: P3,
337 p4: P4,
338 p5: P5,
339 p6: P6,
340 result_selector: impl Fn(&Self, P1::Value, P2::Value, P3::Value, P4::Value, P5::Value, P6::Value) -> R + 'static,
341 p1_fallback: Fallback<P1::Value>,
342 p2_fallback: Fallback<P2::Value>,
343 p3_fallback: Fallback<P3::Value>,
344 p4_fallback: Fallback<P4::Value>,
345 p5_fallback: Fallback<P5::Value>,
346 p6_fallback: Fallback<P6::Value>,
347 ) -> Rc<dyn IObservable<R>>
348 where
349 P1::Value: Clone + 'static,
350 P2::Value: Clone + 'static,
351 P3::Value: Clone + 'static,
352 P4::Value: Clone + 'static,
353 P5::Value: Clone + 'static,
354 P6::Value: Clone + 'static,
355 {
356 let source = self.clone();
357 when_changed_of(p1.into_property_chain(), self.clone(), true, p1_fallback).combine_latest6(
358 when_changed_of(p2.into_property_chain(), self.clone(), true, p2_fallback),
359 when_changed_of(p3.into_property_chain(), self.clone(), true, p3_fallback),
360 when_changed_of(p4.into_property_chain(), self.clone(), true, p4_fallback),
361 when_changed_of(p5.into_property_chain(), self.clone(), true, p5_fallback),
362 when_changed_of(p6.into_property_chain(), self.clone(), true, p6_fallback),
363 move |v1, v2, v3, v4, v5, v6| result_selector(&source, v1, v2, v3, v4, v5, v6),
364 )
365 }
366}
367
368impl<M: INotifyPropertyChanged + 'static> NotifyPropertyChangedEx for Rc<M> {
369 fn when_any_property_changed(&self, properties_to_monitor: &[&str]) -> Rc<dyn IObservable<Self>> {
370 let source = self.clone();
371 let properties_to_monitor: Rc<[String]> = properties_to_monitor.iter().map(|name| name.to_string()).collect();
372 Observable::create(move |observer: Rc<dyn IObserver<Rc<M>>>| {
374 let (sender, properties_to_monitor) = (source.clone(), properties_to_monitor.clone());
375 let token = source.property_changed().add(Rc::new(move |property_name: &str| {
376 if properties_to_monitor.is_empty() || properties_to_monitor.iter().any(|name| name == property_name) {
377 observer.on_next(sender.clone());
378 }
379 }));
380 let target = source.clone();
381 ferroui_reactive::disposables::Disposable::create(move || {
382 target.property_changed().remove(token);
383 })
384 })
385 }
386}