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//! Passe that transform the Timer element into a timer in the Component
5
6use crate::diagnostics::BuildDiagnostics;
7use crate::expression_tree::{BuiltinFunction, 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    recurse_elem_including_sub_components_no_borrow(
15        component,
16        &None,
17        &mut |elem, parent_element: &Option<ElementRc>| {
18            let is_timer = matches!(&elem.borrow().base_type, ElementType::Builtin(base_type) if base_type.name == "Timer");
19            if is_timer {
20                lower_timer(elem, parent_element.as_ref(), diag);
21            }
22            Some(elem.clone())
23        },
24    )
25}
26
27fn lower_timer(
28    timer_element: &ElementRc,
29    parent_element: Option<&ElementRc>,
30    diag: &mut BuildDiagnostics,
31) {
32    let parent_component = timer_element.borrow().enclosing_component.upgrade().unwrap();
33    let Some(parent_element) = parent_element else {
34        diag.push_error("A component cannot inherit from Timer".into(), &*timer_element.borrow());
35        return;
36    };
37
38    if Rc::ptr_eq(&parent_component.root_element, timer_element) {
39        diag.push_error(
40            "Timer cannot be directly repeated or conditional".into(),
41            &*timer_element.borrow(),
42        );
43        return;
44    }
45
46    if !timer_element.borrow().is_binding_set("interval", true) {
47        diag.push_error(
48            "Timer must have a binding set for its 'interval' property".into(),
49            &*timer_element.borrow(),
50        );
51        return;
52    }
53
54    // Remove the timer_element from its parent
55    let mut parent_element_borrowed = parent_element.borrow_mut();
56    let index = parent_element_borrowed
57        .children
58        .iter()
59        .position(|child| Rc::ptr_eq(child, timer_element))
60        .expect("Timer must be a child of its parent");
61    let removed = parent_element_borrowed.children.remove(index);
62    parent_component.optimized_elements.borrow_mut().push(removed);
63    drop(parent_element_borrowed);
64    if let Some(parent_cip) = &mut *parent_component.child_insertion_point.borrow_mut() {
65        if Rc::ptr_eq(&parent_cip.parent, parent_element) && parent_cip.insertion_index > index {
66            parent_cip.insertion_index -= 1;
67        }
68    }
69
70    parent_component.timers.borrow_mut().push(Timer {
71        interval: NamedReference::new(timer_element, SmolStr::new_static("interval")),
72        running: NamedReference::new(timer_element, SmolStr::new_static("running")),
73        triggered: NamedReference::new(timer_element, SmolStr::new_static("triggered")),
74    });
75    let update_timers = Expression::FunctionCall {
76        function: BuiltinFunction::UpdateTimers.into(),
77        arguments: vec![],
78        source_location: None,
79    };
80    let change_callbacks = &mut timer_element.borrow_mut().change_callbacks;
81    change_callbacks.entry("running".into()).or_default().borrow_mut().push(update_timers.clone());
82    change_callbacks.entry("interval".into()).or_default().borrow_mut().push(update_timers);
83}