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 #[cfg(all(debug_assertions, feature = "hot-reload"))]
489 pub fn local_write_version(&self, property: &FerroProperty) -> u64 {
490 self.values.local_write_version(property)
491 }
492
493 #[cfg(all(debug_assertions, feature = "hot-reload"))]
497 pub fn local_assignment_version(&self, property: &FerroProperty) -> u64 {
498 self.values.local_assignment_version(property)
499 }
500
501 pub fn clear_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) {
503 self.values.clear_value(self, property);
504 }
505
506 #[inline]
508 pub fn get_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> T {
509 self.values.get_value(self, property)
510 }
511
512 pub fn get_base_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>) -> Option<T> {
515 self.values.get_base_value(property)
516 }
517
518 pub fn is_animating(&self, property: &FerroProperty) -> bool {
520 self.values.is_animating(property)
521 }
522
523 pub fn is_set(&self, property: &FerroProperty) -> bool {
526 self.values.is_set(property)
527 }
528
529 pub fn has_local_value_binding(&self, property: &FerroProperty) -> bool {
534 self.values.has_local_value_binding(property)
535 }
536
537 #[inline]
539 pub fn set_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
540 match value_marker(&value) {
541 Some(ValueMarker::Unset) => self.clear_value(property),
542 Some(ValueMarker::DoNothing) => {}
543 None => {
544 self.values.set_value(self, property, value, BindingPriority::LocalValue);
545 }
546 }
547 }
548
549 pub fn set_value_with_priority<T: PropertyValue>(
552 &self,
553 property: &'static StyledProperty<T>,
554 value: T,
555 priority: BindingPriority,
556 ) -> Option<Rc<dyn IDisposable>> {
557 Self::validate_priority(priority);
558 match value_marker(&value) {
559 Some(ValueMarker::Unset) => {
560 if priority == BindingPriority::LocalValue {
561 self.clear_value(property);
562 }
563 None
564 }
565 Some(ValueMarker::DoNothing) => None,
566 None => self.values.set_value(self, property, value, priority),
567 }
568 }
569
570 pub fn set_current_value<T: PropertyValue>(&self, property: &'static StyledProperty<T>, value: T) {
578 match value_marker(&value) {
579 Some(ValueMarker::Unset) => self.clear_value(property),
580 Some(ValueMarker::DoNothing) => {}
581 None => self.values.set_current_value(self, property, value),
582 }
583 }
584
585 pub fn bind_binding(
588 &self,
589 property: &'static FerroProperty,
590 binding: &dyn BindingBase,
591 ) -> Rc<dyn BindingExpressionBase> {
592 self.bind_binding_with_anchor(property, binding, None)
593 }
594
595 pub fn bind_binding_with_anchor(
601 &self,
602 property: &'static FerroProperty,
603 binding: &dyn BindingBase,
604 anchor: Option<&Ref<FerroObject>>,
605 ) -> Rc<dyn BindingExpressionBase> {
606 crate::perf_count!(BindingsInstanced);
607 let expression = binding.create_instance(self, Some(property), anchor);
608 self.values.add_binding_expression(self, property, expression)
609 }
610
611 pub fn bind_binding_with_priority(
618 &self,
619 property: &'static FerroProperty,
620 binding: &dyn BindingBase,
621 priority: BindingPriority,
622 anchor: Option<&Ref<FerroObject>>,
623 ) -> Rc<dyn BindingExpressionBase> {
624 Self::validate_priority(priority);
625 crate::perf_count!(BindingsInstanced);
626 let expression = binding.create_instance(self, Some(property), anchor);
627 self.values.add_binding_expression_with_priority(self, property, expression, priority)
628 }
629
630 pub fn indexer(&self, binding: &crate::data::IndexerDescriptor) -> crate::data::IndexerBinding {
634 let property = binding.property.expect("The IndexerDescriptor has no property.");
635 crate::data::IndexerBinding::new(self.to_ref(), property, binding.mode)
636 }
637
638 pub fn bind_indexer(
643 &self,
644 binding: &crate::data::IndexerDescriptor,
645 value: &dyn BindingBase,
646 ) -> Rc<dyn BindingExpressionBase> {
647 let property = binding.property.expect("The IndexerDescriptor has no property.");
648 self.bind_binding(property, value)
649 }
650
651 pub(crate) fn get_expression(&self, property: &'static FerroProperty) -> Option<Rc<dyn BindingExpressionBase>> {
654 self.values.get_expression(property)
655 }
656
657 pub fn bind<T: PropertyValue>(
659 &self,
660 property: &'static StyledProperty<T>,
661 source: Rc<dyn IObservable<T>>,
662 priority: BindingPriority,
663 ) -> Rc<dyn IDisposable> {
664 Self::validate_priority(priority);
665 self.values.add_binding(self, property, BindingSource::Typed(source), priority)
666 }
667
668 pub fn bind_value<T: PropertyValue>(
670 &self,
671 property: &'static StyledProperty<T>,
672 source: Rc<dyn IObservable<BindingValue<T>>>,
673 priority: BindingPriority,
674 ) -> Rc<dyn IDisposable> {
675 Self::validate_priority(priority);
676 self.values.add_binding(self, property, BindingSource::Value(source), priority)
677 }
678
679 pub fn bind_untyped<T: PropertyValue>(
681 &self,
682 property: &'static StyledProperty<T>,
683 source: Rc<dyn IObservable<BoxedValue>>,
684 priority: BindingPriority,
685 ) -> Rc<dyn IDisposable> {
686 Self::validate_priority(priority);
687 self.values.add_binding(self, property, BindingSource::Untyped(source), priority)
688 }
689
690 pub fn coerce_value(&self, property: &'static FerroProperty) {
692 self.values.coerce_value(self, property);
693 }
694
695 pub fn get_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) -> T {
699 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
700 registered.invoke_getter(self)
701 }
702
703 pub fn set_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>, value: T) {
705 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
706 registered.invoke_setter(self, BindingValue::new(value));
707 }
708
709 pub fn clear_direct_value<T: PropertyValue>(&self, property: &'static DirectPropertyBase<T>) {
711 let registered = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
712 let unset = registered.get_unset_value_for(self);
713 registered.invoke_setter(self, BindingValue::new(unset));
714 }
715
716 pub(crate) fn set_direct_value_unchecked<T: PropertyValue>(
717 &self,
718 property: &'static DirectPropertyBase<T>,
719 value: BindingValue<T>,
720 ) {
721 let state = value.value_type();
722 let error = value.error().cloned();
723 match state {
724 BindingValueType::UNSET_VALUE | BindingValueType::BINDING_ERROR => {
725 let fallback =
726 if value.has_value() { value } else { value.with_value(property.get_unset_value_for(self)) };
727 property.invoke_setter(self, fallback);
728 }
729 BindingValueType::VALUE
730 | BindingValueType::BINDING_ERROR_WITH_FALLBACK
731 | BindingValueType::DATA_VALIDATION_ERROR
732 | BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK => property.invoke_setter(self, value),
733 _ => {}
735 }
736 if property.get_metadata_for(self).enable_data_validation() == Some(true) {
737 self.on_update_data_validation(property, state, error);
738 }
739 }
740
741 pub fn bind_direct<T: PropertyValue>(
743 &self,
744 property: &'static DirectPropertyBase<T>,
745 source: Rc<dyn IObservable<T>>,
746 ) -> Rc<dyn IDisposable> {
747 let property = self.writable_direct(property);
748 self.values.add_direct_binding(self, property, BindingSource::Typed(source))
749 }
750
751 pub fn bind_direct_value<T: PropertyValue>(
753 &self,
754 property: &'static DirectPropertyBase<T>,
755 source: Rc<dyn IObservable<BindingValue<T>>>,
756 ) -> Rc<dyn IDisposable> {
757 let property = self.writable_direct(property);
758 self.values.add_direct_binding(self, property, BindingSource::Value(source))
759 }
760
761 pub fn bind_direct_untyped<T: PropertyValue>(
763 &self,
764 property: &'static DirectPropertyBase<T>,
765 source: Rc<dyn IObservable<BoxedValue>>,
766 ) -> Rc<dyn IDisposable> {
767 let property = self.writable_direct(property);
768 self.values.add_direct_binding(self, property, BindingSource::Untyped(source))
769 }
770
771 fn writable_direct<T: PropertyValue>(
772 &self,
773 property: &'static DirectPropertyBase<T>,
774 ) -> &'static DirectPropertyBase<T> {
775 let property = FerroPropertyRegistry::instance().get_registered_direct_for(self, property);
776 assert!(!property.is_read_only(), "The property {} is readonly.", property.name());
777 property
778 }
779
780 pub fn set_and_raise<T: PropertyValue>(
783 &self,
784 property: &'static DirectPropertyBase<T>,
785 field: &RefCell<T>,
786 value: T,
787 ) -> bool {
788 if *field.borrow() == value {
789 return false;
790 }
791 let old = field.replace(value.clone());
792 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
793 true
794 }
795
796 pub fn set_and_raise_cell<T: PropertyValue + Copy>(
799 &self,
800 property: &'static DirectPropertyBase<T>,
801 field: &Cell<T>,
802 value: T,
803 ) -> bool {
804 let old = field.get();
805 if old == value {
806 return false;
807 }
808 field.set(value);
809 self.raise_property_changed(property, Some(&old), &value, BindingPriority::LocalValue, true);
810 true
811 }
812
813 pub fn get_value_untyped(&self, property: &'static FerroProperty) -> BoxedValue {
817 property.routes().route_get_value(self)
818 }
819
820 pub fn set_value_untyped(
823 &self,
824 property: &'static FerroProperty,
825 value: &dyn Any,
826 priority: BindingPriority,
827 ) -> Option<Rc<dyn IDisposable>> {
828 property.routes().route_set_value(self, value, priority)
829 }
830
831 pub fn set_current_value_untyped(&self, property: &'static FerroProperty, value: &dyn Any) {
833 property.routes().route_set_current_value(self, value)
834 }
835
836 pub fn clear_value_untyped(&self, property: &'static FerroProperty) {
838 property.routes().route_clear_value(self)
839 }
840
841 pub fn bind_property_untyped(
843 &self,
844 property: &'static FerroProperty,
845 source: Rc<dyn IObservable<BoxedValue>>,
846 priority: BindingPriority,
847 ) -> Rc<dyn IDisposable> {
848 property.routes().route_bind(self, source, priority)
849 }
850
851 pub fn property_changed(
856 &self,
857 handler: impl Fn(&FerroPropertyChangedEventArgs<'_>) + 'static,
858 ) -> Rc<dyn IDisposable> {
859 let token = self.property_changed.add(Rc::new(handler));
860 let weak = self.to_weak();
861 Disposable::create(move || {
862 if let Some(this) = weak.upgrade() {
863 this.property_changed.remove(token);
864 }
865 })
866 }
867
868 pub fn add_property_changed_handler(&self, handler: Rc<dyn Fn(&FerroPropertyChangedEventArgs<'_>)>) -> u64 {
873 self.property_changed.add(handler)
874 }
875
876 pub fn remove_property_changed_handler(&self, token: u64) -> bool {
878 self.property_changed.remove(token)
879 }
880
881 pub fn property_changed_subscriber_count(&self) -> usize {
885 self.property_changed.len()
886 }
887
888 pub fn raise_property_changed<T: PropertyValue>(
890 &self,
891 property: &'static FerroProperty,
892 old_value: Option<&T>,
893 new_value: &T,
894 priority: BindingPriority,
895 is_effective_value: bool,
896 ) {
897 let e = FerroPropertyChangedEventArgs::new(
898 self,
899 property,
900 old_value.map(|v| v as &dyn Any),
901 new_value,
902 priority,
903 is_effective_value,
904 );
905 self.notify_property_changed(&e);
906 }
907
908 pub(crate) fn raise_inherited_property_changed<T: PropertyValue>(
909 &self,
910 property: &'static FerroProperty,
911 old_value: &T,
912 new_value: &T,
913 priority: BindingPriority,
914 ) {
915 let e = FerroPropertyChangedEventArgs::new(
916 self, property, Some(old_value), new_value, priority, true,
917 ).from_inheritance();
918 self.notify_property_changed(&e);
919 }
920
921 fn notify_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
922 crate::perf_count!(PropertyChangesRaised);
923 self.on_property_changed_core(e);
924
925 if e.is_effective_value_change() {
926 crate::perf_count!(PropertyChangesEffective);
927 e.property().notify_changed(e);
928 if !self.property_changed.is_empty() {
929 crate::perf_count!(PropertyChangesWithObjectListeners);
930 for (_, handler) in self.property_changed.snapshot().iter() {
931 handler(e);
932 }
933 }
934 }
935 }
936
937 pub fn raise_direct_property_changed<T: PropertyValue>(
939 &self,
940 property: &'static DirectPropertyBase<T>,
941 old_value: &T,
942 new_value: &T,
943 ) {
944 self.raise_property_changed(property, Some(old_value), new_value, BindingPriority::LocalValue, true);
945 }
946
947 fn validate_priority(priority: BindingPriority) {
948 if priority < BindingPriority::Animation || priority >= BindingPriority::Inherited {
949 panic!("Invalid priority {priority:?}");
950 }
951 }
952}
953
954impl fmt::Debug for FerroObject {
955 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956 f.write_str(self.get_type().name())
957 }
958}
959
960enum ValueMarker {
962 Unset,
963 DoNothing,
964}
965
966#[inline]
970fn value_marker<T: 'static>(value: &T) -> Option<ValueMarker> {
971 let any: &dyn Any = value;
972 let boxed = match any.downcast_ref::<BoxedValue>() {
973 Some(boxed) => boxed,
974 None => any.downcast_ref::<Option<BoxedValue>>()?.as_ref()?,
975 };
976 if boxed.is::<crate::UnsetValueType>() {
977 Some(ValueMarker::Unset)
978 } else if boxed.is::<crate::DoNothingType>() {
979 Some(ValueMarker::DoNothing)
980 } else {
981 None
982 }
983}