ferroui_base/controls/
name_scope.rs1use super::{INameScope, NameScopeError, NameScopeRef};
2use crate::utilities::{SynchronousCompletionAsyncResult, SynchronousCompletionAsyncResultSource};
3use crate::{ferro_property, AttachedProperty, FerroObject, FerroProperty, Ref, StyledElement, WeakRef};
4use std::cell::{Cell, RefCell};
5use std::collections::HashMap;
6
7type FindResult = Option<Ref<FerroObject>>;
8
9enum Named {
16 Element(Ref<FerroObject>),
17 Owner(WeakRef<FerroObject>),
18}
19
20impl Named {
21 fn element(&self) -> Option<Ref<FerroObject>> {
22 match self {
23 Named::Element(element) => Some(element.clone()),
24 Named::Owner(owner) => owner.upgrade(),
25 }
26 }
27
28 fn is(&self, element: &Ref<FerroObject>) -> bool {
29 match self {
30 Named::Element(named) => named == element,
31 Named::Owner(owner) => owner.points_to(element),
32 }
33 }
34}
35
36#[derive(Default)]
38pub struct NameScope {
39 is_completed: Cell<bool>,
40 inner: RefCell<HashMap<String, Named>>,
41 owners: RefCell<Vec<WeakRef<FerroObject>>>,
43 pending_searches: RefCell<HashMap<String, SynchronousCompletionAsyncResultSource<FindResult>>>,
44}
45
46crate::ferro_static_type!(NameScope);
47
48crate::ferro_properties! { impl NameScope {
49 ferro_property!(
50 pub fn name_scope_property() -> AttachedProperty<Option<NameScopeRef>> {
52 FerroProperty::register_attached::<NameScope, StyledElement, _>("NameScope", None)
53 }
54 );
55} }
56
57impl NameScope {
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn get_name_scope(styled: &StyledElement) -> Option<NameScopeRef> {
64 styled.get_value(Self::name_scope_property())
65 }
66
67 pub fn set_name_scope(styled: &StyledElement, value: Option<NameScopeRef>) {
72 let owner = styled.to_ref().upcast::<FerroObject>();
73 let previous = Self::get_name_scope(styled);
74 if previous == value {
75 return;
76 }
77 if let Some(previous) = &previous {
78 previous.detached_from(&owner);
79 }
80 styled.set_value(Self::name_scope_property(), value.clone());
81 if let Some(scope) = &value {
82 scope.attached_to(&owner);
83 }
84 }
85
86 fn is_owner(&self, element: &Ref<FerroObject>) -> bool {
87 self.owners.borrow().iter().any(|owner| owner.points_to(element))
88 }
89}
90
91impl INameScope for NameScope {
92 fn try_register(&self, name: &str, element: Ref<FerroObject>) -> Result<(), NameScopeError> {
93 if self.is_completed.get() {
94 return Err(NameScopeError::Completed);
95 }
96
97 let existing = self.inner.borrow().get(name).map(|existing| existing.is(&element));
100 match existing {
101 Some(is_element) => {
102 if !is_element {
103 return Err(NameScopeError::DuplicateName(name.to_string()));
104 }
105 }
106 None => {
107 let named =
108 if self.is_owner(&element) { Named::Owner(element.downgrade()) } else { Named::Element(element.clone()) };
109 self.inner.borrow_mut().insert(name.to_string(), named);
110 let pending = self.pending_searches.borrow_mut().remove(name);
111 if let Some(tcs) = pending {
112 tcs.set_result(Some(element));
113 }
114 }
115 }
116 Ok(())
117 }
118
119 fn find_async(&self, name: &str) -> SynchronousCompletionAsyncResult<FindResult> {
120 let found = self.find(name);
121 if found.is_some() {
122 return SynchronousCompletionAsyncResult::new(found);
123 }
124 if self.is_completed.get() {
125 return SynchronousCompletionAsyncResult::new(None);
126 }
127 self.pending_searches.borrow_mut().entry(name.to_string()).or_default().async_result()
129 }
130
131 fn find(&self, name: &str) -> FindResult {
132 self.inner.borrow().get(name).and_then(Named::element)
133 }
134
135 fn complete(&self) {
136 self.is_completed.set(true);
137 let pending = std::mem::take(&mut *self.pending_searches.borrow_mut());
138 for (_, tcs) in pending {
139 tcs.try_set_result(None);
140 }
141 }
142
143 fn is_completed(&self) -> bool {
144 self.is_completed.get()
145 }
146
147 fn attached_to(&self, owner: &Ref<FerroObject>) {
148 let mut owners = self.owners.borrow_mut();
149 owners.retain(|known| known.upgrade().is_some());
150 if !owners.iter().any(|known| known.points_to(owner)) {
151 owners.push(owner.downgrade());
152 }
153 drop(owners);
154 for named in self.inner.borrow_mut().values_mut() {
155 if matches!(named, Named::Element(element) if element == owner) {
156 *named = Named::Owner(owner.downgrade());
157 }
158 }
159 }
160
161 fn detached_from(&self, owner: &Ref<FerroObject>) {
162 self.owners.borrow_mut().retain(|known| known.upgrade().is_some() && !known.points_to(owner));
163 for named in self.inner.borrow_mut().values_mut() {
164 if matches!(named, Named::Owner(known) if known.points_to(owner)) {
165 *named = Named::Element(owner.clone());
166 }
167 }
168 }
169}