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