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