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 fn set_inheritance_parent(&self, value: impl Into<Nullable<FerroObject>>) {
410 let value = value.into().0;
411 let value = value.as_ref();
412 let old = self.inheritance_parent();
413 let same = match (&old, value) {
414 (Some(a), Some(b)) => a.ptr_eq(b),
415 (None, None) => true,
416 _ => false,
417 };
418 if same {
419 return;
420 }
421 if let Some(old) = &old {
422 old.remove_inheritance_child(self);
423 }
424 *self.inheritance_parent.borrow_mut() = value.map(Ref::downgrade);
425 if let Some(new) = value {
426 let mut children = new.inheritance_children.borrow_mut();
427 if children.len() == children.capacity() {
428 children.retain(|c| c.upgrade().is_some());
431 }
432 children.push(self.to_weak());
433 }
434 self.values.set_inheritance_parent(self, value);
435 }
436
437 fn remove_inheritance_child(&self, child: &FerroObject) {
438 let child = child.to_ref();
439 self.inheritance_children.borrow_mut().retain(|c| !c.points_to(&child));
440 }
441
442 pub(crate) fn for_each_inheritance_child(&self, mut f: impl FnMut(&Ref<FerroObject>)) {
447 let count = self.inheritance_children.borrow().len();
448 for index in 0..count {
449 let child = match self.inheritance_children.borrow().get(index) {
454 Some(child) => child.upgrade(),
455 None => break,
456 };
457 if let Some(child) = child {
458 f(&child);
459 }
460 }
461 }
462
463 pub(crate) fn inheritance_children(&self) -> Vec<Ref<FerroObject>> {
464 let mut children = self.inheritance_children.borrow_mut();
465 if children.is_empty() {
466 return Vec::new();
467 }
468 let result: Vec<Ref<FerroObject>> = children.iter().filter_map(WeakRef::upgrade).collect();
469 if result.len() != children.len() {
470 children.retain(|c| c.upgrade().is_some());
473 }
474 result
475 }
476
477 pub fn clear_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) {
481 self.values.clear_value(self, property);
482 }
483
484 #[inline]
486 pub fn get_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> T {
487 self.values.get_value(self, property)
488 }
489
490 pub fn get_base_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> Option<T> {
493 self.values.get_base_value(property)
494 }
495
496 pub fn is_animating(&self, property: &FerroProperty) -> bool {
498 self.values.is_animating(property)
499 }
500
501 pub fn is_set(&self, property: &FerroProperty) -> bool {
504 self.values.is_set(property)
505 }
506
507 #[inline]
509 pub fn set_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
510 match value_marker(&value) {
511 Some(ValueMarker::Unset) => self.clear_value(property),
512 Some(ValueMarker::DoNothing) => {}
513 None => {
514 self.values.set_value(self, property, value, BindingPriority::LocalValue);
515 }
516 }
517 }
518
519 pub fn set_value_with_priority<T: PropertyValue>(
522 &self,
523 property: &'static StyledProperty<T>,
524 value: T,
525 priority: BindingPriority,
526 ) -> Option<Rc<dyn IDisposable>> {
527 Self::validate_priority(priority);
528 match value_marker(&value) {
529 Some(ValueMarker::Unset) => {
530 if priority == BindingPriority::LocalValue {
531 self.clear_value(property);
532 }
533 None
534 }
535 Some(ValueMarker::DoNothing) => None,
536 None => self.values.set_value(self, property, value, priority),
537 }
538 }
539
540 pub fn set_current_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
548 match value_marker(&value) {
549 Some(ValueMarker::Unset) => self.clear_value(property),
550 Some(ValueMarker::DoNothing) => {}
551 None => self.values.set_current_value(self, property, value),
552 }
553 }
554
555 pub fn bind_binding(
558 &self,
559 property: &'static FerroProperty,
560 binding: &dyn BindingBase,
561 ) -> Rc<dyn BindingExpressionBase> {
562 self.bind_binding_with_anchor(property, binding, None)
563 }
564
565 pub fn bind_binding_with_anchor(
571 &self,
572 property: &'static FerroProperty,
573 binding: &dyn BindingBase,
574 anchor: Option<&Ref<FerroObject>>,
575 ) -> Rc<dyn BindingExpressionBase> {
576 crate::perf_count!(BindingsInstanced);
577 let expression = binding.create_instance(self, Some(property), anchor);
578 self.values.add_binding_expression(self, property, expression)
579 }
580
581 pub fn indexer(&self, binding: &crate::data::IndexerDescriptor) -> crate::data::IndexerBinding {
585 let property = binding.property.expect("The IndexerDescriptor has no property.");
586 crate::data::IndexerBinding::new(self.to_ref(), property, binding.mode)
587 }
588
589 pub fn bind_indexer(
594 &self,
595 binding: &crate::data::IndexerDescriptor,
596 value: &dyn BindingBase,
597 ) -> Rc<dyn BindingExpressionBase> {
598 let property = binding.property.expect("The IndexerDescriptor has no property.");
599 self.bind_binding(property, value)
600 }
601
602 pub(crate) fn get_expression(&self, property: &'static FerroProperty) -> Option<Rc<dyn BindingExpressionBase>> {
605 self.values.get_expression(property)
606 }
607
608 pub fn bind<T: PropertyValue>(
610 &self,
611 property: &'static StyledProperty<T>,
612 source: Rc<dyn IObservable<T>>,
613 priority: BindingPriority,
614 ) -> Rc<dyn IDisposable> {
615 Self::validate_priority(priority);
616 self.values.add_binding(self, property, BindingSource::Typed(source), priority)
617 }
618
619 pub fn bind_value<T: PropertyValue>(
621 &self,
622 property: &'static StyledProperty<T>,
623 source: Rc<dyn IObservable<BindingValue<T>>>,
624 priority: BindingPriority,
625 ) -> Rc<dyn IDisposable> {
626 Self::validate_priority(priority);
627 self.values.add_binding(self, property, BindingSource::Value(source), priority)
628 }
629
630 pub fn bind_untyped<T: PropertyValue>(
632 &self,
633 property: &'static StyledProperty<T>,
634 source: Rc<dyn IObservable<BoxedValue>>,
635 priority: BindingPriority,
636 ) -> Rc<dyn IDisposable> {
637 Self::validate_priority(priority);
638 self.values.add_binding(self, property, BindingSource::Untyped(source), priority)
639 }
640
641 pub fn coerce_value(&self, property: &'static FerroProperty) {
643 self.values.coerce_value(self, property);
644 }
645
646 pub fn get_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) -> T {
650 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
651 registered.invoke_getter(self)
652 }
653
654 pub fn set_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>, value: T) {
656 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
657 registered.invoke_setter(self, BindingValue::new(value));
658 }
659
660 pub fn clear_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) {
662 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
663 let unset = registered.get_unset_value_for(self);
664 registered.invoke_setter(self, BindingValue::new(unset));
665 }
666
667 pub(crate) fn set_direct_value_unchecked<T: PropertyValue>(
668 &self,
669 property: &'static DirectPropertyBase<T>,
670 value: BindingValue<T>,
671 ) {
672 let state = value.value_type();
673 let error = value.error().cloned();
674 match state {
675 BindingValueType::UNSET_VALUE | BindingValueType::BINDING_ERROR => {
676 let fallback =
677 if value.has_value() { value } else { value.with_value(property.get_unset_value_for(self)) };
678 property.invoke_setter(self, fallback);
679 }
680 BindingValueType::VALUE
681 | BindingValueType::BINDING_ERROR_WITH_FALLBACK
682 | BindingValueType::DATA_VALIDATION_ERROR
683 | BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK => property.invoke_setter(self, value),
684 _ => {}
686 }
687 if property.get_metadata_for(self).enable_data_validation() == Some(true) {
688 self.on_update_data_validation(property, state, error);
689 }
690 }
691
692 pub fn bind_direct<T: PropertyValue>(
694 &self,
695 property: &'static DirectPropertyBase<T>,
696 source: Rc<dyn IObservable<T>>,
697 ) -> Rc<dyn IDisposable> {
698 let property = self.writable_direct(property);
699 self.values.add_direct_binding(self, property, BindingSource::Typed(source))
700 }
701
702 pub fn bind_direct_value<T: PropertyValue>(
704 &self,
705 property: &'static DirectPropertyBase<T>,
706 source: Rc<dyn IObservable<BindingValue<T>>>,
707 ) -> Rc<dyn IDisposable> {
708 let property = self.writable_direct(property);
709 self.values.add_direct_binding(self, property, BindingSource::Value(source))
710 }
711
712 pub fn bind_direct_untyped<T: PropertyValue>(
714 &self,
715 property: &'static DirectPropertyBase<T>,
716 source: Rc<dyn IObservable<BoxedValue>>,
717 ) -> Rc<dyn IDisposable> {
718 let property = self.writable_direct(property);
719 self.values.add_direct_binding(self, property, BindingSource::Untyped(source))
720 }
721
722 fn writable_direct<T: PropertyValue>(
723 &self,
724 property: &'static DirectPropertyBase<T>,
725 ) -> &'static DirectPropertyBase<T> {
726 let property = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
727 assert!(!property.is_read_only(), "The property {} is readonly.", property.name());
728 property
729 }
730
731 pub fn set_and_raise<T: PropertyValue>(
734 &self,
735 property: &'static DirectPropertyBase<T>,
736 field: &RefCell<T>,
737 value: T,
738 ) -> bool {
739 if *field.borrow() == value {
740 return false;
741 }
742 let old = field.replace(value.clone());
743 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
744 true
745 }
746
747 pub fn set_and_raise_cell<T: PropertyValue + Copy>(
750 &self,
751 property: &'static DirectPropertyBase<T>,
752 field: &Cell<T>,
753 value: T,
754 ) -> bool {
755 let old = field.get();
756 if old == value {
757 return false;
758 }
759 field.set(value);
760 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
761 true
762 }
763
764 pub fn get_value_untyped(&self, property: &'static FerroProperty) -> BoxedValue {
768 property.routes().route_get_value(self)
769 }
770
771 pub fn set_value_untyped(
774 &self,
775 property: &'static FerroProperty,
776 value: &dyn Any,
777 priority: BindingPriority,
778 ) -> Option<Rc<dyn IDisposable>> {
779 property.routes().route_set_value(self, value, priority)
780 }
781
782 pub fn set_current_value_untyped(&self, property: &'static FerroProperty, value: &dyn Any) {
784 property.routes().route_set_current_value(self, value)
785 }
786
787 pub fn clear_value_untyped(&self, property: &'static FerroProperty) {
789 property.routes().route_clear_value(self)
790 }
791
792 pub fn bind_property_untyped(
794 &self,
795 property: &'static FerroProperty,
796 source: Rc<dyn IObservable<BoxedValue>>,
797 priority: BindingPriority,
798 ) -> Rc<dyn IDisposable> {
799 property.routes().route_bind(self, source, priority)
800 }
801
802 pub fn property_changed(
807 &self,
808 handler: impl Fn(&FerroPropertyChangedEventArgs<'_>) + 'static,
809 ) -> Rc<dyn IDisposable> {
810 let token = self.property_changed.add(Rc::new(handler));
811 let weak = self.to_weak();
812 Disposable::create(move || {
813 if let Some(this) = weak.upgrade() {
814 this.property_changed.remove(token);
815 }
816 })
817 }
818
819 pub fn property_changed_subscriber_count(&self) -> usize {
823 self.property_changed.len()
824 }
825
826 pub fn raise_property_changed<T: PropertyValue>(
828 &self,
829 property: &'static FerroProperty,
830 old_value: Option<&T>,
831 new_value: &T,
832 priority: BindingPriority,
833 is_effective_value: bool,
834 ) {
835 crate::perf_count!(PropertyChangesRaised);
836 let e = FerroPropertyChangedEventArgs::new(
837 self,
838 property,
839 old_value.map(|v| v as &dyn Any),
840 new_value,
841 priority,
842 is_effective_value,
843 );
844
845 self.on_property_changed_core(&e);
846
847 if is_effective_value {
848 crate::perf_count!(PropertyChangesEffective);
849 property.notify_changed(&e);
850 if !self.property_changed.is_empty() {
851 crate::perf_count!(PropertyChangesWithObjectListeners);
852 for (_, handler) in self.property_changed.snapshot().iter() {
853 handler(&e);
854 }
855 }
856 }
857 }
858
859 pub fn raise_direct_property_changed<T: PropertyValue>(
861 &self,
862 property: &'static DirectPropertyBase<T>,
863 old_value: &T,
864 new_value: &T,
865 ) {
866 self.raise_property_changed(property, Some(old_value), new_value, BindingPriority::LocalValue, true);
867 }
868
869 fn validate_priority(priority: BindingPriority) {
870 if priority < BindingPriority::Animation || priority >= BindingPriority::Inherited {
871 panic!("Invalid priority {priority:?}");
872 }
873 }
874}
875
876impl fmt::Debug for FerroObject {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 f.write_str(self.get_type().name())
879 }
880}
881
882enum ValueMarker {
884 Unset,
885 DoNothing,
886}
887
888#[inline]
892fn value_marker<T: 'static>(value: &T) -> Option<ValueMarker> {
893 let any: &dyn Any = value;
894 let boxed = match any.downcast_ref::<BoxedValue>() {
895 Some(boxed) => boxed,
896 None => any.downcast_ref::<Option<BoxedValue>>()?.as_ref()?,
897 };
898 if boxed.is::<crate::UnsetValueType>() {
899 Some(ValueMarker::Unset)
900 } else if boxed.is::<crate::DoNothingType>() {
901 Some(ValueMarker::DoNothing)
902 } else {
903 None
904 }
905}