Skip to main content

ferroui_base/controls/
name_scope_locator.rs

1use super::NameScopeRef;
2use crate::reactive::{Disposable, IDisposable, IObservable, IObserver};
3use crate::utilities::SynchronousCompletionAsyncResult;
4use crate::{FerroObject, Ref};
5use std::cell::RefCell;
6use std::rc::Rc;
7
8type FindResult = Option<Ref<FerroObject>>;
9
10/// Tracks a named element in a name scope.
11pub struct NameScopeLocator;
12
13impl NameScopeLocator {
14    /// Tracks a named control relative to another control: the observable
15    /// produces the element once it is registered (or `None` once the scope
16    /// completes without it) and never completes.
17    pub fn track(scope: &NameScopeRef, name: &str) -> Rc<dyn IObservable<FindResult>> {
18        let task = scope.find_async(name);
19        let state = if task.is_completed() { State::Value(task.get_result()) } else { State::Pending(task) };
20        Rc::new(NeverEndingSynchronousCompletionAsyncResultObservable { state: RefCell::new(state) })
21    }
22}
23
24enum State {
25    Value(FindResult),
26    Pending(SynchronousCompletionAsyncResult<FindResult>),
27}
28
29// The binding system treats completion of a source as the end of the binding,
30// so this observable never completes.
31struct NeverEndingSynchronousCompletionAsyncResultObservable {
32    state: RefCell<State>,
33}
34
35impl IObservable<FindResult> for NeverEndingSynchronousCompletionAsyncResultObservable {
36    fn subscribe(&self, observer: Rc<dyn IObserver<FindResult>>) -> Rc<dyn IDisposable> {
37        let pending = {
38            let mut state = self.state.borrow_mut();
39            if let State::Pending(task) = &*state {
40                if task.is_completed() {
41                    let result = task.get_result();
42                    *state = State::Value(result);
43                }
44            }
45            match &*state {
46                State::Pending(task) => Ok(task.clone()),
47                State::Value(value) => Err(value.clone()),
48            }
49        };
50
51        match pending {
52            Ok(task) => {
53                let result = task.clone();
54                task.on_completed(move || observer.on_next(result.get_result()));
55            }
56            Err(value) => observer.on_next(value),
57        }
58
59        Disposable::empty()
60    }
61}