1use std::cell::RefCell;
8use std::rc::Rc;
9
10use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
11use crate::expression_tree::{BuiltinFunction, Callable, Expression};
12use crate::langtype::{ElementType, Function, Type};
13use crate::namedreference::NamedReference;
14use crate::object_tree::*;
15use by_address::ByAddress;
16use smol_str::SmolStr;
17use std::collections::{HashMap, HashSet};
18use strum::IntoEnumIterator;
19
20pub fn call_focus_on_init(component: &Rc<Component>) {
22 if let Some(focus_call_code) =
23 call_set_focus_function(&component.root_element, None, FocusFunctionType::SetFocus)
24 {
25 component.init_code.borrow_mut().focus_setting_code.push(focus_call_code);
26 }
27}
28
29pub fn replace_forward_focus_bindings_with_focus_functions(
32 doc: &Document,
33 diag: &mut BuildDiagnostics,
34) {
35 for component in doc.inner_components.iter() {
36 let mut local_forwards = LocalFocusForwards::collect(component, diag);
38
39 local_forwards.remove_uncallable_forwards();
41
42 local_forwards.gen_focus_functions(&component.root_element);
44
45 recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
47 if elem
48 .borrow()
49 .builtin_type()
50 .is_some_and(|b| matches!(b.name.as_str(), "Window" | "PopupWindow"))
51 {
52 local_forwards.gen_focus_functions(elem);
53 }
54 });
55
56 visit_all_expressions(component, |e, _| {
59 local_forwards.resolve_focus_calls_in_expression(e)
60 });
61 }
62}
63
64struct LocalFocusForwards<'a> {
69 forwards:
70 HashMap<ByAddress<Rc<RefCell<Element>>>, Option<(Rc<RefCell<Element>>, SourceLocation)>>,
71 diag: &'a mut BuildDiagnostics,
72}
73
74impl<'a> LocalFocusForwards<'a> {
75 fn collect(component: &Rc<Component>, diag: &'a mut BuildDiagnostics) -> Self {
76 let mut forwards = HashMap::new();
77
78 recurse_elem_no_borrow(&component.root_element, &(), &mut |elem, _| {
79 let Some(forward_focus_binding) =
80 elem.borrow_mut().bindings.remove("forward-focus").map(RefCell::into_inner)
81 else {
82 return;
83 };
84
85 let Expression::ElementReference(focus_target) =
86 super::ignore_debug_hooks(&forward_focus_binding.expression)
87 else {
88 debug_assert!(diag.has_errors());
90 return;
91 };
92
93 let focus_target = focus_target.upgrade().unwrap();
94 let location = forward_focus_binding.to_source_location();
95
96 if Rc::ptr_eq(elem, &focus_target) {
97 diag.push_error("forward-focus can't refer to itself".into(), &location);
98 return;
99 }
100
101 if forwards
102 .insert(ByAddress(elem.clone()), (focus_target, location.clone()).into())
103 .is_some()
104 {
105 diag.push_error(
106 "only one forward-focus binding can point to an element".into(),
107 &location,
108 );
109 }
110 });
111
112 Self { forwards, diag }
113 }
114
115 fn remove_uncallable_forwards(&mut self) {
116 for target_and_location in self.forwards.values_mut() {
117 let (target, source_location) = target_and_location.as_ref().unwrap();
118 if call_set_focus_function(target, None, FocusFunctionType::SetFocus).is_none() {
119 self.diag.push_error(
120 "Cannot forward focus to unfocusable element".into(),
121 source_location,
122 );
123 *target_and_location = None;
124 }
125 }
126 }
127
128 fn get(&self, element: &ElementRc) -> Option<(ElementRc, SourceLocation)> {
129 self.forwards.get(&ByAddress(element.clone())).cloned().flatten()
130 }
131
132 fn focus_forward_for_element(
133 &mut self,
134 element: &ElementRc,
135 ) -> Option<(ElementRc, SourceLocation)> {
136 let (mut focus_redirect, mut location) = self.get(element)?;
137
138 let mut visited: HashSet<ByAddress<Rc<RefCell<Element>>>> = HashSet::new();
139 loop {
140 if !visited.insert(ByAddress(focus_redirect.clone())) {
141 self.diag.push_error("forward-focus loop".into(), &location);
142 return None;
143 }
144 if let Some((redirect, new_location)) = self.get(&focus_redirect) {
145 focus_redirect = redirect;
146 location = new_location;
147 } else {
148 return Some((focus_redirect, location));
149 }
150 }
151 }
152
153 fn resolve_focus_calls_in_expression(&mut self, expr: &mut Expression) {
154 expr.visit_mut(|e| self.resolve_focus_calls_in_expression(e));
155
156 for focus_function in FocusFunctionType::iter() {
157 if let Expression::FunctionCall {
158 function: Callable::Builtin(builtin_function_type),
159 arguments,
160 source_location,
161 } = expr
162 {
163 if *builtin_function_type != focus_function.as_builtin_function() {
164 continue;
165 }
166 if arguments.len() != 1 {
167 assert!(
168 self.diag.has_errors(),
169 "Invalid argument generated for {} call",
170 focus_function.name()
171 );
172 return;
173 }
174 if let Expression::ElementReference(weak_focus_target) = &arguments[0] {
175 let mut focus_target = weak_focus_target.upgrade().expect(
176 "internal compiler error: weak focus/clear-focus parameter cannot be dangling"
177 );
178
179 if self.forwards.contains_key(&ByAddress(focus_target.clone())) {
180 let Some((next_focus_target, _)) =
181 self.focus_forward_for_element(&focus_target)
182 else {
183 return;
186 };
187 focus_target = next_focus_target;
188 }
189
190 if let Some(set_or_clear_focus_code) = call_set_focus_function(
191 &focus_target,
192 source_location.as_ref(),
193 focus_function,
194 ) {
195 *expr = set_or_clear_focus_code;
196 } else {
197 self.diag.push_error(
198 format!(
199 "{}() can only be called on focusable elements",
200 focus_function.name(),
201 ),
202 source_location,
203 );
204 }
205 }
206 }
207 }
208 }
209
210 fn gen_focus_functions(&mut self, elem: &ElementRc) {
211 if let Some((root_focus_forward, focus_forward_location)) =
212 self.focus_forward_for_element(elem)
213 {
214 for function in FocusFunctionType::iter() {
215 if let Some(set_or_clear_focus_code) = call_set_focus_function(
216 &root_focus_forward,
217 Some(&focus_forward_location),
218 function,
219 ) {
220 elem.borrow_mut().property_declarations.insert(
221 function.name().into(),
222 PropertyDeclaration {
223 property_type: Type::Function(Rc::new(Function {
224 return_type: Type::Void.into(),
225 args: vec![],
226 arg_names: vec![],
227 })),
228 visibility: PropertyVisibility::Public,
229 pure: Some(false),
230 ..Default::default()
231 },
232 );
233 elem.borrow_mut().bindings.insert(
234 function.name().into(),
235 RefCell::new(set_or_clear_focus_code.into()),
236 );
237 }
238 }
239 }
240 }
241}
242
243#[derive(Copy, Clone, strum::EnumIter)]
244enum FocusFunctionType {
245 SetFocus,
246 ClearFocus,
247}
248
249impl FocusFunctionType {
250 fn name(&self) -> &'static str {
251 match self {
252 Self::SetFocus => "focus",
253 Self::ClearFocus => "clear-focus",
254 }
255 }
256
257 fn as_builtin_function(&self) -> BuiltinFunction {
258 match self {
259 Self::SetFocus => BuiltinFunction::SetFocusItem,
260 Self::ClearFocus => BuiltinFunction::ClearFocusItem,
261 }
262 }
263}
264
265fn call_set_focus_function(
266 element: &ElementRc,
267 source_location: Option<&SourceLocation>,
268 function_type: FocusFunctionType,
269) -> Option<Expression> {
270 let function_name = function_type.name();
271 let declares_focus_function = {
272 let mut element = element.clone();
273 loop {
274 if element.borrow().property_declarations.contains_key(function_name) {
275 break true;
276 }
277 let base = element.borrow().base_type.clone();
278 match base {
279 ElementType::Component(compo) => element = compo.root_element.clone(),
280 _ => break false,
281 }
282 }
283 };
284 let builtin_focus_function = element.borrow().builtin_type().is_some_and(|ty| ty.accepts_focus);
285
286 if declares_focus_function {
287 Some(Expression::FunctionCall {
288 function: Callable::Function(NamedReference::new(
289 element,
290 SmolStr::new_static(function_name),
291 )),
292 arguments: vec![],
293 source_location: source_location.cloned(),
294 })
295 } else if builtin_focus_function {
296 let source_location = source_location.cloned();
297 Some(Expression::FunctionCall {
298 function: function_type.as_builtin_function().into(),
299 arguments: vec![Expression::ElementReference(Rc::downgrade(element))],
300 source_location,
301 })
302 } else {
303 None
304 }
305}