1use crate::data::core::{UntypedObservableBindingExpression, ValueTypes};
5use crate::data::{BindingBase, BindingExpressionBase, BindingPriority, BindingValue};
6use crate::reactive::{Disposable, IDisposable, IObservable, IObserver, ObservableExt};
7use crate::{
8 AttachedProperty, BoxedValue, DirectProperty, DirectPropertyBase, FerroObject, FerroProperty,
9 FerroPropertyChangedEventArgs, ObjectType, PropertyValue, StyledProperty, WeakRef,
10};
11use std::cell::{Cell, RefCell};
12use std::rc::{Rc, Weak};
13
14pub fn to_binding<T: PropertyValue>(source: Rc<dyn IObservable<T>>) -> Rc<dyn BindingBase> {
19 Rc::new(BindingAdaptor { source: source.select(|x| ValueTypes::normalize(Rc::new(x))) })
20}
21
22struct BindingAdaptor {
25 source: Rc<dyn IObservable<Option<BoxedValue>>>,
26}
27
28impl BindingBase for BindingAdaptor {
29 fn create_instance(
30 &self,
31 _target: &FerroObject,
32 _target_property: Option<&'static FerroProperty>,
33 _anchor: Option<&crate::Ref<FerroObject>>,
34 ) -> Rc<dyn BindingExpressionBase> {
35 let expression: Rc<dyn BindingExpressionBase> =
36 UntypedObservableBindingExpression::new(self.source.clone(), BindingPriority::LocalValue);
37 expression
38 }
39}
40
41pub trait TypedProperty: 'static {
47 type Value: PropertyValue;
49
50 fn untyped(&'static self) -> &'static FerroProperty;
52
53 #[doc(hidden)]
54 fn typed_get_value(&'static self, target: &FerroObject) -> Self::Value;
55
56 #[doc(hidden)]
57 fn typed_get_base_value(&'static self, target: &FerroObject) -> Option<Self::Value>;
58
59 #[doc(hidden)]
60 fn typed_bind(
61 &'static self,
62 target: &FerroObject,
63 source: Rc<dyn IObservable<Self::Value>>,
64 priority: BindingPriority,
65 ) -> Rc<dyn IDisposable>;
66
67 #[doc(hidden)]
68 fn typed_bind_value(
69 &'static self,
70 target: &FerroObject,
71 source: Rc<dyn IObservable<BindingValue<Self::Value>>>,
72 priority: BindingPriority,
73 ) -> Rc<dyn IDisposable>;
74}
75
76impl<T: PropertyValue> TypedProperty for StyledProperty<T> {
77 type Value = T;
78
79 fn untyped(&'static self) -> &'static FerroProperty {
80 self
81 }
82
83 fn typed_get_value(&'static self, target: &FerroObject) -> T {
84 target.get_value(self)
85 }
86
87 fn typed_get_base_value(&'static self, target: &FerroObject) -> Option<T> {
88 target.get_base_value(self)
89 }
90
91 fn typed_bind(
92 &'static self,
93 target: &FerroObject,
94 source: Rc<dyn IObservable<T>>,
95 priority: BindingPriority,
96 ) -> Rc<dyn IDisposable> {
97 target.bind(self, source, priority)
98 }
99
100 fn typed_bind_value(
101 &'static self,
102 target: &FerroObject,
103 source: Rc<dyn IObservable<BindingValue<T>>>,
104 priority: BindingPriority,
105 ) -> Rc<dyn IDisposable> {
106 target.bind_value(self, source, priority)
107 }
108}
109
110impl<T: PropertyValue> TypedProperty for AttachedProperty<T> {
111 type Value = T;
112
113 fn untyped(&'static self) -> &'static FerroProperty {
114 self
115 }
116
117 fn typed_get_value(&'static self, target: &FerroObject) -> T {
118 target.get_value(self)
119 }
120
121 fn typed_get_base_value(&'static self, target: &FerroObject) -> Option<T> {
122 target.get_base_value(self)
123 }
124
125 fn typed_bind(
126 &'static self,
127 target: &FerroObject,
128 source: Rc<dyn IObservable<T>>,
129 priority: BindingPriority,
130 ) -> Rc<dyn IDisposable> {
131 target.bind(self, source, priority)
132 }
133
134 fn typed_bind_value(
135 &'static self,
136 target: &FerroObject,
137 source: Rc<dyn IObservable<BindingValue<T>>>,
138 priority: BindingPriority,
139 ) -> Rc<dyn IDisposable> {
140 target.bind_value(self, source, priority)
141 }
142}
143
144impl<T: PropertyValue> TypedProperty for DirectPropertyBase<T> {
145 type Value = T;
146
147 fn untyped(&'static self) -> &'static FerroProperty {
148 self
149 }
150
151 fn typed_get_value(&'static self, target: &FerroObject) -> T {
152 target.get_direct_value(self)
153 }
154
155 fn typed_get_base_value(&'static self, target: &FerroObject) -> Option<T> {
156 Some(target.get_direct_value(self))
157 }
158
159 fn typed_bind(
160 &'static self,
161 target: &FerroObject,
162 source: Rc<dyn IObservable<T>>,
163 _priority: BindingPriority,
164 ) -> Rc<dyn IDisposable> {
165 target.bind_direct(self, source)
166 }
167
168 fn typed_bind_value(
169 &'static self,
170 target: &FerroObject,
171 source: Rc<dyn IObservable<BindingValue<T>>>,
172 _priority: BindingPriority,
173 ) -> Rc<dyn IDisposable> {
174 target.bind_direct_value(self, source)
175 }
176}
177
178impl<TOwner: ObjectType, T: PropertyValue> TypedProperty for DirectProperty<TOwner, T> {
179 type Value = T;
180
181 fn untyped(&'static self) -> &'static FerroProperty {
182 self
183 }
184
185 fn typed_get_value(&'static self, target: &FerroObject) -> T {
186 target.get_direct_value(self)
187 }
188
189 fn typed_get_base_value(&'static self, target: &FerroObject) -> Option<T> {
190 Some(target.get_direct_value(self))
191 }
192
193 fn typed_bind(
194 &'static self,
195 target: &FerroObject,
196 source: Rc<dyn IObservable<T>>,
197 _priority: BindingPriority,
198 ) -> Rc<dyn IDisposable> {
199 target.bind_direct(self, source)
200 }
201
202 fn typed_bind_value(
203 &'static self,
204 target: &FerroObject,
205 source: Rc<dyn IObservable<BindingValue<T>>>,
206 _priority: BindingPriority,
207 ) -> Rc<dyn IDisposable> {
208 target.bind_direct_value(self, source)
209 }
210}
211
212pub trait FerroObjectExtensions {
214 fn get_observable<P: TypedProperty>(&self, property: &'static P) -> Rc<dyn IObservable<P::Value>>;
220
221 fn get_observable_with<P: TypedProperty, TResult: PropertyValue>(
224 &self,
225 property: &'static P,
226 converter: impl Fn(P::Value) -> TResult + 'static,
227 ) -> Rc<dyn IObservable<TResult>>;
228
229 fn get_observable_untyped(&self, property: &'static FerroProperty) -> Rc<dyn IObservable<BoxedValue>>;
231
232 fn get_observable_untyped_with<TResult: PropertyValue>(
235 &self,
236 property: &'static FerroProperty,
237 converter: impl Fn(BoxedValue) -> TResult + 'static,
238 ) -> Rc<dyn IObservable<TResult>>;
239
240 fn get_binding_observable<P: TypedProperty>(
242 &self,
243 property: &'static P,
244 ) -> Rc<dyn IObservable<BindingValue<P::Value>>>;
245
246 fn get_binding_observable_with<P: TypedProperty, TResult: PropertyValue>(
249 &self,
250 property: &'static P,
251 converter: impl Fn(P::Value) -> TResult + 'static,
252 ) -> Rc<dyn IObservable<BindingValue<TResult>>>;
253
254 fn get_binding_observable_untyped(
256 &self,
257 property: &'static FerroProperty,
258 ) -> Rc<dyn IObservable<BindingValue<BoxedValue>>>;
259
260 fn get_binding_observable_untyped_with<TResult: PropertyValue>(
263 &self,
264 property: &'static FerroProperty,
265 converter: impl Fn(BoxedValue) -> TResult + 'static,
266 ) -> Rc<dyn IObservable<BindingValue<TResult>>>;
267
268 fn get_property_changed_observable(&self, property: &'static FerroProperty) -> ObjectPropertyChangedObservable;
272
273 fn bind_typed<P: TypedProperty>(
276 &self,
277 property: &'static P,
278 source: Rc<dyn IObservable<P::Value>>,
279 priority: BindingPriority,
280 ) -> Rc<dyn IDisposable>;
281
282 fn bind_typed_value<P: TypedProperty>(
285 &self,
286 property: &'static P,
287 source: Rc<dyn IObservable<BindingValue<P::Value>>>,
288 priority: BindingPriority,
289 ) -> Rc<dyn IDisposable>;
290
291 fn get_typed_value<P: TypedProperty>(&self, property: &'static P) -> P::Value;
293
294 fn get_typed_base_value<P: TypedProperty>(&self, property: &'static P) -> Option<P::Value>;
297
298 fn get_base_value_untyped(&self, property: &'static FerroProperty) -> BoxedValue;
301}
302
303impl FerroObjectExtensions for FerroObject {
304 fn get_observable<P: TypedProperty>(&self, property: &'static P) -> Rc<dyn IObservable<P::Value>> {
305 PropertyObservable::create(
306 self,
307 property.untyped(),
308 move |o| property.typed_get_value(o),
309 |a, b| a == b,
310 true,
311 )
312 }
313
314 fn get_observable_with<P: TypedProperty, TResult: PropertyValue>(
315 &self,
316 property: &'static P,
317 converter: impl Fn(P::Value) -> TResult + 'static,
318 ) -> Rc<dyn IObservable<TResult>> {
319 PropertyObservable::create(
320 self,
321 property.untyped(),
322 move |o| converter(property.typed_get_value(o)),
323 |a, b| a == b,
324 true,
325 )
326 }
327
328 fn get_observable_untyped(&self, property: &'static FerroProperty) -> Rc<dyn IObservable<BoxedValue>> {
329 PropertyObservable::create(self, property, move |o| o.get_value_untyped(property), |a, b| a == b, true)
330 }
331
332 fn get_observable_untyped_with<TResult: PropertyValue>(
333 &self,
334 property: &'static FerroProperty,
335 converter: impl Fn(BoxedValue) -> TResult + 'static,
336 ) -> Rc<dyn IObservable<TResult>> {
337 PropertyObservable::create(
338 self,
339 property,
340 move |o| converter(o.get_value_untyped(property)),
341 |a, b| a == b,
342 true,
343 )
344 }
345
346 fn get_binding_observable<P: TypedProperty>(
347 &self,
348 property: &'static P,
349 ) -> Rc<dyn IObservable<BindingValue<P::Value>>> {
350 PropertyObservable::create(
351 self,
352 property.untyped(),
353 move |o| BindingValue::new(property.typed_get_value(o)),
354 |a, b| a == b,
355 false,
356 )
357 }
358
359 fn get_binding_observable_with<P: TypedProperty, TResult: PropertyValue>(
360 &self,
361 property: &'static P,
362 converter: impl Fn(P::Value) -> TResult + 'static,
363 ) -> Rc<dyn IObservable<BindingValue<TResult>>> {
364 PropertyObservable::create(
365 self,
366 property.untyped(),
367 move |o| BindingValue::new(converter(property.typed_get_value(o))),
368 |a, b| a == b,
369 false,
370 )
371 }
372
373 fn get_binding_observable_untyped(
374 &self,
375 property: &'static FerroProperty,
376 ) -> Rc<dyn IObservable<BindingValue<BoxedValue>>> {
377 PropertyObservable::create(
378 self,
379 property,
380 move |o| BindingValue::new(o.get_value_untyped(property)),
381 |a, b| a == b,
382 false,
383 )
384 }
385
386 fn get_binding_observable_untyped_with<TResult: PropertyValue>(
387 &self,
388 property: &'static FerroProperty,
389 converter: impl Fn(BoxedValue) -> TResult + 'static,
390 ) -> Rc<dyn IObservable<BindingValue<TResult>>> {
391 PropertyObservable::create(
392 self,
393 property,
394 move |o| BindingValue::new(converter(o.get_value_untyped(property))),
395 |a, b| a == b,
396 false,
397 )
398 }
399
400 fn get_property_changed_observable(&self, property: &'static FerroProperty) -> ObjectPropertyChangedObservable {
401 ObjectPropertyChangedObservable { target: self.to_weak(), property }
402 }
403
404 fn bind_typed<P: TypedProperty>(
405 &self,
406 property: &'static P,
407 source: Rc<dyn IObservable<P::Value>>,
408 priority: BindingPriority,
409 ) -> Rc<dyn IDisposable> {
410 property.typed_bind(self, source, priority)
411 }
412
413 fn bind_typed_value<P: TypedProperty>(
414 &self,
415 property: &'static P,
416 source: Rc<dyn IObservable<BindingValue<P::Value>>>,
417 priority: BindingPriority,
418 ) -> Rc<dyn IDisposable> {
419 property.typed_bind_value(self, source, priority)
420 }
421
422 fn get_typed_value<P: TypedProperty>(&self, property: &'static P) -> P::Value {
423 property.typed_get_value(self)
424 }
425
426 fn get_typed_base_value<P: TypedProperty>(&self, property: &'static P) -> Option<P::Value> {
427 property.typed_get_base_value(self)
428 }
429
430 fn get_base_value_untyped(&self, property: &'static FerroProperty) -> BoxedValue {
431 property.routes().route_get_base_value(self)
432 }
433}
434
435#[derive(Clone)]
442pub struct ObjectPropertyChangedObservable {
443 target: WeakRef<FerroObject>,
444 property: &'static FerroProperty,
445}
446
447impl ObjectPropertyChangedObservable {
448 pub fn subscribe(&self, handler: impl Fn(&FerroPropertyChangedEventArgs<'_>) + 'static) -> Rc<dyn IDisposable> {
451 let property = self.property;
452 match self.target.upgrade() {
453 Some(target) => target.property_changed(move |e| {
454 if e.property() == property {
455 handler(e);
456 }
457 }),
458 None => Disposable::empty(),
459 }
460 }
461
462 pub fn add_class_handler<TTarget: ObjectType>(
465 &self,
466 handler: impl Fn(&TTarget, &FerroPropertyChangedEventArgs<'_>) + 'static,
467 ) -> Rc<dyn IDisposable> {
468 self.subscribe(move |e| {
469 if let Some(target) = e.sender().downcast_ref::<TTarget>() {
470 handler(target, e);
471 }
472 })
473 }
474}
475
476struct PropertyObservable<TResult: Clone + 'static> {
481 this: Weak<PropertyObservable<TResult>>,
482 target: WeakRef<FerroObject>,
483 property: &'static FerroProperty,
484 read: Box<dyn Fn(&FerroObject) -> TResult>,
485 same: fn(&TResult, &TResult) -> bool,
486 clear_value_on_deinitialize: bool,
488 value: RefCell<Option<TResult>>,
489 observers: RefCell<Vec<(u64, Rc<dyn IObserver<TResult>>)>>,
490 next_id: Cell<u64>,
491 subscription: RefCell<Option<Rc<dyn IDisposable>>>,
492}
493
494impl<TResult: Clone + 'static> PropertyObservable<TResult> {
495 fn create(
496 target: &FerroObject,
497 property: &'static FerroProperty,
498 read: impl Fn(&FerroObject) -> TResult + 'static,
499 same: fn(&TResult, &TResult) -> bool,
500 clear_value_on_deinitialize: bool,
501 ) -> Rc<dyn IObservable<TResult>> {
502 Rc::new_cyclic(|this| Self {
503 this: this.clone(),
504 target: target.to_weak(),
505 property,
506 read: Box::new(read),
507 same,
508 clear_value_on_deinitialize,
509 value: RefCell::new(None),
510 observers: RefCell::new(Vec::new()),
511 next_id: Cell::new(0),
512 subscription: RefCell::new(None),
513 })
514 }
515
516 fn initialize(&self) {
517 let Some(target) = self.target.upgrade() else { return };
518 let value = (self.read)(&target);
519 *self.value.borrow_mut() = Some(value);
520 let Some(this) = self.this.upgrade() else { return };
523 let subscription = target.property_changed(move |e| this.property_changed(e));
524 *self.subscription.borrow_mut() = Some(subscription);
525 }
526
527 fn deinitialize(&self) {
528 let subscription = self.subscription.take();
529 if let Some(subscription) = subscription {
530 subscription.dispose();
531 }
532 if self.clear_value_on_deinitialize {
533 *self.value.borrow_mut() = None;
534 }
535 }
536
537 fn property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
538 if e.property() != self.property {
539 return;
540 }
541 let new_value = (self.read)(e.sender());
542 let changed = match &*self.value.borrow() {
543 Some(current) => !(self.same)(&new_value, current),
544 None => true,
545 };
546 if changed {
547 *self.value.borrow_mut() = Some(new_value.clone());
548 let observers: Vec<Rc<dyn IObserver<TResult>>> =
549 self.observers.borrow().iter().map(|(_, o)| o.clone()).collect();
550 match observers.len() {
551 0 => {}
552 1 => observers[0].on_next(new_value),
553 _ => {
554 for observer in observers {
555 observer.on_next(new_value.clone());
556 }
557 }
558 }
559 }
560 }
561
562 fn remove(&self, id: u64) {
563 let now_empty = {
564 let mut observers = self.observers.borrow_mut();
565 let before = observers.len();
566 observers.retain(|(i, _)| *i != id);
567 before != observers.len() && observers.is_empty()
568 };
569 if now_empty {
570 self.deinitialize();
571 }
572 }
573}
574
575impl<TResult: Clone + 'static> IObservable<TResult> for PropertyObservable<TResult> {
576 fn subscribe(&self, observer: Rc<dyn IObserver<TResult>>) -> Rc<dyn IDisposable> {
577 let id = self.next_id.get();
578 self.next_id.set(id + 1);
579 let first = {
580 let mut observers = self.observers.borrow_mut();
581 observers.push((id, observer.clone()));
582 observers.len() == 1
583 };
584 if first {
585 self.initialize();
586 }
587 let current = self.value.borrow().clone();
588 if let Some(value) = current {
589 observer.on_next(value);
590 }
591 match self.this.upgrade() {
592 Some(this) => Disposable::create(move || this.remove(id)),
593 None => Disposable::empty(),
594 }
595 }
596}