Skip to main content

ferroui_diagnostics/diagnostics/view_models/
reactive_extensions.rs

1//! Port of `Diagnostics/ViewModels/ReactiveExtensions.cs`.
2
3use ferroui_base::data::model::INotifyPropertyChanged;
4use ferroui_base::reactive::{Disposable, IObservable, IObserver, Observable};
5use std::rc::Rc;
6
7/// Observables of the properties of view models.
8pub struct ReactiveExtensions;
9
10impl ReactiveExtensions {
11    /// `vm.GetObservable(x => x.Property, fireImmediately)`: an observable of
12    /// the values of a property of a view model, produced again whenever the
13    /// view model notifies a change of the property.
14    ///
15    /// The original takes the property as an expression and reads its name
16    /// and its value through reflection; here the name of the property and
17    /// the function that reads it are passed. When `fire_immediately` is set,
18    /// each observer receives the current value when it subscribes.
19    pub fn get_observable<TOwner, TValue>(
20        vm: &Rc<TOwner>,
21        property_name: &'static str,
22        property: impl Fn(&TOwner) -> TValue + 'static,
23        fire_immediately: bool,
24    ) -> Rc<dyn IObservable<TValue>>
25    where
26        TOwner: INotifyPropertyChanged + ?Sized + 'static,
27        TValue: 'static,
28    {
29        let vm = vm.clone();
30        let property: Rc<dyn Fn(&TOwner) -> TValue> = Rc::new(property);
31        Observable::create(move |o: Rc<dyn IObserver<TValue>>| {
32            let fire = {
33                let vm = vm.clone();
34                let property = property.clone();
35                Rc::new(move || o.on_next(property(&*vm)))
36            };
37
38            if fire_immediately {
39                fire();
40            }
41
42            let on_property_changed: Rc<dyn Fn(&str)> = Rc::new(move |e: &str| {
43                if e == property_name {
44                    fire();
45                }
46            });
47
48            let token = vm.property_changed().add(on_property_changed);
49
50            let vm = vm.clone();
51            Disposable::create(move || {
52                vm.property_changed().remove(token);
53            })
54        })
55    }
56}