Skip to main content

ferroui_base/
ferro_property_changed_event_args.rs

1use crate::data::core::{ValueType, ValueTypes};
2use crate::data::BindingPriority;
3use crate::{BoxedValue, FerroObject, FerroProperty, Ref};
4use std::any::Any;
5
6/// Provides information for a property change.
7///
8/// The values are borrowed for the duration of the notification; use the typed
9/// accessors to copy them out.
10pub struct FerroPropertyChangedEventArgs<'a> {
11    sender: &'a FerroObject,
12    property: &'static FerroProperty,
13    old_value: Option<&'a dyn Any>,
14    new_value: &'a dyn Any,
15    priority: BindingPriority,
16    is_effective_value_change: bool,
17    is_inherited_value_change: bool,
18}
19
20impl<'a> FerroPropertyChangedEventArgs<'a> {
21    pub(crate) fn new(
22        sender: &'a FerroObject,
23        property: &'static FerroProperty,
24        old_value: Option<&'a dyn Any>,
25        new_value: &'a dyn Any,
26        priority: BindingPriority,
27        is_effective_value_change: bool,
28    ) -> Self {
29        Self { sender, property, old_value, new_value, priority, is_effective_value_change, is_inherited_value_change: false }
30    }
31
32    /// The object that the property changed on.
33    #[inline]
34    pub fn sender(&self) -> &'a FerroObject {
35        self.sender
36    }
37
38    /// The property that changed.
39    #[inline]
40    pub fn property(&self) -> &'static FerroProperty {
41        self.property
42    }
43
44    /// The old value of the property, untyped. `None` when only the base
45    /// value changed.
46    #[inline]
47    pub fn old_value(&self) -> Option<&'a dyn Any> {
48        self.old_value
49    }
50
51    /// The new value of the property, untyped.
52    #[inline]
53    pub fn new_value(&self) -> &'a dyn Any {
54        self.new_value
55    }
56
57    /// The priority of the binding that produced the value.
58    #[inline]
59    pub fn priority(&self) -> BindingPriority {
60        self.priority
61    }
62
63    /// Whether the change represents a change to the effective value of the
64    /// property (as opposed to a change of a lower-priority base value).
65    #[inline]
66    pub fn is_effective_value_change(&self) -> bool {
67        self.is_effective_value_change
68    }
69
70    /// Whether an ancestor or inheritance-parent change produced this notification.
71    /// This differs from the resulting value's priority: clearing a local value
72    /// can expose an inherited value while still being an edit on this object.
73    pub fn is_inherited_value_change(&self) -> bool {
74        self.is_inherited_value_change
75    }
76
77    pub(crate) fn from_inheritance(mut self) -> Self {
78        self.is_inherited_value_change = true;
79        self
80    }
81
82    /// Gets the old value of the property. Panics if `T` is not the type of
83    /// the property.
84    pub fn get_old_value<T: Clone + 'static>(&self) -> Option<T> {
85        self.old_value.map(|v| Self::cast::<T>(self.property, v).clone())
86    }
87
88    /// Gets the new value of the property. Panics if `T` is not the type of
89    /// the property.
90    pub fn get_new_value<T: Clone + 'static>(&self) -> T {
91        Self::cast::<T>(self.property, self.new_value).clone()
92    }
93
94    /// Gets the old and new values of the property. Panics if `T` is not the
95    /// type of the property or if there is no old value.
96    pub fn get_old_and_new_value<T: Clone + 'static>(&self) -> (T, T) {
97        (self.get_old_value::<T>().expect("change has no old value"), self.get_new_value::<T>())
98    }
99
100    /// The change held by value: the form a handler receives that cannot
101    /// borrow the values (a handler attached from markup).
102    pub fn to_owned_args(&self) -> OwnedFerroPropertyChangedEventArgs {
103        let routes = self.property.routes();
104        OwnedFerroPropertyChangedEventArgs {
105            sender: self.sender.to_ref(),
106            property: self.property,
107            old_value: self.old_value.and_then(|value| routes.route_box_value(value)).and_then(untyped),
108            new_value: routes.route_box_value(self.new_value).and_then(untyped),
109            priority: self.priority,
110            is_effective_value_change: self.is_effective_value_change,
111            is_inherited_value_change: self.is_inherited_value_change,
112        }
113    }
114
115    /// Returns the same change retargeted at another sender, used when an
116    /// inherited value change propagates down the tree.
117    pub(crate) fn with_sender<'b>(&'b self, sender: &'b FerroObject) -> FerroPropertyChangedEventArgs<'b> {
118        FerroPropertyChangedEventArgs {
119            sender,
120            property: self.property,
121            old_value: self.old_value,
122            new_value: self.new_value,
123            priority: self.priority,
124            is_effective_value_change: self.is_effective_value_change,
125            is_inherited_value_change: self.is_inherited_value_change,
126        }
127    }
128
129    fn cast<'v, T: 'static>(property: &FerroProperty, value: &'v dyn Any) -> &'v T {
130        value.downcast_ref::<T>().unwrap_or_else(|| {
131            panic!(
132                "property '{}' is of type {}, not {}",
133                property.name(),
134                property.property_type_name(),
135                std::any::type_name::<T>()
136            )
137        })
138    }
139}
140
141/// The untyped form of a boxed property value: null or the contents of a
142/// nullable, the value itself otherwise.
143fn untyped(value: BoxedValue) -> Option<BoxedValue> {
144    match ValueTypes::try_cast(&value, ValueType::of::<Option<BoxedValue>>()) {
145        Some(untyped) => untyped.downcast_ref::<Option<BoxedValue>>().cloned().flatten(),
146        None => Some(value),
147    }
148}
149
150/// A property change held by value
151/// ([`FerroPropertyChangedEventArgs::to_owned_args`]): the object, the
152/// property and copies of the values in their untyped form.
153///
154/// This is what the handler of the `PropertyChanged` event of an object
155/// receives when it is attached from markup.
156#[derive(Clone)]
157pub struct OwnedFerroPropertyChangedEventArgs {
158    sender: Ref<FerroObject>,
159    property: &'static FerroProperty,
160    old_value: Option<BoxedValue>,
161    new_value: Option<BoxedValue>,
162    priority: BindingPriority,
163    is_effective_value_change: bool,
164    is_inherited_value_change: bool,
165}
166
167impl OwnedFerroPropertyChangedEventArgs {
168    /// The object that the property changed on.
169    pub fn sender(&self) -> Ref<FerroObject> {
170        self.sender.clone()
171    }
172
173    /// The property that changed.
174    pub fn property(&self) -> &'static FerroProperty {
175        self.property
176    }
177
178    /// The old value of the property. Null when only the base value changed.
179    pub fn old_value(&self) -> Option<BoxedValue> {
180        self.old_value.clone()
181    }
182
183    /// The new value of the property.
184    pub fn new_value(&self) -> Option<BoxedValue> {
185        self.new_value.clone()
186    }
187
188    /// The priority of the binding that produced the value.
189    pub fn priority(&self) -> BindingPriority {
190        self.priority
191    }
192
193    /// Whether the change represents a change to the effective value of the
194    /// property (as opposed to a change of a lower-priority base value).
195    pub fn is_effective_value_change(&self) -> bool {
196        self.is_effective_value_change
197    }
198
199    /// Whether an ancestor or inheritance-parent change produced this notification.
200    pub fn is_inherited_value_change(&self) -> bool {
201        self.is_inherited_value_change
202    }
203}
204
205impl PartialEq for OwnedFerroPropertyChangedEventArgs {
206    fn eq(&self, other: &Self) -> bool {
207        self.sender == other.sender
208            && self.property == other.property
209            && ValueTypes::identity_equals(self.old_value.as_ref(), other.old_value.as_ref())
210            && ValueTypes::identity_equals(self.new_value.as_ref(), other.new_value.as_ref())
211            && self.priority == other.priority
212            && self.is_effective_value_change == other.is_effective_value_change
213            && self.is_inherited_value_change == other.is_inherited_value_change
214    }
215}