1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
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)]
13#[cfg(not(feature = "std"))]
14use alloc::boxed::Box;
15#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17use core::{
18 cell::{Cell, RefCell},
19 num::NonZeroUsize,
20};
21
22use crate::animations::Instant;
23
24type TimerCallback = Box<dyn FnMut()>;
25type SingleShotTimerCallback = Box<dyn FnOnce()>;
26
27/// The TimerMode specifies what should happen after the timer fired.
28///
29/// Used by the [`Timer::start()`] function.
30#[derive(Copy, Clone)]
31#[repr(u8)]
32#[non_exhaustive]
33pub enum TimerMode {
34 /// A SingleShot timer is fired only once.
35 SingleShot,
36 /// A Repeated timer is fired repeatedly until it is stopped or dropped.
37 Repeated,
38}
39
40/// Timer is a handle to the timer system that allows triggering a callback to be called
41/// after a specified period of time.
42///
43/// Use [`Timer::start()`] to create a timer that can repeat at frequent interval, or
44/// [`Timer::single_shot`] if you just want to call a function with a delay and do not
45/// need to be able to stop it.
46///
47/// The timer will automatically stop when dropped. You must keep the Timer object
48/// around for as long as you want the timer to keep firing.
49///
50/// The timer can only be used in the thread that runs the Slint event loop.
51/// They will not fire if used in another thread.
52///
53/// ## Example
54/// ```rust,no_run
55/// # i_slint_backend_testing::init();
56/// use slint::{Timer, TimerMode};
57/// let timer = Timer::default();
58/// timer.start(TimerMode::Repeated, std::time::Duration::from_millis(200), move || {
59/// println!("This will be printed every 200ms.");
60/// });
61/// // ... more initialization ...
62/// slint::run_event_loop();
63/// ```
64#[derive(Default)]
65pub struct Timer {
66 id: Cell<Option<NonZeroUsize>>,
67}
68
69impl Timer {
70 /// Starts the timer with the given mode and interval, in order for the callback to called when the
71 /// timer fires. If the timer has been started previously and not fired yet, then it will be restarted.
72 ///
73 /// Arguments:
74 /// * `mode`: The timer mode to apply, i.e. whether to repeatedly fire the timer or just once.
75 /// * `interval`: The duration from now until when the timer should fire. And the period of that timer
76 /// for [`Repeated`](TimerMode::Repeated) timers.
77 /// * `callback`: The function to call when the time has been reached or exceeded.
78 pub fn start(
79 &self,
80 mode: TimerMode,
81 interval: core::time::Duration,
82 callback: impl FnMut() + 'static,
83 ) {
84 CURRENT_TIMERS.with(|timers| {
85 let mut timers = timers.borrow_mut();
86 let id = timers.start_or_restart_timer(
87 self.id(),
88 mode,
89 interval,
90 CallbackVariant::MultiFire(Box::new(callback)),
91 );
92 self.set_id(Some(id));
93 })
94 }
95
96 /// Starts the timer with the duration, in order for the callback to called when the
97 /// timer fires. It is fired only once and then deleted.
98 ///
99 /// Arguments:
100 /// * `duration`: The duration from now until when the timer should fire.
101 /// * `callback`: The function to call when the time has been reached or exceeded.
102 ///
103 /// ## Example
104 /// ```rust
105 /// # i_slint_backend_testing::init();
106 /// use slint::Timer;
107 /// Timer::single_shot(std::time::Duration::from_millis(200), move || {
108 /// println!("This will be printed after 200ms.");
109 /// });
110 /// ```
111 pub fn single_shot(duration: core::time::Duration, callback: impl FnOnce() + 'static) {
112 CURRENT_TIMERS.with(|timers| {
113 let mut timers = timers.borrow_mut();
114 timers.start_or_restart_timer(
115 None,
116 TimerMode::SingleShot,
117 duration,
118 CallbackVariant::SingleShot(Box::new(callback)),
119 );
120 })
121 }
122
123 /// Stops the previously started timer. Does nothing if the timer has never been started.
124 pub fn stop(&self) {
125 if let Some(id) = self.id() {
126 CURRENT_TIMERS.with(|timers| {
127 timers.borrow_mut().deactivate_timer(id);
128 });
129 }
130 }
131
132 /// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
133 /// with a duration and callback, then the time when the callback will be next invoked
134 /// is re-calculated to be in the specified duration relative to when this function is called.
135 ///
136 /// Does nothing if the timer was never started.
137 pub fn restart(&self) {
138 if let Some(id) = self.id() {
139 CURRENT_TIMERS.with(|timers| {
140 timers.borrow_mut().deactivate_timer(id);
141 timers.borrow_mut().activate_timer(id);
142 });
143 }
144 }
145
146 /// Returns true if the timer is running; false otherwise.
147 pub fn running(&self) -> bool {
148 self.id()
149 .map(|timer_id| CURRENT_TIMERS.with(|timers| timers.borrow().timers[timer_id].running))
150 .unwrap_or(false)
151 }
152
153 /// Change the duration of timer. If the timer was previously started by calling [`Self::start()`]
154 /// with a duration and callback, then the time when the callback will be next invoked
155 /// is re-calculated to be in the specified duration relative to when this function is called.
156 ///
157 /// Does nothing if the timer was never started.
158 ///
159 /// Arguments:
160 /// * `interval`: The duration from now until when the timer should fire. And the period of that timer
161 /// for [`Repeated`](TimerMode::Repeated) timers.
162 pub fn set_interval(&self, interval: core::time::Duration) {
163 if let Some(id) = self.id() {
164 CURRENT_TIMERS.with(|timers| {
165 timers.borrow_mut().set_interval(id, interval);
166 });
167 }
168 }
169
170 fn id(&self) -> Option<usize> {
171 self.id.get().map(|v| usize::from(v) - 1)
172 }
173
174 fn set_id(&self, id: Option<usize>) {
175 self.id.set(id.and_then(|v| NonZeroUsize::new(v + 1)));
176 }
177}
178
179impl Drop for Timer {
180 fn drop(&mut self) {
181 if let Some(id: usize) = self.id() {
182 let _ = CURRENT_TIMERS.try_with(|timers: &RefCell| {
183 timers.borrow_mut().remove_timer(id);
184 });
185 }
186 }
187}
188
189enum CallbackVariant {
190 Empty,
191 MultiFire(TimerCallback),
192 SingleShot(SingleShotTimerCallback),
193}
194
195struct TimerData {
196 duration: core::time::Duration,
197 mode: TimerMode,
198 running: bool,
199 /// Set to true when it is removed when the callback is still running
200 removed: bool,
201 /// true if it is in the cached the active_timers list in the maybe_activate_timers stack
202 being_activated: bool,
203
204 callback: CallbackVariant,
205}
206
207#[derive(Clone, Copy)]
208struct ActiveTimer {
209 id: usize,
210 timeout: Instant,
211}
212
213/// TimerList provides the interface to the event loop for activating times and
214/// determining the nearest timeout.
215#[derive(Default)]
216pub struct TimerList {
217 timers: slab::Slab<TimerData>,
218 active_timers: Vec<ActiveTimer>,
219 /// If a callback is currently running, this is the id of the currently running callback
220 callback_active: Option<usize>,
221}
222
223impl TimerList {
224 /// Returns the timeout of the timer that should fire the soonest, or None if there
225 /// is no timer active.
226 pub fn next_timeout() -> Option<Instant> {
227 CURRENT_TIMERS.with(|timers| {
228 timers
229 .borrow()
230 .active_timers
231 .first()
232 .map(|first_active_timer| first_active_timer.timeout)
233 })
234 }
235
236 /// Activates any expired timers by calling their callback function. Returns true if any timers were
237 /// activated; false otherwise.
238 pub fn maybe_activate_timers(now: Instant) -> bool {
239 // Shortcut: Is there any timer worth activating?
240 if TimerList::next_timeout().map(|timeout| now < timeout).unwrap_or(false) {
241 return false;
242 }
243
244 CURRENT_TIMERS.with(|timers| {
245 assert!(timers.borrow().callback_active.is_none(), "Recursion in timer code");
246
247 let mut any_activated = false;
248
249 // The active timer list is cleared here and not-yet-fired ones are inserted below, in order to allow
250 // timer callbacks to register their own timers.
251 let timers_to_process = core::mem::take(&mut timers.borrow_mut().active_timers);
252 {
253 let mut timers = timers.borrow_mut();
254 for active_timer in &timers_to_process {
255 let timer = &mut timers.timers[active_timer.id];
256 assert!(!timer.being_activated);
257 timer.being_activated = true;
258 }
259 }
260 for active_timer in timers_to_process.into_iter() {
261 if active_timer.timeout <= now {
262 any_activated = true;
263
264 let mut callback = {
265 let mut timers = timers.borrow_mut();
266
267 timers.callback_active = Some(active_timer.id);
268
269 // do it before invoking the callback, in case the callback wants to stop or adjust its own timer
270 if matches!(timers.timers[active_timer.id].mode, TimerMode::Repeated) {
271 timers.activate_timer(active_timer.id);
272 } else {
273 timers.timers[active_timer.id].running = false;
274 }
275
276 // have to release the borrow on `timers` before invoking the callback,
277 // so here we temporarily move the callback out of its permanent place
278 core::mem::replace(
279 &mut timers.timers[active_timer.id].callback,
280 CallbackVariant::Empty,
281 )
282 };
283
284 match callback {
285 CallbackVariant::Empty => (),
286 CallbackVariant::MultiFire(ref mut cb) => cb(),
287 CallbackVariant::SingleShot(cb) => {
288 cb();
289 timers.borrow_mut().callback_active = None;
290 timers.borrow_mut().timers.remove(active_timer.id);
291 continue;
292 }
293 };
294
295 let mut timers = timers.borrow_mut();
296
297 let callback_register = &mut timers.timers[active_timer.id].callback;
298
299 // only emplace back the callback if its permanent store is still Empty:
300 // if not, it means the invoked callback has restarted its own timer with a new callback
301 if matches!(callback_register, CallbackVariant::Empty) {
302 *callback_register = callback;
303 }
304
305 timers.callback_active = None;
306 let t = &mut timers.timers[active_timer.id];
307 if t.removed {
308 timers.timers.remove(active_timer.id);
309 } else {
310 t.being_activated = false;
311 }
312 } else {
313 let mut timers = timers.borrow_mut();
314 let t = &mut timers.timers[active_timer.id];
315 if t.removed {
316 timers.timers.remove(active_timer.id);
317 } else {
318 t.being_activated = false;
319 timers.register_active_timer(active_timer);
320 }
321 }
322 }
323 any_activated
324 })
325 }
326
327 fn start_or_restart_timer(
328 &mut self,
329 id: Option<usize>,
330 mode: TimerMode,
331 duration: core::time::Duration,
332 callback: CallbackVariant,
333 ) -> usize {
334 let timer_data = TimerData {
335 duration,
336 mode,
337 running: false,
338 removed: false,
339 callback,
340 being_activated: false,
341 };
342 let inactive_timer_id = if let Some(id) = id {
343 self.deactivate_timer(id);
344 self.timers[id] = timer_data;
345 id
346 } else {
347 self.timers.insert(timer_data)
348 };
349 self.activate_timer(inactive_timer_id);
350 inactive_timer_id
351 }
352
353 fn deactivate_timer(&mut self, id: usize) {
354 let mut i = 0;
355 while i < self.active_timers.len() {
356 if self.active_timers[i].id == id {
357 self.active_timers.remove(i);
358 self.timers[id].running = false;
359 break;
360 } else {
361 i += 1;
362 }
363 }
364 }
365
366 fn activate_timer(&mut self, id: usize) {
367 self.register_active_timer(ActiveTimer {
368 id,
369 timeout: Instant::now() + self.timers[id].duration,
370 });
371 }
372
373 fn register_active_timer(&mut self, new_active_timer: ActiveTimer) {
374 let insertion_index = lower_bound(&self.active_timers, |existing_timer| {
375 existing_timer.timeout < new_active_timer.timeout
376 });
377
378 self.active_timers.insert(insertion_index, new_active_timer);
379 self.timers[new_active_timer.id].running = true;
380 }
381
382 fn remove_timer(&mut self, id: usize) {
383 self.deactivate_timer(id);
384 let t = &mut self.timers[id];
385 if t.being_activated {
386 t.removed = true;
387 } else {
388 self.timers.remove(id);
389 }
390 }
391
392 fn set_interval(&mut self, id: usize, duration: core::time::Duration) {
393 let timer = &self.timers[id];
394
395 if !matches!(timer.callback, CallbackVariant::MultiFire { .. }) {
396 return;
397 }
398
399 if timer.running {
400 self.deactivate_timer(id);
401 self.timers[id].duration = duration;
402 self.activate_timer(id);
403 } else {
404 self.timers[id].duration = duration;
405 }
406 }
407}
408
409#[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))]
410use crate::unsafe_single_threaded::thread_local;
411
412thread_local!(static CURRENT_TIMERS : RefCell<TimerList> = RefCell::default());
413
414fn lower_bound<T>(vec: &[T], mut less_than: impl FnMut(&T) -> bool) -> usize {
415 let mut left: usize = 0;
416 let mut right: usize = vec.len();
417
418 while left != right {
419 let mid: usize = left + (right - left) / 2;
420 let value: &T = &vec[mid];
421 if less_than(value) {
422 left = mid + 1;
423 } else {
424 right = mid;
425 }
426 }
427
428 left
429}
430
431#[cfg(feature = "ffi")]
432pub(crate) mod ffi {
433 #![allow(unsafe_code)]
434
435 use super::*;
436 #[allow(non_camel_case_types)]
437 type c_void = ();
438
439 struct WrapFn {
440 callback: extern "C" fn(*mut c_void),
441 user_data: *mut c_void,
442 drop_user_data: Option<extern "C" fn(*mut c_void)>,
443 }
444
445 impl Drop for WrapFn {
446 fn drop(&mut self) {
447 if let Some(x) = self.drop_user_data {
448 x(self.user_data)
449 }
450 }
451 }
452
453 impl WrapFn {
454 fn call(&self) {
455 (self.callback)(self.user_data)
456 }
457 }
458
459 /// Start a timer with the given mode, duration in millisecond and callback. A timer id may be provided (first argument).
460 /// A value of -1 for the timer id means a new timer is to be allocated.
461 /// The (new) timer id is returned.
462 /// The timer MUST be destroyed with slint_timer_destroy.
463 #[no_mangle]
464 pub extern "C" fn slint_timer_start(
465 id: usize,
466 mode: TimerMode,
467 duration: u64,
468 callback: extern "C" fn(*mut c_void),
469 user_data: *mut c_void,
470 drop_user_data: Option<extern "C" fn(*mut c_void)>,
471 ) -> usize {
472 let wrap = WrapFn { callback, user_data, drop_user_data };
473 let timer = Timer::default();
474 if id != 0 {
475 timer.id.set(NonZeroUsize::new(id));
476 }
477 timer.start(mode, core::time::Duration::from_millis(duration), move || wrap.call());
478 timer.id.take().map(|x| usize::from(x)).unwrap_or(0)
479 }
480
481 /// Execute a callback with a delay in millisecond
482 #[no_mangle]
483 pub extern "C" fn slint_timer_singleshot(
484 delay: u64,
485 callback: extern "C" fn(*mut c_void),
486 user_data: *mut c_void,
487 drop_user_data: Option<extern "C" fn(*mut c_void)>,
488 ) {
489 let wrap = WrapFn { callback, user_data, drop_user_data };
490 Timer::single_shot(core::time::Duration::from_millis(delay), move || wrap.call());
491 }
492
493 /// Stop a timer and free its raw data
494 #[no_mangle]
495 pub extern "C" fn slint_timer_destroy(id: usize) {
496 if id == 0 {
497 return;
498 }
499 let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
500 drop(timer);
501 }
502
503 /// Stop a timer
504 #[no_mangle]
505 pub extern "C" fn slint_timer_stop(id: usize) {
506 if id == 0 {
507 return;
508 }
509 let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
510 timer.stop();
511 timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
512 }
513
514 /// Restart a repeated timer
515 #[no_mangle]
516 pub extern "C" fn slint_timer_restart(id: usize) {
517 if id == 0 {
518 return;
519 }
520 let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
521 timer.restart();
522 timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
523 }
524
525 /// Returns true if the timer is running; false otherwise.
526 #[no_mangle]
527 pub extern "C" fn slint_timer_running(id: usize) -> bool {
528 if id == 0 {
529 return false;
530 }
531 let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
532 let running = timer.running();
533 timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
534 running
535 }
536}
537
538/**
539```rust
540i_slint_backend_testing::init();
541use slint::{Timer, TimerMode};
542use std::{rc::Rc, cell::RefCell, time::Duration};
543#[derive(Default)]
544struct SharedState {
545 timer_200: Timer,
546 timer_200_called: usize,
547 timer_500: Timer,
548 timer_500_called: usize,
549 timer_once: Timer,
550 timer_once_called: usize,
551}
552let state = Rc::new(RefCell::new(SharedState::default()));
553// Note: state will be leaked because of circular dependencies: don't do that in production
554let state_ = state.clone();
555state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
556 state_.borrow_mut().timer_200_called += 1;
557});
558let state_ = state.clone();
559state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
560 state_.borrow_mut().timer_once_called += 1;
561 state_.borrow().timer_once.stop();
562});
563let state_ = state.clone();
564state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
565 state_.borrow_mut().timer_500_called += 1;
566});
567slint::platform::update_timers_and_animations();
568i_slint_core::tests::slint_mock_elapsed_time(100);
569assert_eq!(state.borrow().timer_200_called, 0);
570assert_eq!(state.borrow().timer_once_called, 0);
571assert_eq!(state.borrow().timer_500_called, 0);
572i_slint_core::tests::slint_mock_elapsed_time(100);
573assert_eq!(state.borrow().timer_200_called, 1);
574assert_eq!(state.borrow().timer_once_called, 0);
575assert_eq!(state.borrow().timer_500_called, 0);
576i_slint_core::tests::slint_mock_elapsed_time(100);
577assert_eq!(state.borrow().timer_200_called, 1);
578assert_eq!(state.borrow().timer_once_called, 1);
579assert_eq!(state.borrow().timer_500_called, 0);
580i_slint_core::tests::slint_mock_elapsed_time(200); // total: 500
581assert_eq!(state.borrow().timer_200_called, 2);
582assert_eq!(state.borrow().timer_once_called, 1);
583assert_eq!(state.borrow().timer_500_called, 1);
584for _ in 0..10 {
585 i_slint_core::tests::slint_mock_elapsed_time(100);
586}
587// total: 1500
588assert_eq!(state.borrow().timer_200_called, 7);
589assert_eq!(state.borrow().timer_once_called, 1);
590assert_eq!(state.borrow().timer_500_called, 3);
591state.borrow().timer_once.restart();
592state.borrow().timer_200.restart();
593state.borrow().timer_500.stop();
594slint::platform::update_timers_and_animations();
595i_slint_core::tests::slint_mock_elapsed_time(100);
596assert_eq!(state.borrow().timer_200_called, 7);
597assert_eq!(state.borrow().timer_once_called, 1);
598assert_eq!(state.borrow().timer_500_called, 3);
599slint::platform::update_timers_and_animations();
600i_slint_core::tests::slint_mock_elapsed_time(100);
601assert_eq!(state.borrow().timer_200_called, 8);
602assert_eq!(state.borrow().timer_once_called, 1);
603assert_eq!(state.borrow().timer_500_called, 3);
604slint::platform::update_timers_and_animations();
605i_slint_core::tests::slint_mock_elapsed_time(100);
606assert_eq!(state.borrow().timer_200_called, 8);
607assert_eq!(state.borrow().timer_once_called, 2);
608assert_eq!(state.borrow().timer_500_called, 3);
609slint::platform::update_timers_and_animations();
610i_slint_core::tests::slint_mock_elapsed_time(1000);
611slint::platform::update_timers_and_animations();
612slint::platform::update_timers_and_animations();
613// Despite 1000ms have passed, the 200 timer is only called once because we didn't call update_timers_and_animations in between
614assert_eq!(state.borrow().timer_200_called, 9);
615assert_eq!(state.borrow().timer_once_called, 2);
616assert_eq!(state.borrow().timer_500_called, 3);
617let state_ = state.clone();
618state.borrow().timer_200.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
619 state_.borrow_mut().timer_200_called += 1;
620});
621for _ in 0..5 {
622 i_slint_core::tests::slint_mock_elapsed_time(75);
623}
624assert_eq!(state.borrow().timer_200_called, 10);
625assert_eq!(state.borrow().timer_once_called, 2);
626assert_eq!(state.borrow().timer_500_called, 3);
627state.borrow().timer_200.restart();
628for _ in 0..5 {
629 i_slint_core::tests::slint_mock_elapsed_time(75);
630}
631assert_eq!(state.borrow().timer_200_called, 11);
632assert_eq!(state.borrow().timer_once_called, 2);
633assert_eq!(state.borrow().timer_500_called, 3);
634
635// Test re-starting from a callback
636let state_ = state.clone();
637state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
638 state_.borrow_mut().timer_500_called += 1;
639 let state__ = state_.clone();
640 state_.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
641 state__.borrow_mut().timer_500_called += 1000;
642 });
643 let state__ = state_.clone();
644 state_.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
645 state__.borrow_mut().timer_200_called += 1000;
646 });
647});
648for _ in 0..20 {
649 i_slint_core::tests::slint_mock_elapsed_time(100);
650}
651assert_eq!(state.borrow().timer_200_called, 7011);
652assert_eq!(state.borrow().timer_once_called, 2);
653assert_eq!(state.borrow().timer_500_called, 3004);
654
655// Test set interval
656let state_ = state.clone();
657state.borrow_mut().timer_200.start(TimerMode::Repeated, Duration::from_millis(200), move || {
658 state_.borrow_mut().timer_200_called += 1;
659});
660let state_ = state.clone();
661state.borrow_mut().timer_once.start(TimerMode::Repeated, Duration::from_millis(300), move || {
662 state_.borrow_mut().timer_once_called += 1;
663 state_.borrow().timer_once.stop();
664});
665let state_ = state.clone();
666state.borrow_mut().timer_500.start(TimerMode::Repeated, Duration::from_millis(500), move || {
667 state_.borrow_mut().timer_500_called += 1;
668});
669
670let state_ = state.clone();
671slint::platform::update_timers_and_animations();
672for _ in 0..5 {
673 i_slint_core::tests::slint_mock_elapsed_time(100);
674}
675slint::platform::update_timers_and_animations();
676assert_eq!(state.borrow().timer_200_called, 7013);
677assert_eq!(state.borrow().timer_once_called, 3);
678assert_eq!(state.borrow().timer_500_called, 3005);
679
680for _ in 0..20 {
681 state.borrow().timer_200.set_interval(Duration::from_millis(200 * 2));
682 state.borrow().timer_once.set_interval(Duration::from_millis(300 * 2));
683 state.borrow().timer_500.set_interval(Duration::from_millis(500 * 2));
684
685 assert_eq!(state.borrow().timer_200_called, 7013);
686 assert_eq!(state.borrow().timer_once_called, 3);
687 assert_eq!(state.borrow().timer_500_called, 3005);
688
689 i_slint_core::tests::slint_mock_elapsed_time(100);
690}
691
692slint::platform::update_timers_and_animations();
693for _ in 0..9 {
694 i_slint_core::tests::slint_mock_elapsed_time(100);
695}
696slint::platform::update_timers_and_animations();
697assert_eq!(state.borrow().timer_200_called, 7015);
698assert_eq!(state.borrow().timer_once_called, 3);
699assert_eq!(state.borrow().timer_500_called, 3006);
700
701state.borrow_mut().timer_once.restart();
702for _ in 0..4 {
703 i_slint_core::tests::slint_mock_elapsed_time(100);
704}
705assert_eq!(state.borrow().timer_once_called, 3);
706for _ in 0..4 {
707 i_slint_core::tests::slint_mock_elapsed_time(100);
708}
709assert_eq!(state.borrow().timer_once_called, 4);
710
711```
712 */
713#[cfg(doctest)]
714const _TIMER_TESTS: () = ();
715
716/**
717 * Test that deleting an active timer from a timer event works.
718```rust
719// There is a 200 ms timer that increase variable1
720// after 500ms, that timer is destroyed by a single shot timer,
721// and a new new timer increase variable2
722i_slint_backend_testing::init();
723use slint::{Timer, TimerMode};
724use std::{rc::Rc, cell::RefCell, time::Duration};
725#[derive(Default)]
726struct SharedState {
727 repeated_timer: Timer,
728 variable1: usize,
729 variable2: usize,
730}
731let state = Rc::new(RefCell::new(SharedState::default()));
732// Note: state will be leaked because of circular dependencies: don't do that in production
733let state_ = state.clone();
734state.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
735 state_.borrow_mut().variable1 += 1;
736});
737let state_ = state.clone();
738Timer::single_shot(Duration::from_millis(500), move || {
739 state_.borrow_mut().repeated_timer = Default::default();
740 let state = state_.clone();
741 state_.borrow_mut().repeated_timer.start(TimerMode::Repeated, Duration::from_millis(200), move || {
742 state.borrow_mut().variable2 += 1;
743 })
744} );
745i_slint_core::tests::slint_mock_elapsed_time(10);
746assert_eq!(state.borrow().variable1, 0);
747assert_eq!(state.borrow().variable2, 0);
748i_slint_core::tests::slint_mock_elapsed_time(200);
749assert_eq!(state.borrow().variable1, 1);
750assert_eq!(state.borrow().variable2, 0);
751i_slint_core::tests::slint_mock_elapsed_time(200);
752assert_eq!(state.borrow().variable1, 2);
753assert_eq!(state.borrow().variable2, 0);
754i_slint_core::tests::slint_mock_elapsed_time(100);
755// More than 500ms have elapsed, the single shot timer should have been activated, but that has no effect on variable 1 and 2
756// This should just restart the timer so that the next change should happen 200ms from now
757assert_eq!(state.borrow().variable1, 2);
758assert_eq!(state.borrow().variable2, 0);
759i_slint_core::tests::slint_mock_elapsed_time(110);
760assert_eq!(state.borrow().variable1, 2);
761assert_eq!(state.borrow().variable2, 0);
762i_slint_core::tests::slint_mock_elapsed_time(100);
763assert_eq!(state.borrow().variable1, 2);
764assert_eq!(state.borrow().variable2, 1);
765i_slint_core::tests::slint_mock_elapsed_time(100);
766assert_eq!(state.borrow().variable1, 2);
767assert_eq!(state.borrow().variable2, 1);
768i_slint_core::tests::slint_mock_elapsed_time(100);
769assert_eq!(state.borrow().variable1, 2);
770assert_eq!(state.borrow().variable2, 2);
771```
772 */
773#[cfg(doctest)]
774const _BUG3029: () = ();
775
776/**
777 * Test that starting a singleshot timer works
778```rust
779// There is a 200 ms singleshot timer that increase variable1
780i_slint_backend_testing::init();
781use slint::{Timer, TimerMode};
782use std::{rc::Rc, cell::RefCell, time::Duration};
783#[derive(Default)]
784struct SharedState {
785 variable1: usize,
786}
787let state = Rc::new(RefCell::new(SharedState::default()));
788// Note: state will be leaked because of circular dependencies: don't do that in production
789let state_ = state.clone();
790let timer = Timer::default();
791
792timer.start(TimerMode::SingleShot, Duration::from_millis(200), move || {
793 state_.borrow_mut().variable1 += 1;
794});
795
796// Singleshot timer set up and run...
797assert!(timer.running());
798i_slint_core::tests::slint_mock_elapsed_time(10);
799assert!(timer.running());
800assert_eq!(state.borrow().variable1, 0);
801i_slint_core::tests::slint_mock_elapsed_time(200);
802assert_eq!(state.borrow().variable1, 1);
803assert!(!timer.running());
804i_slint_core::tests::slint_mock_elapsed_time(200);
805assert_eq!(state.borrow().variable1, 1); // It's singleshot, it only triggers once!
806assert!(!timer.running());
807
808// Restart a previously set up singleshot timer
809timer.restart();
810assert!(timer.running());
811assert_eq!(state.borrow().variable1, 1);
812i_slint_core::tests::slint_mock_elapsed_time(200);
813assert_eq!(state.borrow().variable1, 2);
814assert!(!timer.running());
815i_slint_core::tests::slint_mock_elapsed_time(200);
816assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
817assert!(!timer.running());
818
819// Stop a non-running singleshot timer
820timer.stop();
821assert!(!timer.running());
822assert_eq!(state.borrow().variable1, 2);
823i_slint_core::tests::slint_mock_elapsed_time(200);
824assert_eq!(state.borrow().variable1, 2);
825assert!(!timer.running());
826i_slint_core::tests::slint_mock_elapsed_time(200);
827assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
828assert!(!timer.running());
829
830// Stop a running singleshot timer
831timer.restart();
832assert!(timer.running());
833assert_eq!(state.borrow().variable1, 2);
834i_slint_core::tests::slint_mock_elapsed_time(10);
835timer.stop();
836assert!(!timer.running());
837i_slint_core::tests::slint_mock_elapsed_time(200);
838assert_eq!(state.borrow().variable1, 2);
839assert!(!timer.running());
840i_slint_core::tests::slint_mock_elapsed_time(200);
841assert_eq!(state.borrow().variable1, 2); // It's singleshot, it only triggers once!
842assert!(!timer.running());
843
844// set_interval on a non-running singleshot timer
845timer.set_interval(Duration::from_millis(300));
846assert!(!timer.running());
847i_slint_core::tests::slint_mock_elapsed_time(1000);
848assert_eq!(state.borrow().variable1, 2);
849assert!(!timer.running());
850timer.restart();
851assert!(timer.running());
852i_slint_core::tests::slint_mock_elapsed_time(200);
853assert_eq!(state.borrow().variable1, 2);
854assert!(timer.running());
855i_slint_core::tests::slint_mock_elapsed_time(200);
856assert_eq!(state.borrow().variable1, 3);
857assert!(!timer.running());
858i_slint_core::tests::slint_mock_elapsed_time(300);
859assert_eq!(state.borrow().variable1, 3); // It's singleshot, it only triggers once!
860assert!(!timer.running());
861
862// set_interval on a running singleshot timer
863timer.restart();
864assert!(timer.running());
865assert_eq!(state.borrow().variable1, 3);
866i_slint_core::tests::slint_mock_elapsed_time(290);
867timer.set_interval(Duration::from_millis(400));
868assert!(timer.running());
869i_slint_core::tests::slint_mock_elapsed_time(200);
870assert_eq!(state.borrow().variable1, 3);
871assert!(timer.running());
872i_slint_core::tests::slint_mock_elapsed_time(250);
873assert_eq!(state.borrow().variable1, 4);
874assert!(!timer.running());
875i_slint_core::tests::slint_mock_elapsed_time(400);
876assert_eq!(state.borrow().variable1, 4); // It's singleshot, it only triggers once!
877assert!(!timer.running());
878```
879 */
880#[cfg(doctest)]
881const _SINGLESHOT_START: () = ();
882