ferroui_base/logical_tree/
control_locator.rs1use crate::reactive::{IDisposable, IObservable, IObserver, LightweightObservable, LightweightObservableBase};
2use crate::{Ref, StyledElement, TypeInfo, WeakRef};
3use std::cell::RefCell;
4use std::rc::{Rc, Weak};
5
6pub struct ControlLocator;
8
9impl ControlLocator {
10 pub fn track(
17 relative_to: &StyledElement,
18 ancestor_level: usize,
19 ancestor_type: Option<&'static TypeInfo>,
20 ) -> Rc<dyn IObservable<Option<Ref<StyledElement>>>> {
21 ControlTracker::new(relative_to.to_ref(), ancestor_level, ancestor_type)
22 }
23}
24
25struct ControlTracker {
26 this: Weak<ControlTracker>,
27 base: LightweightObservableBase<Option<Ref<StyledElement>>>,
28 relative_to: WeakRef<StyledElement>,
33 ancestor_level: usize,
34 ancestor_type: Option<&'static TypeInfo>,
35 value: RefCell<Option<WeakRef<StyledElement>>>,
36 subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
37}
38
39impl ControlTracker {
40 fn new(relative_to: Ref<StyledElement>, ancestor_level: usize, ancestor_type: Option<&'static TypeInfo>) -> Rc<Self> {
41 Rc::new_cyclic(|this| Self {
42 this: this.clone(),
43 base: LightweightObservableBase::new(),
44 relative_to: relative_to.downgrade(),
45 ancestor_level,
46 ancestor_type,
47 value: RefCell::new(None),
48 subscriptions: RefCell::new(Vec::new()),
49 })
50 }
51
52 fn attached(&self) {
53 self.update();
54 let value = self.value();
55 self.base.publish_next(value);
56 }
57
58 fn value(&self) -> Option<Ref<StyledElement>> {
59 self.value.borrow().as_ref().and_then(WeakRef::upgrade)
60 }
61
62 fn detached(&self) {
63 *self.value.borrow_mut() = None;
64 self.base.publish_next(None);
65 }
66
67 fn update(&self) {
68 let value = self.relative_to.upgrade().and_then(|relative_to| {
69 relative_to
70 .get_logical_ancestors()
71 .filter(|x| self.ancestor_type.is_none_or(|t| t.is_assignable_from(x.get_type())))
72 .nth(self.ancestor_level)
73 });
74 *self.value.borrow_mut() = value.as_ref().map(Ref::downgrade);
75 }
76}
77
78impl LightweightObservable<Option<Ref<StyledElement>>> for ControlTracker {
79 fn observable_base(&self) -> &LightweightObservableBase<Option<Ref<StyledElement>>> {
80 &self.base
81 }
82
83 fn initialize(&self) {
84 self.update();
85 let Some(relative_to) = self.relative_to.upgrade() else { return };
86 let this = self.this.clone();
87 let attached = relative_to.attached_to_logical_tree(move |_| {
88 if let Some(this) = this.upgrade() {
89 this.attached();
90 }
91 });
92 let this = self.this.clone();
93 let detached = relative_to.detached_from_logical_tree(move |_| {
94 if let Some(this) = this.upgrade() {
95 this.detached();
96 }
97 });
98 self.subscriptions.borrow_mut().extend([attached, detached]);
99 }
100
101 fn deinitialize(&self) {
102 let subscriptions = std::mem::take(&mut *self.subscriptions.borrow_mut());
103 for subscription in subscriptions {
104 subscription.dispose();
105 }
106 *self.value.borrow_mut() = None;
107 }
108
109 fn subscribed(&self, observer: &Rc<dyn IObserver<Option<Ref<StyledElement>>>>, _first: bool) {
110 let value = self.value();
111 observer.on_next(value);
112 }
113}
114
115impl IObservable<Option<Ref<StyledElement>>> for ControlTracker {
116 fn subscribe(&self, observer: Rc<dyn IObserver<Option<Ref<StyledElement>>>>) -> Rc<dyn IDisposable> {
117 let this = self.this.upgrade().expect("the tracker is alive while it is subscribed to");
118 LightweightObservableBase::subscribe(&this, observer)
119 }
120}