Skip to main content

ferroui_base/logical_tree/
control_locator.rs

1use crate::reactive::{IDisposable, IObservable, IObserver, LightweightObservable, LightweightObservableBase};
2use crate::{Ref, StyledElement, TypeInfo, WeakRef};
3use std::cell::RefCell;
4use std::rc::{Rc, Weak};
5
6/// Tracks an ancestor of an element in the logical tree.
7pub struct ControlLocator;
8
9impl ControlLocator {
10    /// An observable of the ancestor at `ancestor_level` (counting only
11    /// ancestors of `ancestor_type`, when given) of `relative_to` in the
12    /// logical tree. It publishes the ancestor again when `relative_to` is
13    /// attached to a logical tree, and `None` when it is detached.
14    // Deviation (DEVIATIONS.md, Logical tree): upstream takes an `int` level; a negative level
15    // cannot be passed here.
16    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    /// The element and the ancestor found are held weakly (DEVIATIONS.md,
29    /// Logical tree): the tracker belongs to a binding of the element, and
30    /// the ancestor owns the element. Held strongly, as the managed original
31    /// holds both, the element keeps itself and its ancestor alive.
32    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}