1use crate::data::{BindingBase, BindingError, BindingExpressionBase, BindingPriority, BindingValue, BindingValueType};
2use crate::property_store::{BindingSource, ValueStore};
3use crate::reactive::{Disposable, IDisposable, IObservable};
4use crate::threading::Dispatcher;
5use crate::type_system::{__forwards_to_parent, cast_this, parent_vtable};
6use crate::utilities::HandlerList;
7use crate::{
8 BoxedValue, DirectPropertyBase, FerroProperty, FerroPropertyChangedEventArgs, FerroPropertyRegistry,
9 Nullable, ObjectType, PropertyValue, Ref, StaticType, StyledProperty, Subclassable, TypeInfo, Upcast, WeakRef,
10};
11use std::any::Any;
12use std::cell::{Cell, OnceCell, RefCell};
13use std::fmt;
14use std::rc::{Rc, Weak};
15use std::sync::Arc;
16
17type PropertyChangedHandler = dyn Fn(&FerroPropertyChangedEventArgs<'_>);
18
19#[derive(Clone, Copy)]
21#[repr(C)]
22pub struct FerroObjectVTable {
23 pub type_info: &'static TypeInfo,
24 pub constructed: fn(&FerroObject),
25 pub on_property_changed_core: fn(&FerroObject, &FerroPropertyChangedEventArgs<'_>),
26 pub on_property_changed: fn(&FerroObject, &FerroPropertyChangedEventArgs<'_>),
27 pub update_data_validation: fn(&FerroObject, &'static FerroProperty, BindingValueType, Option<&BindingError>),
28}
29
30#[repr(C)]
32pub struct FerroObject {
33 this: OnceCell<Weak<dyn Any>>,
34 vtable: Cell<Option<&'static FerroObjectVTable>>,
35 values: ValueStore,
36 inheritance_parent: RefCell<Option<WeakRef<FerroObject>>>,
37 inheritance_children: RefCell<Vec<WeakRef<FerroObject>>>,
38 property_changed: HandlerList<PropertyChangedHandler>,
39 dispatcher: OnceCell<Arc<Dispatcher>>,
42}
43
44static FERRO_OBJECT_TYPE: TypeInfo = TypeInfo::new("FerroObject", None)
45 .with_module_path(module_path!())
46 .with_class_init(<FerroObject as ObjectType>::register_class)
47 .with_handles(|| [std::any::TypeId::of::<Ref<FerroObject>>(), std::any::TypeId::of::<Option<Ref<FerroObject>>>()])
48 .with_interfaces({
49 #[allow(unused_imports)]
50 use crate::ClassDefaults as _;
51 <FerroObject>::__INTERFACES
52 })
53 .with_markup({
54 #[allow(unused_imports)]
55 use crate::ClassDefaults as _;
56 <FerroObject>::__MARKUP
57 });
58
59impl StaticType for FerroObject {
60 const TYPE: &'static TypeInfo = &FERRO_OBJECT_TYPE;
61}
62
63impl Upcast<FerroObject> for FerroObject {
64 #[inline]
65 fn upcast(&self) -> &FerroObject {
66 self
67 }
68}
69
70unsafe impl ObjectType for FerroObject {
72 type Parent = FerroObject;
73 type VTable = FerroObjectVTable;
74
75 fn vtable() -> &'static FerroObjectVTable {
76 static VTABLE: std::sync::OnceLock<FerroObjectVTable> = std::sync::OnceLock::new();
77 VTABLE.get_or_init(<FerroObject as Subclassable<FerroObject>>::build_vtable)
78 }
79
80 #[inline]
81 fn register_class() {
82 crate::ferro_thread_local! {
83 static REGISTERED: Cell<bool> = const { Cell::new(false) };
84 }
85 if !REGISTERED.replace(true) {
86 crate::type_system::__register_class::<FerroObject>();
87 }
88 }
89}
90
91pub trait FerroObjectImpl: ObjectType {
93 #[doc(hidden)]
99 const __OVERRIDES: Option<&'static [&'static str]> = None;
100
101 fn constructed(this: &Self) {
105 Self::parent_constructed(this)
106 }
107
108 fn on_property_changed_core(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
112 Self::parent_on_property_changed_core(this, change)
113 }
114
115 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
117 Self::parent_on_property_changed(this, change)
118 }
119
120 fn update_data_validation(
123 this: &Self,
124 property: &'static FerroProperty,
125 state: BindingValueType,
126 error: Option<&BindingError>,
127 ) {
128 Self::parent_update_data_validation(this, property, state, error)
129 }
130}
131
132pub trait FerroObjectImplExt: FerroObjectImpl {
134 #[inline]
135 fn parent_constructed(this: &Self) {
136 (parent_vtable::<Self, FerroObjectVTable>().constructed)(this.upcast())
137 }
138
139 #[inline]
140 fn parent_on_property_changed_core(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
141 (parent_vtable::<Self, FerroObjectVTable>().on_property_changed_core)(this.upcast(), change)
142 }
143
144 #[inline]
145 fn parent_on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
146 (parent_vtable::<Self, FerroObjectVTable>().on_property_changed)(this.upcast(), change)
147 }
148
149 #[inline]
150 fn parent_update_data_validation(
151 this: &Self,
152 property: &'static FerroProperty,
153 state: BindingValueType,
154 error: Option<&BindingError>,
155 ) {
156 (parent_vtable::<Self, FerroObjectVTable>().update_data_validation)(this.upcast(), property, state, error)
157 }
158}
159
160impl<T: FerroObjectImpl> FerroObjectImplExt for T {}
161
162impl<T: FerroObjectImpl> Subclassable<T> for FerroObject {
163 fn build_vtable() -> FerroObjectVTable {
164 let mut table = FerroObjectVTable {
167 type_info: T::TYPE,
168 constructed: |this| T::constructed(unsafe { cast_this::<FerroObject, T>(this) }),
169 on_property_changed_core: |this, change| {
170 T::on_property_changed_core(unsafe { cast_this::<FerroObject, T>(this) }, change)
171 },
172 on_property_changed: |this, change| {
173 T::on_property_changed(unsafe { cast_this::<FerroObject, T>(this) }, change)
174 },
175 update_data_validation: |this, property, state, error| {
176 T::update_data_validation(unsafe { cast_this::<FerroObject, T>(this) }, property, state, error)
177 },
178 };
179
180 let forwards: [bool; 4] = const {
186 let overrides = <T as FerroObjectImpl>::__OVERRIDES;
187 [
188 __forwards_to_parent::<T, FerroObjectVTable>(overrides, "constructed"),
189 __forwards_to_parent::<T, FerroObjectVTable>(overrides, "on_property_changed_core"),
190 __forwards_to_parent::<T, FerroObjectVTable>(overrides, "on_property_changed"),
191 __forwards_to_parent::<T, FerroObjectVTable>(overrides, "update_data_validation"),
192 ]
193 };
194 if forwards[0] {
195 table.constructed = parent_vtable::<T, FerroObjectVTable>().constructed;
196 }
197 if forwards[1] {
198 table.on_property_changed_core = parent_vtable::<T, FerroObjectVTable>().on_property_changed_core;
199 }
200 if forwards[2] {
201 table.on_property_changed = parent_vtable::<T, FerroObjectVTable>().on_property_changed;
202 }
203 if forwards[3] {
204 table.update_data_validation = parent_vtable::<T, FerroObjectVTable>().update_data_validation;
205 }
206 table
207 }
208}
209
210impl FerroObjectImpl for FerroObject {
211 fn constructed(_this: &Self) {}
212
213 fn on_property_changed_core(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
214 if change.is_effective_value_change() {
215 this.on_property_changed(change);
216 }
217 }
218
219 fn on_property_changed(_this: &Self, _change: &FerroPropertyChangedEventArgs<'_>) {}
220
221 fn update_data_validation(
222 _this: &Self,
223 _property: &'static FerroProperty,
224 _state: BindingValueType,
225 _error: Option<&BindingError>,
226 ) {
227 }
228}
229
230impl FerroObject {
231 pub const TYPE: &'static TypeInfo = &FERRO_OBJECT_TYPE;
233
234 pub fn construct() -> FerroObject {
238 FerroObject {
239 this: OnceCell::new(),
240 vtable: Cell::new(None),
241 values: ValueStore::new(),
242 inheritance_parent: RefCell::new(None),
243 inheritance_children: RefCell::new(Vec::new()),
244 property_changed: HandlerList::new(),
245 dispatcher: OnceCell::new(),
246 }
247 }
248
249 #[inline]
252 pub fn dispatcher(&self) -> &Arc<Dispatcher> {
253 self.dispatcher.get_or_init(Dispatcher::current_dispatcher)
256 }
257
258 #[inline]
261 pub fn check_access(&self) -> bool {
262 self.dispatcher().check_access()
263 }
264
265 #[inline]
270 #[track_caller]
271 pub fn verify_access(&self) {
272 self.dispatcher().verify_access()
273 }
274
275 pub fn new() -> Ref<FerroObject> {
277 crate::instantiate(Self::construct())
278 }
279
280 #[doc(hidden)]
288 pub unsafe fn attach(&self, this: Weak<dyn Any>, vtable: *const ()) {
289 let _ = self.this.set(this);
290 self.vtable.set(Some(&*(vtable as *const FerroObjectVTable)));
291 }
292
293 #[inline]
294 fn vt(&self) -> &'static FerroObjectVTable {
295 self.vtable.get().expect("object is not instantiated")
296 }
297
298 #[doc(hidden)]
300 #[inline]
301 pub fn vtable_of<T: ObjectType>(this: &T) -> &'static T::VTable {
302 let object: &FerroObject = this.upcast();
303 let table: *const FerroObjectVTable = object.vt();
304 unsafe { &*(table as *const T::VTable) }
308 }
309
310 #[doc(hidden)]
312 #[inline]
313 pub fn ref_of<T: ObjectType>(this: &T) -> Ref<T> {
314 let object: &FerroObject = this.upcast();
315 let ptr = object
316 .this
317 .get()
318 .and_then(Weak::upgrade)
319 .expect("object is not instantiated or is being dropped");
320 Ref::from_rc(ptr)
322 }
323
324 #[inline]
326 pub fn to_ref(&self) -> Ref<FerroObject> {
327 Self::ref_of(self)
328 }
329
330 #[inline]
332 pub fn to_weak(&self) -> WeakRef<FerroObject> {
333 WeakRef::from_weak(self.this.get().expect("object is not instantiated").clone())
334 }
335
336 #[inline]
338 pub fn get_type(&self) -> &'static TypeInfo {
339 match self.vtable.get() {
340 Some(table) => table.type_info,
341 None => Self::TYPE,
342 }
343 }
344
345 #[inline]
347 pub fn is<T: ObjectType>(&self) -> bool {
348 T::TYPE.is_assignable_from(self.get_type())
349 }
350
351 #[inline]
353 pub fn downcast_ref<T: ObjectType>(&self) -> Option<&T> {
354 if self.is::<T>() {
355 Some(unsafe { cast_this::<FerroObject, T>(self) })
358 } else {
359 None
360 }
361 }
362
363 #[inline]
364 pub(crate) fn values(&self) -> &ValueStore {
365 &self.values
366 }
367
368 #[inline]
371 pub(crate) fn constructed(&self) {
372 crate::perf_count_virtual!("FerroObject::constructed");
373 (self.vt().constructed)(self)
374 }
375
376 #[inline]
377 pub(crate) fn on_property_changed_core(&self, change: &FerroPropertyChangedEventArgs<'_>) {
378 crate::perf_count_virtual!("FerroObject::on_property_changed_core");
379 (self.vt().on_property_changed_core)(self, change)
380 }
381
382 #[inline]
384 pub fn on_property_changed(&self, change: &FerroPropertyChangedEventArgs<'_>) {
385 crate::perf_count_virtual!("FerroObject::on_property_changed");
386 (self.vt().on_property_changed)(self, change)
387 }
388
389 #[inline]
390 pub(crate) fn on_update_data_validation(
391 &self,
392 property: &'static FerroProperty,
393 state: BindingValueType,
394 error: Option<BindingError>,
395 ) {
396 crate::perf_count_virtual!("FerroObject::update_data_validation");
397 (self.vt().update_data_validation)(self, property, state, error.as_ref())
398 }
399
400 pub fn inheritance_parent(&self) -> Option<Ref<FerroObject>> {
404 self.inheritance_parent.borrow().as_ref().and_then(WeakRef::upgrade)
405 }
406
407 pub(crate) fn is_inheritance_parent(&self, parent: &FerroObject) -> bool {
410 self.inheritance_parent.borrow().as_ref().is_some_and(|weak| weak.points_at(parent))
411 }
412
413 pub fn set_inheritance_parent(&self, value: impl Into<Nullable<FerroObject>>) {
416 let value = value.into().0;
417 let value = value.as_ref();
418 let old = self.inheritance_parent();
419 let same = match (&old, value) {
420 (Some(a), Some(b)) => a.ptr_eq(b),
421 (None, None) => true,
422 _ => false,
423 };
424 if same {
425 return;
426 }
427 if let Some(old) = &old {
428 old.remove_inheritance_child(self);
429 }
430 *self.inheritance_parent.borrow_mut() = value.map(Ref::downgrade);
431 if let Some(new) = value {
432 let mut children = new.inheritance_children.borrow_mut();
433 if children.len() == children.capacity() {
434 children.retain(|c| c.upgrade().is_some());
437 }
438 children.push(self.to_weak());
439 }
440 self.values.set_inheritance_parent(self, value);
441 }
442
443 fn remove_inheritance_child(&self, child: &FerroObject) {
444 let child = child.to_ref();
445 self.inheritance_children.borrow_mut().retain(|c| !c.points_to(&child));
446 }
447
448 pub(crate) fn for_each_inheritance_child(&self, mut f: impl FnMut(&Ref<FerroObject>)) {
453 let count = self.inheritance_children.borrow().len();
454 for index in 0..count {
455 let child = match self.inheritance_children.borrow().get(index) {
460 Some(child) => child.upgrade(),
461 None => break,
462 };
463 if let Some(child) = child {
464 f(&child);
465 }
466 }
467 }
468
469 pub(crate) fn inheritance_children(&self) -> Vec<Ref<FerroObject>> {
470 let mut children = self.inheritance_children.borrow_mut();
471 if children.is_empty() {
472 return Vec::new();
473 }
474 let result: Vec<Ref<FerroObject>> = children.iter().filter_map(WeakRef::upgrade).collect();
475 if result.len() != children.len() {
476 children.retain(|c| c.upgrade().is_some());
479 }
480 result
481 }
482
483 pub fn clear_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) {
487 self.values.clear_value(self, property);
488 }
489
490 #[inline]
492 pub fn get_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> T {
493 self.values.get_value(self, property)
494 }
495
496 pub fn get_base_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> Option<T> {
499 self.values.get_base_value(property)
500 }
501
502 pub fn is_animating(&self, property: &FerroProperty) -> bool {
504 self.values.is_animating(property)
505 }
506
507 pub fn is_set(&self, property: &FerroProperty) -> bool {
510 self.values.is_set(property)
511 }
512
513 #[inline]
515 pub fn set_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
516 match value_marker(&value) {
517 Some(ValueMarker::Unset) => self.clear_value(property),
518 Some(ValueMarker::DoNothing) => {}
519 None => {
520 self.values.set_value(self, property, value, BindingPriority::LocalValue);
521 }
522 }
523 }
524
525 pub fn set_value_with_priority<T: PropertyValue>(
528 &self,
529 property: &'static StyledProperty<T>,
530 value: T,
531 priority: BindingPriority,
532 ) -> Option<Rc<dyn IDisposable>> {
533 Self::validate_priority(priority);
534 match value_marker(&value) {
535 Some(ValueMarker::Unset) => {
536 if priority == BindingPriority::LocalValue {
537 self.clear_value(property);
538 }
539 None
540 }
541 Some(ValueMarker::DoNothing) => None,
542 None => self.values.set_value(self, property, value, priority),
543 }
544 }
545
546 pub fn set_current_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
554 match value_marker(&value) {
555 Some(ValueMarker::Unset) => self.clear_value(property),
556 Some(ValueMarker::DoNothing) => {}
557 None => self.values.set_current_value(self, property, value),
558 }
559 }
560
561 pub fn bind_binding(
564 &self,
565 property: &'static FerroProperty,
566 binding: &dyn BindingBase,
567 ) -> Rc<dyn BindingExpressionBase> {
568 self.bind_binding_with_anchor(property, binding, None)
569 }
570
571 pub fn bind_binding_with_anchor(
577 &self,
578 property: &'static FerroProperty,
579 binding: &dyn BindingBase,
580 anchor: Option<&Ref<FerroObject>>,
581 ) -> Rc<dyn BindingExpressionBase> {
582 crate::perf_count!(BindingsInstanced);
583 let expression = binding.create_instance(self, Some(property), anchor);
584 self.values.add_binding_expression(self, property, expression)
585 }
586
587 pub fn indexer(&self, binding: &crate::data::IndexerDescriptor) -> crate::data::IndexerBinding {
591 let property = binding.property.expect("The IndexerDescriptor has no property.");
592 crate::data::IndexerBinding::new(self.to_ref(), property, binding.mode)
593 }
594
595 pub fn bind_indexer(
600 &self,
601 binding: &crate::data::IndexerDescriptor,
602 value: &dyn BindingBase,
603 ) -> Rc<dyn BindingExpressionBase> {
604 let property = binding.property.expect("The IndexerDescriptor has no property.");
605 self.bind_binding(property, value)
606 }
607
608 pub(crate) fn get_expression(&self, property: &'static FerroProperty) -> Option<Rc<dyn BindingExpressionBase>> {
611 self.values.get_expression(property)
612 }
613
614 pub fn bind<T: PropertyValue>(
616 &self,
617 property: &'static StyledProperty<T>,
618 source: Rc<dyn IObservable<T>>,
619 priority: BindingPriority,
620 ) -> Rc<dyn IDisposable> {
621 Self::validate_priority(priority);
622 self.values.add_binding(self, property, BindingSource::Typed(source), priority)
623 }
624
625 pub fn bind_value<T: PropertyValue>(
627 &self,
628 property: &'static StyledProperty<T>,
629 source: Rc<dyn IObservable<BindingValue<T>>>,
630 priority: BindingPriority,
631 ) -> Rc<dyn IDisposable> {
632 Self::validate_priority(priority);
633 self.values.add_binding(self, property, BindingSource::Value(source), priority)
634 }
635
636 pub fn bind_untyped<T: PropertyValue>(
638 &self,
639 property: &'static StyledProperty<T>,
640 source: Rc<dyn IObservable<BoxedValue>>,
641 priority: BindingPriority,
642 ) -> Rc<dyn IDisposable> {
643 Self::validate_priority(priority);
644 self.values.add_binding(self, property, BindingSource::Untyped(source), priority)
645 }
646
647 pub fn coerce_value(&self, property: &'static FerroProperty) {
649 self.values.coerce_value(self, property);
650 }
651
652 pub fn get_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) -> T {
656 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
657 registered.invoke_getter(self)
658 }
659
660 pub fn set_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>, value: T) {
662 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
663 registered.invoke_setter(self, BindingValue::new(value));
664 }
665
666 pub fn clear_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) {
668 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
669 let unset = registered.get_unset_value_for(self);
670 registered.invoke_setter(self, BindingValue::new(unset));
671 }
672
673 pub(crate) fn set_direct_value_unchecked<T: PropertyValue>(
674 &self,
675 property: &'static DirectPropertyBase<T>,
676 value: BindingValue<T>,
677 ) {
678 let state = value.value_type();
679 let error = value.error().cloned();
680 match state {
681 BindingValueType::UNSET_VALUE | BindingValueType::BINDING_ERROR => {
682 let fallback =
683 if value.has_value() { value } else { value.with_value(property.get_unset_value_for(self)) };
684 property.invoke_setter(self, fallback);
685 }
686 BindingValueType::VALUE
687 | BindingValueType::BINDING_ERROR_WITH_FALLBACK
688 | BindingValueType::DATA_VALIDATION_ERROR
689 | BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK => property.invoke_setter(self, value),
690 _ => {}
692 }
693 if property.get_metadata_for(self).enable_data_validation() == Some(true) {
694 self.on_update_data_validation(property, state, error);
695 }
696 }
697
698 pub fn bind_direct<T: PropertyValue>(
700 &self,
701 property: &'static DirectPropertyBase<T>,
702 source: Rc<dyn IObservable<T>>,
703 ) -> Rc<dyn IDisposable> {
704 let property = self.writable_direct(property);
705 self.values.add_direct_binding(self, property, BindingSource::Typed(source))
706 }
707
708 pub fn bind_direct_value<T: PropertyValue>(
710 &self,
711 property: &'static DirectPropertyBase<T>,
712 source: Rc<dyn IObservable<BindingValue<T>>>,
713 ) -> Rc<dyn IDisposable> {
714 let property = self.writable_direct(property);
715 self.values.add_direct_binding(self, property, BindingSource::Value(source))
716 }
717
718 pub fn bind_direct_untyped<T: PropertyValue>(
720 &self,
721 property: &'static DirectPropertyBase<T>,
722 source: Rc<dyn IObservable<BoxedValue>>,
723 ) -> Rc<dyn IDisposable> {
724 let property = self.writable_direct(property);
725 self.values.add_direct_binding(self, property, BindingSource::Untyped(source))
726 }
727
728 fn writable_direct<T: PropertyValue>(
729 &self,
730 property: &'static DirectPropertyBase<T>,
731 ) -> &'static DirectPropertyBase<T> {
732 let property = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
733 assert!(!property.is_read_only(), "The property {} is readonly.", property.name());
734 property
735 }
736
737 pub fn set_and_raise<T: PropertyValue>(
740 &self,
741 property: &'static DirectPropertyBase<T>,
742 field: &RefCell<T>,
743 value: T,
744 ) -> bool {
745 if *field.borrow() == value {
746 return false;
747 }
748 let old = field.replace(value.clone());
749 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
750 true
751 }
752
753 pub fn set_and_raise_cell<T: PropertyValue + Copy>(
756 &self,
757 property: &'static DirectPropertyBase<T>,
758 field: &Cell<T>,
759 value: T,
760 ) -> bool {
761 let old = field.get();
762 if old == value {
763 return false;
764 }
765 field.set(value);
766 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
767 true
768 }
769
770 pub fn get_value_untyped(&self, property: &'static FerroProperty) -> BoxedValue {
774 property.routes().route_get_value(self)
775 }
776
777 pub fn set_value_untyped(
780 &self,
781 property: &'static FerroProperty,
782 value: &dyn Any,
783 priority: BindingPriority,
784 ) -> Option<Rc<dyn IDisposable>> {
785 property.routes().route_set_value(self, value, priority)
786 }
787
788 pub fn set_current_value_untyped(&self, property: &'static FerroProperty, value: &dyn Any) {
790 property.routes().route_set_current_value(self, value)
791 }
792
793 pub fn clear_value_untyped(&self, property: &'static FerroProperty) {
795 property.routes().route_clear_value(self)
796 }
797
798 pub fn bind_property_untyped(
800 &self,
801 property: &'static FerroProperty,
802 source: Rc<dyn IObservable<BoxedValue>>,
803 priority: BindingPriority,
804 ) -> Rc<dyn IDisposable> {
805 property.routes().route_bind(self, source, priority)
806 }
807
808 pub fn property_changed(
813 &self,
814 handler: impl Fn(&FerroPropertyChangedEventArgs<'_>) + 'static,
815 ) -> Rc<dyn IDisposable> {
816 let token = self.property_changed.add(Rc::new(handler));
817 let weak = self.to_weak();
818 Disposable::create(move || {
819 if let Some(this) = weak.upgrade() {
820 this.property_changed.remove(token);
821 }
822 })
823 }
824
825 pub fn add_property_changed_handler(&self, handler: Rc<dyn Fn(&FerroPropertyChangedEventArgs<'_>)>) -> u64 {
830 self.property_changed.add(handler)
831 }
832
833 pub fn remove_property_changed_handler(&self, token: u64) -> bool {
835 self.property_changed.remove(token)
836 }
837
838 pub fn property_changed_subscriber_count(&self) -> usize {
842 self.property_changed.len()
843 }
844
845 pub fn raise_property_changed<T: PropertyValue>(
847 &self,
848 property: &'static FerroProperty,
849 old_value: Option<&T>,
850 new_value: &T,
851 priority: BindingPriority,
852 is_effective_value: bool,
853 ) {
854 crate::perf_count!(PropertyChangesRaised);
855 let e = FerroPropertyChangedEventArgs::new(
856 self,
857 property,
858 old_value.map(|v| v as &dyn Any),
859 new_value,
860 priority,
861 is_effective_value,
862 );
863
864 self.on_property_changed_core(&e);
865
866 if is_effective_value {
867 crate::perf_count!(PropertyChangesEffective);
868 property.notify_changed(&e);
869 if !self.property_changed.is_empty() {
870 crate::perf_count!(PropertyChangesWithObjectListeners);
871 for (_, handler) in self.property_changed.snapshot().iter() {
872 handler(&e);
873 }
874 }
875 }
876 }
877
878 pub fn raise_direct_property_changed<T: PropertyValue>(
880 &self,
881 property: &'static DirectPropertyBase<T>,
882 old_value: &T,
883 new_value: &T,
884 ) {
885 self.raise_property_changed(property, Some(old_value), new_value, BindingPriority::LocalValue, true);
886 }
887
888 fn validate_priority(priority: BindingPriority) {
889 if priority < BindingPriority::Animation || priority >= BindingPriority::Inherited {
890 panic!("Invalid priority {priority:?}");
891 }
892 }
893}
894
895impl fmt::Debug for FerroObject {
896 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
897 f.write_str(self.get_type().name())
898 }
899}
900
901enum ValueMarker {
903 Unset,
904 DoNothing,
905}
906
907#[inline]
911fn value_marker<T: 'static>(value: &T) -> Option<ValueMarker> {
912 let any: &dyn Any = value;
913 let boxed = match any.downcast_ref::<BoxedValue>() {
914 Some(boxed) => boxed,
915 None => any.downcast_ref::<Option<BoxedValue>>()?.as_ref()?,
916 };
917 if boxed.is::<crate::UnsetValueType>() {
918 Some(ValueMarker::Unset)
919 } else if boxed.is::<crate::DoNothingType>() {
920 Some(ValueMarker::DoNothing)
921 } else {
922 None
923 }
924}