Skip to main content

i_slint_compiler/passes/
lower_timers.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! This pass transforms the Timer element into a timer in the Component
5
6use 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    // Replace all `<timer>.start()` and `<timer>.end()` for the corresponding `timer` to `timer.running = true/false`
15    visit_all_expressions(component, |e, _| {
16        e.visit_recursive_mut(&mut |e| {
17            if let Expression::FunctionCall { function, arguments, .. } = e
18                && let Callable::Builtin(BuiltinFunction::StartTimer | BuiltinFunction::StopTimer) =
19                    function
20                && let [Expression::ElementReference(timer)] = arguments.as_slice()
21            {
22                *e = Expression::SelfAssignment {
23                    lhs: Box::new(Expression::PropertyReference(NamedReference::new(
24                        &timer.upgrade().unwrap(),
25                        SmolStr::new_static("running"),
26                    ))),
27                    rhs: Box::new(Expression::BoolLiteral(matches!(
28                        function,
29                        Callable::Builtin(BuiltinFunction::StartTimer)
30                    ))),
31                    op: '=',
32                    node: None,
33                }
34            }
35        });
36    });
37
38    // Lower the timer it self
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    // Remove the timer_element from its parent
80    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        && Rc::ptr_eq(&parent_cip.parent, parent_element)
91        && parent_cip.insertion_index > index
92    {
93        parent_cip.insertion_index -= 1;
94    }
95
96    let running = NamedReference::new(timer_element, SmolStr::new_static("running"));
97    running.mark_as_set();
98
99    parent_component.timers.borrow_mut().push(Timer {
100        interval: NamedReference::new(timer_element, SmolStr::new_static("interval")),
101        running,
102        triggered: NamedReference::new(timer_element, SmolStr::new_static("triggered")),
103        element: Rc::downgrade(timer_element),
104    });
105    let update_timers = Expression::FunctionCall {
106        function: BuiltinFunction::UpdateTimers.into(),
107        arguments: Vec::new(),
108        source_location: None,
109    };
110    let change_callbacks = &mut timer_element.borrow_mut().change_callbacks;
111    change_callbacks.entry("running".into()).or_default().borrow_mut().push(update_timers.clone());
112    change_callbacks.entry("interval".into()).or_default().borrow_mut().push(update_timers);
113}