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: () = ();