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