Skip to main content

ferroui_diagnostics/diagnostics/view_models/
view_model_base.rs

1//! Port of `Diagnostics/ViewModels/ViewModelBase.cs`.
2
3use ferroui_base::data::model::{Event, INotifyPropertyChanged};
4use std::cell::{Cell, RefCell};
5use std::rc::Rc;
6
7/// The base of the view models of the developer tools: the property change
8/// notification and the helpers that raise it.
9///
10/// A view model embeds it (the base class of the original) and forwards
11/// [`INotifyPropertyChanged`] to it. The name of the property is passed
12/// explicitly where the original takes the name of the calling member.
13///
14/// The original also records every addition and removal of a handler in a
15/// list that nothing reads; the port leaves that list out.
16#[derive(Default)]
17pub struct ViewModelBase {
18    property_changed: Event<str>,
19    on_property_changed: RefCell<Option<Rc<dyn Fn(&str)>>>,
20}
21
22impl INotifyPropertyChanged for ViewModelBase {
23    fn property_changed(&self) -> &Event<str> {
24        &self.property_changed
25    }
26}
27
28impl ViewModelBase {
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// The override of `protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)`
34    /// of a deriving view model: called with the name of the property before
35    /// the handlers of [`property_changed`](INotifyPropertyChanged::property_changed).
36    /// The base implementation does nothing. A deriving view model sets it
37    /// once, when it is constructed (a closure over a weak reference to
38    /// itself).
39    pub fn set_on_property_changed(&self, handler: Option<Rc<dyn Fn(&str)>>) {
40        *self.on_property_changed.borrow_mut() = handler;
41    }
42
43    /// `RaiseAndSetIfChanged(ref field, value)` for a field of a type that is
44    /// not `Copy`: stores `value` and raises the notification of
45    /// `property_name` when it differs from the stored value. Returns whether
46    /// it did.
47    pub fn raise_and_set_if_changed<T: PartialEq>(&self, field: &RefCell<T>, value: T, property_name: &str) -> bool {
48        if *field.borrow() != value {
49            // The borrow ends before the handlers run: they read the property.
50            *field.borrow_mut() = value;
51            self.raise_property_changed(property_name);
52            return true;
53        }
54
55        false
56    }
57
58    /// `RaiseAndSetIfChanged(ref field, value)` for a `Copy` field.
59    pub fn raise_and_set_if_changed_cell<T: PartialEq + Copy>(&self, field: &Cell<T>, value: T, property_name: &str) -> bool {
60        if field.get() != value {
61            field.set(value);
62            self.raise_property_changed(property_name);
63            return true;
64        }
65
66        false
67    }
68
69    /// `RaisePropertyChanged(propertyName)`: the override of
70    /// `OnPropertyChanged`, then the handlers.
71    pub fn raise_property_changed(&self, property_name: &str) {
72        let on_property_changed = self.on_property_changed.borrow().clone();
73        if let Some(on_property_changed) = on_property_changed {
74            on_property_changed(property_name);
75        }
76        self.property_changed.raise(property_name);
77    }
78}