i_slint_core/
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// cSpell: ignore singleshot
5
6/*!
7    Support for timers.
8
9    Timers are just a bunch of callbacks sorted by expiry date.
10*/
11
12#![warn(missing_docs)]
13use alloc::boxed::Box;
14use alloc::vec::Vec;
15use core::{
16    cell::{Cell, RefCell},
17    num::NonZeroUsize,
18};
19
20use crate::animations::Instant;
21
22type TimerCallback = Box<dyn FnMut()>;
23type SingleShotTimerCallback = Box<dyn FnOnce()>;
24
25/// The TimerMode specifies what should happen after the timer fired.
26///
27/// Used by the [`Timer::start()`] function.
28#[derive(Copy, Clone)]
29#[repr(u8)]
30#[non_exhaustive]
31pub enum TimerMode {
32    /// A SingleShot timer is fired only once.
33    SingleShot,
34    /// A Repeated timer is fired repeatedly until it is stopped or dropped.
35    Repeated,
36}
37
38/// Timer is a handle to the timer system that triggers a callback after a specified
39/// period of time.
40///
41/// Use [`Timer::start()`] to create a timer that repeatedly triggers a callback, or
42/// [`Timer::single_shot`] to trigger a callback only once.
43///
44/// The timer will automatically stop when dropped. You must keep the Timer object
45/// around for as long as you want the timer to keep firing.
46///
47/// Timers can only be used in the thread that runs the Slint event loop. They don't
48/// fire if used in another thread.
49///
50/// ## Example
51/// ```rust,no_run
52/// # i_slint_backend_testing::init_no_event_loop();
53/// use slint::{Timer, TimerMode};
54/// let timer = Timer::default();
55/// timer.start(TimerMode::Repeated, std::time::Duration::from_millis(200), move || {
56///    println!("This will be printed every 200ms.");
57/// });
58/// // ... more initialization ...
59/// slint::run_event_loop();
60/// ```
61#[derive(Default)]
62pub struct Timer {
63    id: Cell<Option<NonZeroUsize>>,
64    /// The timer cannot be moved between treads
65    _phantom: core::marker::PhantomData<*mut ()>,
66}
67
68impl Timer {
69    /// Starts the timer with the given mode and interval, in order for the callback to called when the
70    /// timer fires. If the timer has been started previously and not fired yet, then it will be restarted.
71    ///
72    /// Arguments:
73    /// * `mode`: The timer mode to apply, i.e. whether to repeatedly fire the timer or just once.
74    /// * `interval`: The duration from now until when the timer should fire the first time, and subsequently
75    ///    for repeated [`Repeated`](TimerMode::Repeated) timers.
76    /// * `callback`: The function to call when the time has been reached or exceeded.
77    pub fn start(
78        &self,
79        mode: TimerMode,
80        interval: core::time::Duration,
81        callback: impl FnMut() + 'static,
82    ) {
83        CURRENT_TIMERS.with(|timers| {
84            let mut timers = timers.borrow_mut();
85            let id = timers.start_or_restart_timer(
86                self.id(),
87                mode,
88                interval,
89                CallbackVariant::MultiFire(Box::new(callback)),
90            );
91            self.set_id(Some(id));
92        })
93    }
94
95    /// Starts the timer with the duration and the callback to called when the
96    /// timer fires. It is fired only once and then deleted.
97    ///
98    /// Arguments:
99    /// * `duration`: The duration from now until when the timer should fire.
100    /// * `callback`: The function to call when the time has been reached or exceeded.
101    ///
102    /// ## Example
103    /// ```rust
104    /// # i_slint_backend_testing::init_no_event_loop();
105    /// use slint::Timer;
106    /// Timer::single_shot(std::time::Duration::from_millis(200), move || {
107    ///    println!("This will be printed after 200ms.");
108    /// });
109    /// ```
110    pub fn single_shot(duration: core::time::Duration, callback: impl FnOnce() + 'static) {
111        CURRENT_TIMERS.with(|timers| {
112            let mut timers = timers.borrow_mut();
113            timers.start_or_restart_timer(
114                None,
115                TimerMode::SingleShot,
116                duration,
117                CallbackVariant::SingleShot(Box::new(callback)),
118            );
119        })
120    }
121
122    /// Stops the previously started timer. Does nothing if the timer has never been started.
123    pub fn stop(&self) {
124        if let Some(id) = self.id() {
125            CURRENT_TIMERS.with(|timers| {
126                timers.borrow_mut().deactivate_timer(id);
127            });
128        }
129    }
130
131    /// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
132    /// with a duration and callback, then the time when the callback will be next invoked
133    /// is re-calculated to be in the specified duration relative to when this function is called.
134    ///
135    /// Does nothing if the timer was never started.
136    pub fn restart(&self) {
137        if let Some(id) = self.id() {
138            CURRENT_TIMERS.with(|timers| {
139                timers.borrow_mut().deactivate_timer(id);
140                timers.borrow_mut().activate_timer(id);
141            });
142        }
143    }
144
145    /// Returns true if the timer is running; false otherwise.
146    pub fn running(&self) -> bool {
147        self.id()
148            .map(|timer_id| CURRENT_TIMERS.with(|timers| timers.borrow().timers[timer_id].running))
149            .unwrap_or(false)
150    }
151
152    /// Change the duration of timer. If the timer was is running (see [`Self::running()`]),
153    /// then the time when the callback will be next invoked is re-calculated to be in the
154    /// specified duration relative to when this function is called.
155    ///
156    /// Arguments:
157    /// * `interval`: The duration from now until when the timer should fire. And the period of that timer
158    ///    for [`Repeated`](TimerMode::Repeated) timers.
159    pub fn set_interval(&self, interval: core::time::Duration) {
160        if let Some(id) = self.id() {
161            CURRENT_TIMERS.with(|timers| {
162                timers.borrow_mut().set_interval(id, interval);
163            });
164        }
165    }
166
167    /// Returns the interval of the timer. If the timer was never started, the returned duration is 0ms.
168    pub fn interval(&self) -> core::time::Duration {
169        self.id()
170            .map(|timer_id| CURRENT_TIMERS.with(|timers| timers.borrow().timers[timer_id].duration))
171            .unwrap_or_default()
172    }
173
174    fn id(&self) -> Option<usize> {
175        self.id.get().map(|v| usize::from(v) - 1)
176    }
177
178    fn set_id(&self, id: Option<usize>) {
179        self.id.set(id.and_then(|v| NonZeroUsize::new(v + 1)));
180    }
181}
182
183impl Drop for Timer {
184    fn drop(&mut self) {
185        if let Some(id) = self.id() {
186            let _ = CURRENT_TIMERS.try_with(|timers| {
187                #[cfg(target_os = "android")]
188                if timers.borrow().timers.is_empty() {
189                    // There seems to be a bug in android thread_local where try_with recreates the already thread local.
190                    // But we are called from the drop of another thread local, just ignore the drop then
191                    return;
192                }
193                let callback = timers.borrow_mut().remove_timer(id);
194                // drop the callback without having CURRENT_TIMERS borrowed
195                drop(callback);
196            });
197        }
198    }
199}
200
201enum CallbackVariant {
202    Empty,
203    MultiFire(TimerCallback),
204    SingleShot(SingleShotTimerCallback),
205}
206
207struct TimerData {
208    duration: core::time::Duration,
209    mode: TimerMode,
210    running: bool,
211    /// Set to true when it is removed when the callback is still running
212    removed: bool,
213    /// true if it is in the cached the active_timers list in the maybe_activate_timers stack
214    being_activated: bool,
215
216    callback: CallbackVariant,
217}
218
219#[derive(Clone, Copy)]
220struct ActiveTimer {
221    id: usize,
222    timeout: Instant,
223}
224
225/// TimerList provides the interface to the event loop for activating times and
226/// determining the nearest timeout.
227#[derive(Default)]
228pub struct TimerList {
229    timers: slab::Slab<TimerData>,
230    active_timers: Vec<ActiveTimer>,
231    /// If a callback is currently running, this is the id of the currently running callback
232    callback_active: Option<usize>,
233}
234
235impl TimerList {
236    /// Returns the timeout of the timer that should fire the soonest, or None if there
237    /// is no timer active.
238    pub fn next_timeout() -> Option<Instant> {
239        CURRENT_TIMERS.with(|timers| {
240            timers
241                .borrow()
242                .active_timers
243                .first()
244                .map(|first_active_timer| first_active_timer.timeout)
245        })
246    }
247
248    /// Activates any expired timers by calling their callback function. Returns true if any timers were
249    /// activated; false otherwise.
250    pub fn maybe_activate_timers(now: Instant) -> bool {
251        // Shortcut: Is there any timer worth activating?
252        if TimerList::next_timeout().map(|timeout| now < timeout).unwrap_or(false) {
253            return false;
254        }
255
256        CURRENT_TIMERS.with(|timers| {
257            assert!(timers.borrow().callback_active.is_none(), "Recursion in timer code");
258
259            // Re-register all timers that expired but are repeating, as well as all that haven't expired yet. This is
260            // done in one shot to ensure a consistent state by the time the callbacks are invoked.
261            let expired_timers = {
262                let mut timers = timers.borrow_mut();
263
264                // Empty active_timers and rebuild it, to preserve insertion order across expired and not expired timers.
265                let mut active_timers = core::mem::take(&mut timers.active_timers);
266
267                let expired_vs_remaining_timers_partition_point =
268                    active_timers.partition_point(|active_timer| active_timer.timeout <= now);
269
270                let (expired_timers, timers_not_activated_this_time) =
271                    active_timers.split_at(expired_vs_remaining_timers_partition_point);
272
273                for expired_timer in expired_timers {
274                    let timer = &mut timers.timers[expired_timer.id];
275                    assert!(!timer.being_activated);
276                    timer.being_activated = true;
277
278                    if matches!(timers.timers[expired_timer.id].mode, TimerMode::Repeated) {
279                        timers.activate_timer(expired_timer.id);
280                    } else {
281                        timers.timers[expired_timer.id].running = false;
282                    }
283                }
284
285                for future_timer in timers_not_activated_this_time.iter() {
286                    timers.register_active_timer(*future_timer);
287                }
288
289                // turn `expired_timers` slice into a truncated vec.
290                active_timers.truncate(expired_vs_remaining_timers_partition_point);
291                active_timers
292            };
293
294            let any_activated = !expired_timers.is_empty();
295
296            for active_timer in expired_timers.into_iter() {
297                let mut callback = {
298                    let mut timers = timers.borrow_mut();
299
300                    timers.callback_active = Some(active_timer.id);
301
302                    // have to release the borrow on `timers` before invoking the callback,
303                    // so here we temporarily move the callback out of its permanent place
304                    core::mem::replace(
305                        &mut timers.timers[active_timer.id].callback,
306                        CallbackVariant::Empty,
307                    )
308                };
309
310                match callback {
311                    CallbackVariant::Empty => (),
312                    CallbackVariant::MultiFire(ref mut cb) => cb(),
313                    CallbackVariant::SingleShot(cb) => {
314                        cb();
315                        timers.borrow_mut().callback_active = None;
316                        timers.borrow_mut().timers.remove(active_timer.id);
317                        continue;
318                    }
319                };
320
321                let mut timers = timers.borrow_mut();
322
323                let callback_register = &mut timers.timers[active_timer.id].callback;
324
325                // only emplace back the callback if its permanent store is still Empty:
326                // if not, it means the invoked callback has restarted its own timer with a new callback
327                if matches!(callback_register, CallbackVariant::Empty) {
328                    *callback_register = callback;
329                }
330
331                timers.callback_active = None;
332                let t = &mut timers.timers[active_timer.id];
333                if t.removed {
334                    timers.timers.remove(active_timer.id);
335                } else {
336                    t.being_activated = false;
337                }
338            }
339            any_activated
340        })
341    }
342
343    fn start_or_restart_timer(
344        &mut self,
345        id: Option<usize>,
346        mode: TimerMode,
347        duration: core::time::Duration,
348        callback: CallbackVariant,
349    ) -> usize {
350        let mut timer_data = TimerData {
351            duration,
352            mode,
353            running: false,
354            removed: false,
355            callback,
356            being_activated: false,
357        };
358        let inactive_timer_id = if let Some(id) = id {
359            self.deactivate_timer(id);
360            timer_data.being_activated = self.timers[id].being_activated;
361            self.timers[id] = timer_data;
362            id
363        } else {
364            self.timers.insert(timer_data)
365        };
366        self.activate_timer(inactive_timer_id);
367        inactive_timer_id
368    }
369
370    fn deactivate_timer(&mut self, id: usize) {
371        let mut i = 0;
372        while i < self.active_timers.len() {
373            if self.active_timers[i].id == id {
374                self.active_timers.remove(i);
375                self.timers[id].running = false;
376                debug_assert!(!self.active_timers.iter().any(|t| t.id == id));
377                break;
378            } else {
379                i += 1;
380            }
381        }
382    }
383
384    fn activate_timer(&mut self, id: usize) {
385        self.register_active_timer(ActiveTimer {
386            id,
387            timeout: Instant::now() + self.timers[id].duration,
388        });
389    }
390
391    fn register_active_timer(&mut self, new_active_timer: ActiveTimer) {
392        debug_assert!(!self.active_timers.iter().any(|t| t.id == new_active_timer.id));
393        let insertion_index = self
394            .active_timers
395            .partition_point(|existing_timer| existing_timer.timeout < new_active_timer.timeout);
396        self.active_timers.insert(insertion_index, new_active_timer);
397        self.timers[new_active_timer.id].running = true;
398    }
399
400    fn remove_timer(&mut self, id: usize) -> CallbackVariant {
401        self.deactivate_timer(id);
402        let t = &mut self.timers[id];
403        if t.being_activated {
404            t.removed = true;
405            CallbackVariant::Empty
406        } else {
407            self.timers.remove(id).callback
408        }
409    }
410
411    fn set_interval(&mut self, id: usize, duration: core::time::Duration) {
412        let timer = &self.timers[id];
413        if timer.running {
414            self.deactivate_timer(id);
415            self.timers[id].duration = duration;
416            self.activate_timer(id);
417        } else {
418            self.timers[id].duration = duration;
419        }
420    }
421}
422
423crate::thread_local!(static CURRENT_TIMERS : RefCell<TimerList> = RefCell::default());
424
425#[cfg(feature = "ffi")]
426pub(crate) mod ffi {
427    #![allow(unsafe_code)]
428
429    use super::*;
430    #[allow(non_camel_case_types)]
431    type c_void = ();
432
433    struct WrapFn {
434        callback: extern "C" fn(*mut c_void),
435        user_data: *mut c_void,
436        drop_user_data: Option<extern "C" fn(*mut c_void)>,
437    }
438
439    impl Drop for WrapFn {
440        fn drop(&mut self) {
441            if let Some(x) = self.drop_user_data {
442                x(self.user_data)
443            }
444        }
445    }
446
447    impl WrapFn {
448        fn call(&self) {
449            (self.callback)(self.user_data)
450        }
451    }
452
453    /// Start a timer with the given mode, duration in millisecond and callback. A timer id may be provided (first argument).
454    /// A value of -1 for the timer id means a new timer is to be allocated.
455    /// The (new) timer id is returned.
456    /// The timer MUST be destroyed with slint_timer_destroy.
457    #[unsafe(no_mangle)]
458    pub extern "C" fn slint_timer_start(
459        id: usize,
460        mode: TimerMode,
461        duration: u64,
462        callback: extern "C" fn(*mut c_void),
463        user_data: *mut c_void,
464        drop_user_data: Option<extern "C" fn(*mut c_void)>,
465    ) -> usize {
466        let wrap = WrapFn { callback, user_data, drop_user_data };
467        let timer = Timer::default();
468        if id != 0 {
469            timer.id.set(NonZeroUsize::new(id));
470        }
471        if duration > i64::MAX as u64 {
472            // negative duration? stop the timer
473            timer.stop();
474        } else {
475            timer.start(mode, core::time::Duration::from_millis(duration), move || wrap.call());
476        }
477        timer.id.take().map(|x| usize::from(x)).unwrap_or(0)
478    }
479
480    /// Execute a callback with a delay in millisecond
481    #[unsafe(no_mangle)]
482    pub extern "C" fn slint_timer_singleshot(
483        delay: u64,
484        callback: extern "C" fn(*mut c_void),
485        user_data: *mut c_void,
486        drop_user_data: Option<extern "C" fn(*mut c_void)>,
487    ) {
488        let wrap = WrapFn { callback, user_data, drop_user_data };
489        Timer::single_shot(core::time::Duration::from_millis(delay), move || wrap.call());
490    }
491
492    /// Stop a timer and free its raw data
493    #[unsafe(no_mangle)]
494    pub extern "C" fn slint_timer_destroy(id: usize) {
495        if id == 0 {
496            return;
497        }
498        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
499        drop(timer);
500    }
501
502    /// Stop a timer
503    #[unsafe(no_mangle)]
504    pub extern "C" fn slint_timer_stop(id: usize) {
505        if id == 0 {
506            return;
507        }
508        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
509        timer.stop();
510        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
511    }
512
513    /// Restart a repeated timer
514    #[unsafe(no_mangle)]
515    pub extern "C" fn slint_timer_restart(id: usize) {
516        if id == 0 {
517            return;
518        }
519        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
520        timer.restart();
521        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
522    }
523
524    /// Returns true if the timer is running; false otherwise.
525    #[unsafe(no_mangle)]
526    pub extern "C" fn slint_timer_running(id: usize) -> bool {
527        if id == 0 {
528            return false;
529        }
530        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
531        let running = timer.running();
532        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
533        running
534    }
535
536    /// Returns the interval in milliseconds. 0 when the timer was never started.
537    #[unsafe(no_mangle)]
538    pub extern "C" fn slint_timer_interval(id: usize) -> u64 {
539        if id == 0 {
540            return 0;
541        }
542        let timer = Timer { id: Cell::new(NonZeroUsize::new(id)), _phantom: Default::default() };
543        let val = timer.interval().as_millis() as u64;
544        timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
545        val
546    }
547}
548
549/**
550```rust
551i_slint_backend_testing::init_no_event_loop();
552use slint::{Timer, TimerMode};
553use std::{rc::Rc, cell::RefCell, time::Duration};
554#[derive(Default)]
555struct SharedState {
556    timer_200: Timer,
557    timer_200_called: usize,
558    timer_500: Timer,
559    timer_500_called: usize,
560    timer_once: Timer,
561    timer_once_called: usize,
562}
563let state = Rc::new(RefCell::new(SharedState::default()));
564// Note: state will be leaked because of circular dependencies: don't do that in production
565let state_ = state.clone();
566state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
567    state_.borrow_mut().timer_200_called += 1;
568});
569let state_ = state.clone();
570state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
571    state_.borrow_mut().timer_once_called += 1;
572    state_.borrow().timer_once.stop();
573});
574let state_ = state.clone();
575state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
576    state_.borrow_mut().timer_500_called += 1;
577});
578slint::platform::update_timers_and_animations();
579i_slint_core::tests::slint_mock_elapsed_time(100);
580assert_eq!(state.borrow().timer_200_called, 0);
581assert_eq!(state.borrow().timer_once_called, 0);
582assert_eq!(state.borrow().timer_500_called, 0);
583i_slint_core::tests::slint_mock_elapsed_time(100);
584assert_eq!(state.borrow().timer_200_called, 1);
585assert_eq!(state.borrow().timer_once_called, 0);
586assert_eq!(state.borrow().timer_500_called, 0);
587i_slint_core::tests::slint_mock_elapsed_time(100);
588assert_eq!(state.borrow().timer_200_called, 1);
589assert_eq!(state.borrow().timer_once_called, 1);
590assert_eq!(state.borrow().timer_500_called, 0);
591i_slint_core::tests::slint_mock_elapsed_time(200); // total: 500
592assert_eq!(state.borrow().timer_200_called, 2);
593assert_eq!(state.borrow().timer_once_called, 1);
594assert_eq!(state.borrow().timer_500_called, 1);
595for _ in 0..10 {
596    i_slint_core::tests::slint_mock_elapsed_time(100);
597}
598// total: 1500
599assert_eq!(state.borrow().timer_200_called, 7);
600assert_eq!(state.borrow().timer_once_called, 1);
601assert_eq!(state.borrow().timer_500_called, 3);
602state.borrow().timer_once.restart();
603state.borrow().timer_200.restart();
604state.borrow().timer_500.stop();
605slint::platform::update_timers_and_animations();
606i_slint_core::tests::slint_mock_elapsed_time(100);
607assert_eq!(state.borrow().timer_200_called, 7);
608assert_eq!(state.borrow().timer_once_called, 1);
609assert_eq!(state.borrow().timer_500_called, 3);
610slint::platform::update_timers_and_animations();
611i_slint_core::tests::slint_mock_elapsed_time(100);
612assert_eq!(state.borrow().timer_200_called, 8);
613assert_eq!(state.borrow().timer_once_called, 1);
614assert_eq!(state.borrow().timer_500_called, 3);
615slint::platform::update_timers_and_animations();
616i_slint_core::tests::slint_mock_elapsed_time(100);
617assert_eq!(state.borrow().timer_200_called, 8);
618assert_eq!(state.borrow().timer_once_called, 2);
619assert_eq!(state.borrow().timer_500_called, 3);
620slint::platform::update_timers_and_animations();
621i_slint_core::tests::slint_mock_elapsed_time(1000);
622slint::platform::update_timers_and_animations();
623slint::platform::update_timers_and_animations();
624// Despite 1000ms have passed, the 200 timer is only called once because we didn't call update_timers_and_animations in between
625assert_eq!(state.borrow().timer_200_called, 9);
626assert_eq!(state.borrow().timer_once_called, 2);
627assert_eq!(state.borrow().timer_500_called, 3);
628let state_ = state.clone();
629state.borrow().timer_200.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
630    state_.borrow_mut().timer_200_called += 1;
631});
632for _ in 0..5 {
633    i_slint_core::tests::slint_mock_elapsed_time(75);
634}
635assert_eq!(state.borrow().timer_200_called, 10);
636assert_eq!(state.borrow().timer_once_called, 2);
637assert_eq!(state.borrow().timer_500_called, 3);
638state.borrow().timer_200.restart();
639for _ in 0..5 {
640    i_slint_core::tests::slint_mock_elapsed_time(75);
641}
642assert_eq!(state.borrow().timer_200_called, 11);
643assert_eq!(state.borrow().timer_once_called, 2);
644assert_eq!(state.borrow().timer_500_called, 3);
645
646// Test re-starting from a callback
647let state_ = state.clone();
648state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
649    state_.borrow_mut().timer_500_called += 1;
650    let state__ = state_.clone();
651    state_.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
652        state__.borrow_mut().timer_500_called += 1000;
653    });
654    let state__ = state_.clone();
655    state_.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
656        state__.borrow_mut().timer_200_called += 1000;
657    });
658});
659for _ in 0..20 {
660    i_slint_core::tests::slint_mock_elapsed_time(100);
661}
662assert_eq!(state.borrow().timer_200_called, 7011);
663assert_eq!(state.borrow().timer_once_called, 2);
664assert_eq!(state.borrow().timer_500_called, 3004);
665
666// Test set interval
667let state_ = state.clone();
668state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
669    state_.borrow_mut().timer_200_called += 1;
670});
671let state_ = state.clone();
672state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
673    state_.borrow_mut().timer_once_called += 1;
674    state_.borrow().timer_once.stop();
675});
676let state_ = state.clone();
677state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
678    state_.borrow_mut().timer_500_called += 1;
679});
680
681let state_ = state.clone();
682slint::platform::update_timers_and_animations();
683for _ in 0..5 {
684    i_slint_core::tests::slint_mock_elapsed_time(100);
685}
686slint::platform::update_timers_and_animations();
687assert_eq!(state.borrow().timer_200_called, 7013);
688assert_eq!(state.borrow().timer_once_called, 3);
689assert_eq!(state.borrow().timer_500_called, 3005);
690
691for _ in 0..20 {
692    state.borrow().timer_200.set_interval(Duration::from_millis(200 * 2));
693    state.borrow().timer_once.set_interval(Duration::from_millis(300 * 2));
694    state.borrow().timer_500.set_interval(Duration::from_millis(500 * 2));
695
696    assert_eq!(state.borrow().timer_200_called, 7013);
697    assert_eq!(state.borrow().timer_once_called, 3);
698    assert_eq!(state.borrow().timer_500_called, 3005);
699
700    i_slint_core::tests::slint_mock_elapsed_time(100);
701}
702
703slint::platform::update_timers_and_animations();
704for _ in 0..9 {
705    i_slint_core::tests::slint_mock_elapsed_time(100);
706}
707slint::platform::update_timers_and_animations();
708assert_eq!(state.borrow().timer_200_called, 7015);
709assert_eq!(state.borrow().timer_once_called, 3);
710assert_eq!(state.borrow().timer_500_called, 3006);
711
712state.borrow().timer_200.stop();
713state.borrow().timer_500.stop();
714
715state.borrow_mut().timer_once.restart();
716for _ in 0..4 {
717    i_slint_core::tests::slint_mock_elapsed_time(100);
718}
719assert_eq!(state.borrow().timer_once_called, 3);
720for _ in 0..4 {
721    i_slint_core::tests::slint_mock_elapsed_time(100);
722}
723assert_eq!(state.borrow().timer_once_called, 4);
724
725state.borrow_mut().timer_once.stop();
726i_slint_core::tests::slint_mock_elapsed_time(1000);
727
728assert_eq!(state.borrow().timer_200_called, 7015);
729assert_eq!(state.borrow().timer_once_called, 4);
730assert_eq!(state.borrow().timer_500_called, 3006);
731```
732 */
733#[cfg(doctest)]
734const _TIMER_TESTS: () = ();
735
736/**
737 * Test that deleting an active timer from a timer event works.
738```rust
739// There is a 200 ms timer that increase variable1
740// after 500ms, that timer is destroyed by a single shot timer,
741// and a new new timer  increase variable2
742i_slint_backend_testing::init_no_event_loop();
743use slint::{Timer, TimerMode};
744use std::{rc::Rc, cell::RefCell, time::Duration};
745#[derive(Default)]
746struct SharedState {
747    repeated_timer: Timer,
748    variable1: usize,
749    variable2: usize,
750}
751let state = Rc::new(RefCell::new(SharedState::default()));
752// Note: state will be leaked because of circular dependencies: don't do that in production
753let state_ = state.clone();
754state.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
755    state_.borrow_mut().variable1 += 1;
756});
757let state_ = state.clone();
758Timer::single_shot(Duration::from_millis(500), move || {
759    state_.borrow_mut().repeated_timer = Default::default();
760    let state = state_.clone();
761    state_.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
762        state.borrow_mut().variable2 += 1;
763    })
764} );
765i_slint_core::tests::slint_mock_elapsed_time(10);
766assert_eq!(state.borrow().variable1, 0);
767assert_eq!(state.borrow().variable2, 0);
768i_slint_core::tests::slint_mock_elapsed_time(200);
769assert_eq!(state.borrow().variable1, 1);
770assert_eq!(state.borrow().variable2, 0);
771i_slint_core::tests::slint_mock_elapsed_time(200);
772assert_eq!(state.borrow().variable1, 2);
773assert_eq!(state.borrow().variable2, 0);
774i_slint_core::tests::slint_mock_elapsed_time(100);
775// More than 500ms have elapsed, the single shot timer should have been activated, but that has no effect on variable 1 and 2
776// This should just restart the timer so that the next change should happen 200ms from now
777assert_eq!(state.borrow().variable1, 2);
778assert_eq!(state.borrow().variable2, 0);
779i_slint_core::tests::slint_mock_elapsed_time(110);
780assert_eq!(state.borrow().variable1, 2);
781assert_eq!(state.borrow().variable2, 0);
782i_slint_core::tests::slint_mock_elapsed_time(100);
783assert_eq!(state.borrow().variable1, 2);
784assert_eq!(state.borrow().variable2, 1);
785i_slint_core::tests::slint_mock_elapsed_time(100);
786assert_eq!(state.borrow().variable1, 2);
787assert_eq!(state.borrow().variable2, 1);
788i_slint_core::tests::slint_mock_elapsed_time(100);
789assert_eq!(state.borrow().variable1, 2);
790assert_eq!(state.borrow().variable2, 2);
791```
792 */
793#[cfg(doctest)]
794const _BUG3019: () = ();
795
796/**
797 * Test that starting a singleshot timer works
798```rust
799// There is a 200 ms singleshot timer that increase variable1
800i_slint_backend_testing::init_no_event_loop();
801use slint::{Timer, TimerMode};
802use std::{rc::Rc, cell::RefCell, time::Duration};
803#[derive(Default)]
804struct SharedState {
805    variable1: usize,
806}
807let state = Rc::new(RefCell::new(SharedState::default()));
808let state_ = state.clone();
809let timer = Timer::default();
810
811timer.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
812    state_.borrow_mut().variable1 += 1;
813});
814
815// Singleshot timer set up and run...
816assert!(timer.running());
817i_slint_core::tests::slint_mock_elapsed_time(10);
818assert!(timer.running());
819assert_eq!(state.borrow().variable1, 0);
820i_slint_core::tests::slint_mock_elapsed_time(200);
821assert_eq!(state.borrow().variable1, 1);
822assert!(!timer.running());
823i_slint_core::tests::slint_mock_elapsed_time(200);
824assert_eq!(state.borrow().variable1, 1); // It's singleshot, it only triggers once!
825assert!(!timer.running());
826
827// Restart a previously set up singleshot timer
828timer.restart();
829assert!(timer.running());
830assert_eq!(state.borrow().variable1, 1);
831i_slint_core::tests::slint_mock_elapsed_time(200);
832assert_eq!(state.borrow().variable1, 2);
833assert!(!timer.running());
834i_slint_core::tests::slint_mock_elapsed_time(200);
835assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
836assert!(!timer.running());
837
838// Stop a non-running singleshot timer
839timer.stop();
840assert!(!timer.running());
841assert_eq!(state.borrow().variable1, 2);
842i_slint_core::tests::slint_mock_elapsed_time(200);
843assert_eq!(state.borrow().variable1, 2);
844assert!(!timer.running());
845i_slint_core::tests::slint_mock_elapsed_time(200);
846assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
847assert!(!timer.running());
848
849// Stop a running singleshot timer
850timer.restart();
851assert!(timer.running());
852assert_eq!(state.borrow().variable1, 2);
853i_slint_core::tests::slint_mock_elapsed_time(10);
854timer.stop();
855assert!(!timer.running());
856i_slint_core::tests::slint_mock_elapsed_time(200);
857assert_eq!(state.borrow().variable1, 2);
858assert!(!timer.running());
859i_slint_core::tests::slint_mock_elapsed_time(200);
860assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
861assert!(!timer.running());
862
863// set_interval on a non-running singleshot timer
864timer.set_interval(Duration::from_millis(300));
865assert!(!timer.running());
866i_slint_core::tests::slint_mock_elapsed_time(1000);
867assert_eq!(state.borrow().variable1, 2);
868assert!(!timer.running());
869timer.restart();
870assert!(timer.running());
871i_slint_core::tests::slint_mock_elapsed_time(200);
872assert_eq!(state.borrow().variable1, 2);
873assert!(timer.running());
874i_slint_core::tests::slint_mock_elapsed_time(200);
875assert_eq!(state.borrow().variable1, 3);
876assert!(!timer.running());
877i_slint_core::tests::slint_mock_elapsed_time(300);
878assert_eq!(state.borrow().variable1, 3); // It's singleshot, it only triggers once!
879assert!(!timer.running());
880
881// set_interval on a running singleshot timer
882timer.restart();
883assert!(timer.running());
884assert_eq!(state.borrow().variable1, 3);
885i_slint_core::tests::slint_mock_elapsed_time(290);
886timer.set_interval(Duration::from_millis(400));
887assert!(timer.running());
888i_slint_core::tests::slint_mock_elapsed_time(200);
889assert_eq!(state.borrow().variable1, 3);
890assert!(timer.running());
891i_slint_core::tests::slint_mock_elapsed_time(250);
892assert_eq!(state.borrow().variable1, 4);
893assert!(!timer.running());
894i_slint_core::tests::slint_mock_elapsed_time(400);
895assert_eq!(state.borrow().variable1, 4); // It's singleshot, it only triggers once!
896assert!(!timer.running());
897```
898 */
899#[cfg(doctest)]
900const _SINGLESHOT_START: () = ();
901
902/**
903 * Test that it's possible to start a new timer from within Drop of a timer's closure.
904 * This may happen when a timer's closure is dropped, that closure holds the last reference
905 * to a component, that component is destroyed, and the accesskit code schedules a reload_tree
906 * via a single shot.
907```rust
908i_slint_backend_testing::init_no_event_loop();
909use slint::{Timer, TimerMode};
910use std::{rc::Rc, cell::Cell, time::Duration};
911#[derive(Default)]
912struct CapturedInClosure {
913    last_fired: Option<Rc<Cell<bool>>>,
914}
915impl Drop for CapturedInClosure {
916    fn drop(&mut self) {
917        if let Some(last_fired) = self.last_fired.as_ref().cloned() {
918            Timer::single_shot(Duration::from_millis(100), move || last_fired.set(true));
919        }
920    }
921}
922
923let last_fired = Rc::new(Cell::new(false));
924
925let mut cap_in_clos = CapturedInClosure::default();
926
927let timer_to_stop = Timer::default();
928timer_to_stop.start(TimerMode::Repeated, Duration::from_millis(100), {
929    let last_fired = last_fired.clone();
930    move || {
931    cap_in_clos.last_fired = Some(last_fired.clone());
932}});
933
934assert_eq!(last_fired.get(), false);
935i_slint_core::tests::slint_mock_elapsed_time(110);
936assert_eq!(last_fired.get(), false);
937drop(timer_to_stop);
938
939i_slint_core::tests::slint_mock_elapsed_time(110);
940assert_eq!(last_fired.get(), true);
941```
942 */
943#[cfg(doctest)]
944const _TIMER_CLOSURE_DROP_STARTS_NEW_TIMER: () = ();
945
946/**
947 * Test that it's possible to set a timer's interval from within the callback.
948```rust
949i_slint_backend_testing::init_no_event_loop();
950use slint::{Timer, TimerMode};
951use std::{rc::Rc, cell::RefCell, time::Duration};
952#[derive(Default)]
953struct SharedState {
954    // Note: state will be leaked because of circular dependencies: don't do that in production
955    timer: Timer,
956    variable1: usize,
957}
958let state = Rc::new(RefCell::new(SharedState::default()));
959let state_ = state.clone();
960state.borrow().timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
961    state_.borrow_mut().variable1 += 1;
962    let variable1 = state_.borrow().variable1;
963    if variable1 == 2 {
964        state_.borrow().timer.set_interval(Duration::from_millis(500));
965    } else if variable1 == 3 {
966        state_.borrow().timer.set_interval(Duration::from_millis(100));
967    }
968});
969
970assert!(state.borrow().timer.running());
971i_slint_core::tests::slint_mock_elapsed_time(10);
972assert!(state.borrow().timer.running());
973assert_eq!(state.borrow().variable1, 0);
974i_slint_core::tests::slint_mock_elapsed_time(200);
975assert_eq!(state.borrow().variable1, 1); // fired
976assert!(state.borrow().timer.running());
977i_slint_core::tests::slint_mock_elapsed_time(180);
978assert_eq!(state.borrow().variable1, 1);
979assert!(state.borrow().timer.running());
980i_slint_core::tests::slint_mock_elapsed_time(30);
981assert_eq!(state.borrow().variable1, 2); // fired
982assert!(state.borrow().timer.running());
983// now the timer interval should be 500
984i_slint_core::tests::slint_mock_elapsed_time(480);
985assert_eq!(state.borrow().variable1, 2);
986assert!(state.borrow().timer.running());
987i_slint_core::tests::slint_mock_elapsed_time(30);
988assert_eq!(state.borrow().variable1, 3); // fired
989assert!(state.borrow().timer.running());
990// now the timer interval should be 100
991i_slint_core::tests::slint_mock_elapsed_time(100);
992assert_eq!(state.borrow().variable1, 4); // fired
993assert!(state.borrow().timer.running());
994i_slint_core::tests::slint_mock_elapsed_time(100);
995assert_eq!(state.borrow().variable1, 5); // fired
996assert!(state.borrow().timer.running());
997```
998 */
999#[cfg(doctest)]
1000const _BUG6141_SET_INTERVAL_FROM_CALLBACK: () = ();
1001
1002/**
1003 * Test that a timer can't be activated twice.
1004```rust
1005i_slint_backend_testing::init_no_event_loop();
1006use slint::{Timer, TimerMode};
1007use std::{rc::Rc, cell::Cell, time::Duration};
1008
1009let later_timer_expiration_count = Rc::new(Cell::new(0));
1010
1011let sooner_timer = Timer::default();
1012let later_timer = Rc::new(Timer::default());
1013later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1014    let later_timer_expiration_count = later_timer_expiration_count.clone();
1015    move || {
1016        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1017    }
1018});
1019
1020sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1021    let later_timer = later_timer.clone();
1022    let later_timer_expiration_count = later_timer_expiration_count.clone();
1023    move || {
1024    later_timer.start(TimerMode::SingleShot, Duration::from_millis(600), {
1025        let later_timer_expiration_count = later_timer_expiration_count.clone();
1026        move || {
1027            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1028        }
1029    });
1030}});
1031
1032assert_eq!(later_timer_expiration_count.get(), 0);
1033i_slint_core::tests::slint_mock_elapsed_time(110);
1034assert_eq!(later_timer_expiration_count.get(), 0);
1035i_slint_core::tests::slint_mock_elapsed_time(400);
1036assert_eq!(later_timer_expiration_count.get(), 0);
1037i_slint_core::tests::slint_mock_elapsed_time(800);
1038assert_eq!(later_timer_expiration_count.get(), 1);
1039```
1040 */
1041#[cfg(doctest)]
1042const _DOUBLY_REGISTER_ACTIVE_TIMER: () = ();
1043
1044/**
1045 * Test that a timer can't be activated twice.
1046```rust
1047i_slint_backend_testing::init_no_event_loop();
1048use slint::{Timer, TimerMode};
1049use std::{rc::Rc, cell::Cell, time::Duration};
1050
1051let later_timer_expiration_count = Rc::new(Cell::new(0));
1052
1053let sooner_timer = Timer::default();
1054let later_timer = Rc::new(Timer::default());
1055later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1056    let later_timer_expiration_count = later_timer_expiration_count.clone();
1057    move || {
1058        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1059    }
1060});
1061
1062sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1063    let later_timer = later_timer.clone();
1064    let later_timer_expiration_count = later_timer_expiration_count.clone();
1065    move || {
1066    later_timer.start(TimerMode::Repeated, Duration::from_millis(110), {
1067        let later_timer_expiration_count = later_timer_expiration_count.clone();
1068        move || {
1069            later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1070        }
1071    });
1072}});
1073
1074assert_eq!(later_timer_expiration_count.get(), 0);
1075i_slint_core::tests::slint_mock_elapsed_time(120);
1076assert_eq!(later_timer_expiration_count.get(), 1);
1077```
1078 */
1079#[cfg(doctest)]
1080const _DOUBLY_REGISTER_ACTIVE_TIMER_2: () = ();
1081
1082/**
1083 * Test that a timer that's being activated can be restarted and dropped in one go.
1084```rust
1085i_slint_backend_testing::init_no_event_loop();
1086use slint::{Timer, TimerMode};
1087use std::{cell::RefCell, rc::Rc, time::Duration};
1088
1089let destructive_timer = Rc::new(RefCell::new(Some(Timer::default())));
1090
1091destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), {
1092    let destructive_timer = destructive_timer.clone();
1093    move || {
1094        // start() used to reset the `being_activated` flag...
1095        destructive_timer.borrow().as_ref().unwrap().start(TimerMode::Repeated, Duration::from_millis(110), || {});
1096        // ... which would make this drop remove the timer from the timer list altogether and continued processing
1097        // of the timer would panic as the id isn't valid anymore.
1098        drop(destructive_timer.take());
1099    }
1100});
1101
1102drop(destructive_timer);
1103i_slint_core::tests::slint_mock_elapsed_time(120);
1104```
1105 */
1106#[cfg(doctest)]
1107const _RESTART_TIMER_BEING_ACTIVATED: () = ();
1108
1109/**
1110 * Test that a future timer can be stopped from the activation callback of an earlier timer.
1111```rust
1112i_slint_backend_testing::init_no_event_loop();
1113use slint::{Timer, TimerMode};
1114use std::{rc::Rc, cell::Cell, time::Duration};
1115
1116let later_timer_expiration_count = Rc::new(Cell::new(0));
1117
1118let sooner_timer = Timer::default();
1119let later_timer = Rc::new(Timer::default());
1120later_timer.start(TimerMode::SingleShot, Duration::from_millis(500), {
1121    let later_timer_expiration_count = later_timer_expiration_count.clone();
1122    move || {
1123        later_timer_expiration_count.set(later_timer_expiration_count.get() + 1);
1124    }
1125});
1126
1127sooner_timer.start(TimerMode::SingleShot, Duration::from_millis(100), {
1128    let later_timer = later_timer.clone();
1129    let later_timer_expiration_count = later_timer_expiration_count.clone();
1130    move || {
1131        later_timer.stop();
1132    }
1133});
1134
1135assert_eq!(later_timer_expiration_count.get(), 0);
1136assert!(later_timer.running());
1137i_slint_core::tests::slint_mock_elapsed_time(110);
1138assert_eq!(later_timer_expiration_count.get(), 0);
1139assert!(!later_timer.running());
1140i_slint_core::tests::slint_mock_elapsed_time(800);
1141assert_eq!(later_timer_expiration_count.get(), 0);
1142assert!(!later_timer.running());
1143i_slint_core::tests::slint_mock_elapsed_time(800);
1144i_slint_core::tests::slint_mock_elapsed_time(800);
1145assert_eq!(later_timer_expiration_count.get(), 0);
1146```
1147 */
1148#[cfg(doctest)]
1149const _STOP_FUTURE_TIMER_DURING_ACTIVATION_OF_EARLIER: () = ();