Skip to main content

kas_core/event/cx/
timer.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Event context: timers
7
8use super::{EventCx, EventState};
9use crate::{Id, Node, event::Event};
10use std::time::{Duration, Instant};
11
12/// A timer handle
13#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14pub struct TimerHandle(i64);
15impl TimerHandle {
16    /// Construct a new handle
17    ///
18    /// The code must be positive. If a widget uses multiple timers, each must
19    /// have a unique code.
20    ///
21    /// When a timer update is requested multiple times before delivery using
22    /// the same `TimerHandle`, these requests are merged, choosing the
23    /// earliest time if `earliest`, otherwise the latest time.
24    pub const fn new(code: i64, earliest: bool) -> Self {
25        assert!(code >= 0);
26        if earliest {
27            TimerHandle(-code - 1)
28        } else {
29            TimerHandle(code)
30        }
31    }
32
33    /// Check whether this timer chooses the earliest time when merging
34    pub fn earliest(self) -> bool {
35        self.0 < 0
36    }
37}
38
39impl EventState {
40    /// Get the next resume time
41    pub(crate) fn next_resume(&self) -> Option<Instant> {
42        self.time_updates.last().map(|time| time.0)
43    }
44
45    pub(crate) fn need_frame_update(&self) -> bool {
46        self.need_frame_update || !self.frame_updates.is_empty() || !self.fut_messages.is_empty()
47    }
48
49    /// Schedule a timed update
50    ///
51    /// Widget updates may be used for delayed action. For animation, prefer to
52    /// use [`Draw::animate`](crate::draw::Draw::animate) or
53    /// [`Self::request_frame_timer`].
54    ///
55    /// Widget `id` will receive [`Event::Timer`] with this `handle` at
56    /// approximately `time = now + delay` (or possibly a little later due to
57    /// frame-rate limiters and processing time).
58    ///
59    /// Requesting an update with `delay == 0` is valid, except from an
60    /// [`Event::Timer`] handler (where it may cause an infinite loop).
61    ///
62    /// Multiple timer requests with the same `id` and `handle` are merged
63    /// (see [`TimerHandle`] documentation).
64    pub fn request_timer(&mut self, id: Id, handle: TimerHandle, delay: Duration) {
65        debug_assert!(id.is_valid());
66        let time = Instant::now() + delay;
67        if let Some(row) = self
68            .time_updates
69            .iter_mut()
70            .find(|row| row.1 == id && row.2 == handle)
71        {
72            let earliest = handle.earliest();
73            if earliest && row.0 <= time || !earliest && row.0 >= time {
74                return;
75            }
76
77            row.0 = time;
78        } else {
79            log::trace!(
80                target: "kas_core::event",
81                "request_timer: update {id} at now+{}ms",
82                delay.as_millis()
83            );
84            self.time_updates.push((time, id, handle));
85        }
86
87        self.time_updates.sort_by(|a, b| b.0.cmp(&a.0)); // reverse sort
88    }
89
90    /// Schedule a frame timer update
91    ///
92    /// Widget `id` will receive [`Event::Timer`] with this `handle` either
93    /// before or soon after the next frame is drawn.
94    ///
95    /// This may be useful for animations which mutate widget state. Animations
96    /// which don't mutate widget state may use
97    /// [`Draw::animate`](crate::draw::Draw::animate) instead.
98    ///
99    /// It is expected that `handle.earliest() == true` (style guide).
100    pub fn request_frame_timer(&mut self, id: Id, handle: TimerHandle) {
101        debug_assert!(handle.earliest());
102        self.frame_updates.insert((id, handle));
103    }
104}
105
106impl<'a> EventCx<'a> {
107    /// Pre-draw / pre-sleep
108    ///
109    /// This method should be called once per frame as well as after the last
110    /// frame before a long sleep.
111    pub(crate) fn frame_update(&mut self, mut widget: Node<'_>) {
112        self.need_frame_update = false;
113        log::trace!(target: "kas_core::event", "Processing frame update");
114        if let Some((target, affine)) = self.mouse.frame_update() {
115            self.send_event(widget.re(), target, Event::Pan(affine));
116        }
117        self.touch_frame_update(widget.re());
118
119        let frame_updates = std::mem::take(&mut self.frame_updates);
120        for (id, handle) in frame_updates.into_iter() {
121            self.send_event(widget.re(), id, Event::Timer(handle));
122        }
123
124        self.cx.input.frame_update(self.window, widget.as_tile());
125    }
126
127    /// Update widgets due to timer
128    pub(crate) fn update_timer(&mut self, mut widget: Node<'_>) {
129        let now = Instant::now();
130
131        // assumption: time_updates are sorted in reverse order
132        while !self.time_updates.is_empty() {
133            if self.time_updates.last().unwrap().0 > now {
134                break;
135            }
136
137            let update = self.time_updates.pop().unwrap();
138            self.send_event(widget.re(), update.1, Event::Timer(update.2));
139        }
140
141        self.time_updates.sort_by(|a, b| b.0.cmp(&a.0)); // reverse sort
142    }
143}