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