Skip to main content

ferroui_base/controls/
child_name_scope.rs

1use super::{INameScope, NameScope, NameScopeError, NameScopeRef};
2use crate::utilities::{SynchronousCompletionAsyncResult, SynchronousCompletionAsyncResultSource};
3use crate::{FerroObject, Ref};
4
5type FindResult = Option<Ref<FerroObject>>;
6
7/// A name scope nested in another one: names that are not found once the
8/// scope is complete are looked up in the parent scope.
9pub struct ChildNameScope {
10    parent_scope: NameScopeRef,
11    inner: NameScope,
12}
13
14impl ChildNameScope {
15    pub fn new(parent_scope: NameScopeRef) -> Self {
16        Self { parent_scope, inner: NameScope::new() }
17    }
18
19    fn do_find_async(&self, name: &str) -> SynchronousCompletionAsyncResult<FindResult> {
20        let src = SynchronousCompletionAsyncResultSource::<FindResult>::new();
21
22        fn parent_search(
23            parent_scope: &NameScopeRef,
24            name: &str,
25            src: SynchronousCompletionAsyncResultSource<FindResult>,
26        ) {
27            let parent_search = parent_scope.find_async(name);
28            if parent_search.is_completed() {
29                src.set_result(parent_search.get_result());
30            } else {
31                let search = parent_search.clone();
32                parent_search.on_completed(move || src.set_result(search.get_result()));
33            }
34        }
35
36        if !self.inner.is_completed() {
37            // Guaranteed to be incomplete at this point.
38            let inner_search = self.inner.find_async(name);
39            let search = inner_search.clone();
40            let src = src.clone();
41            let parent_scope = self.parent_scope.clone();
42            let name = name.to_string();
43            inner_search.on_completed(move || {
44                let value = search.get_result();
45                if value.is_some() {
46                    src.set_result(value);
47                } else {
48                    parent_search(&parent_scope, &name, src);
49                }
50            });
51        } else {
52            parent_search(&self.parent_scope, name, src.clone());
53        }
54
55        src.async_result()
56    }
57}
58
59impl INameScope for ChildNameScope {
60    fn try_register(&self, name: &str, element: Ref<FerroObject>) -> Result<(), NameScopeError> {
61        self.inner.try_register(name, element)
62    }
63
64    fn find_async(&self, name: &str) -> SynchronousCompletionAsyncResult<FindResult> {
65        let found = self.find(name);
66        if found.is_some() {
67            return SynchronousCompletionAsyncResult::new(found);
68        }
69        // Not found and both the current and the parent scope are completed.
70        if self.is_completed() {
71            return SynchronousCompletionAsyncResult::new(None);
72        }
73        self.do_find_async(name)
74    }
75
76    fn find(&self, name: &str) -> FindResult {
77        let found = self.inner.find(name);
78        if found.is_some() {
79            return found;
80        }
81        if self.inner.is_completed() {
82            return self.parent_scope.find(name);
83        }
84        None
85    }
86
87    fn complete(&self) {
88        self.inner.complete()
89    }
90
91    fn is_completed(&self) -> bool {
92        self.inner.is_completed() && self.parent_scope.is_completed()
93    }
94
95    fn attached_to(&self, owner: &Ref<FerroObject>) {
96        self.inner.attached_to(owner)
97    }
98
99    fn detached_from(&self, owner: &Ref<FerroObject>) {
100        self.inner.detached_from(owner)
101    }
102}