i_slint_compiler/passes/
lower_timers.rs1use crate::diagnostics::BuildDiagnostics;
7use crate::expression_tree::{BuiltinFunction, Callable, Expression, NamedReference};
8use crate::langtype::ElementType;
9use crate::object_tree::*;
10use smol_str::SmolStr;
11use std::rc::Rc;
12
13pub fn lower_timers(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
14 visit_all_expressions(component, |e, _| {
15 e.visit_recursive_mut(&mut |e| match e {
16 Expression::FunctionCall { function, arguments, .. } => match function {
17 Callable::Builtin(BuiltinFunction::StartTimer | BuiltinFunction::StopTimer) => {
18 if let [Expression::ElementReference(timer)] = arguments.as_slice() {
19 *e = Expression::SelfAssignment {
20 lhs: Box::new(Expression::PropertyReference(NamedReference::new(
21 &timer.upgrade().unwrap(),
22 SmolStr::new_static("running"),
23 ))),
24 rhs: Box::new(Expression::BoolLiteral(matches!(
25 function,
26 Callable::Builtin(BuiltinFunction::StartTimer)
27 ))),
28 op: '=',
29 node: None,
30 }
31 }
32 }
33 _ => {}
34 },
35 _ => {}
36 });
37 });
38
39 recurse_elem_including_sub_components_no_borrow(
40 component,
41 &None,
42 &mut |elem, parent_element: &Option<ElementRc>| {
43 let is_timer = matches!(&elem.borrow().base_type, ElementType::Builtin(base_type) if base_type.name == "Timer");
44 if is_timer {
45 lower_timer(elem, parent_element.as_ref(), diag);
46 }
47 Some(elem.clone())
48 },
49 )
50}
51
52fn lower_timer(
53 timer_element: &ElementRc,
54 parent_element: Option<&ElementRc>,
55 diag: &mut BuildDiagnostics,
56) {
57 let parent_component = timer_element.borrow().enclosing_component.upgrade().unwrap();
58 let Some(parent_element) = parent_element else {
59 diag.push_error("A component cannot inherit from Timer".into(), &*timer_element.borrow());
60 return;
61 };
62
63 if Rc::ptr_eq(&parent_component.root_element, timer_element) {
64 diag.push_error(
65 "Timer cannot be directly repeated or conditional".into(),
66 &*timer_element.borrow(),
67 );
68 return;
69 }
70
71 if !timer_element.borrow().is_binding_set("interval", true) {
72 diag.push_error(
73 "Timer must have a binding set for its 'interval' property".into(),
74 &*timer_element.borrow(),
75 );
76 return;
77 }
78
79 let mut parent_element_borrowed = parent_element.borrow_mut();
81 let index = parent_element_borrowed
82 .children
83 .iter()
84 .position(|child| Rc::ptr_eq(child, timer_element))
85 .expect("Timer must be a child of its parent");
86 let removed = parent_element_borrowed.children.remove(index);
87 parent_component.optimized_elements.borrow_mut().push(removed);
88 drop(parent_element_borrowed);
89 if let Some(parent_cip) = &mut *parent_component.child_insertion_point.borrow_mut() {
90 if Rc::ptr_eq(&parent_cip.parent, parent_element) && parent_cip.insertion_index > index {
91 parent_cip.insertion_index -= 1;
92 }
93 }
94
95 let running = NamedReference::new(timer_element, SmolStr::new_static("running"));
96 running.mark_as_set();
97
98 parent_component.timers.borrow_mut().push(Timer {
99 interval: NamedReference::new(timer_element, SmolStr::new_static("interval")),
100 running,
101 triggered: NamedReference::new(timer_element, SmolStr::new_static("triggered")),
102 element: Rc::downgrade(timer_element),
103 });
104 let update_timers = Expression::FunctionCall {
105 function: BuiltinFunction::UpdateTimers.into(),
106 arguments: vec![],
107 source_location: None,
108 };
109 let change_callbacks = &mut timer_element.borrow_mut().change_callbacks;
110 change_callbacks.entry("running".into()).or_default().borrow_mut().push(update_timers.clone());
111 change_callbacks.entry("interval".into()).or_default().borrow_mut().push(update_timers);
112}