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}