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, TileExt, event::Event, geom::Size};
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        let time = Instant::now() + delay;
66        if let Some(row) = self
67            .time_updates
68            .iter_mut()
69            .find(|row| row.1 == id && row.2 == handle)
70        {
71            let earliest = handle.earliest();
72            if earliest && row.0 <= time || !earliest && row.0 >= time {
73                return;
74            }
75
76            row.0 = time;
77        } else {
78            log::trace!(
79                target: "kas_core::event",
80                "request_timer: update {id} at now+{}ms",
81                delay.as_millis()
82            );
83            self.time_updates.push((time, id, handle));
84        }
85
86        self.time_updates.sort_by(|a, b| b.0.cmp(&a.0)); // reverse sort
87    }
88
89    /// Schedule a frame timer update
90    ///
91    /// Widget `id` will receive [`Event::Timer`] with this `handle` either
92    /// before or soon after the next frame is drawn.
93    ///
94    /// This may be useful for animations which mutate widget state. Animations
95    /// which don't mutate widget state may use
96    /// [`Draw::animate`](crate::draw::Draw::animate) instead.
97    ///
98    /// It is expected that `handle.earliest() == true` (style guide).
99    pub fn request_frame_timer(&mut self, id: Id, handle: TimerHandle) {
100        debug_assert!(handle.earliest());
101        self.frame_updates.insert((id, handle));
102    }
103}
104
105impl<'a> EventCx<'a> {
106    /// Pre-draw / pre-sleep
107    ///
108    /// This method should be called once per frame as well as after the last
109    /// frame before a long sleep.
110    pub(crate) fn frame_update(&mut self, mut widget: Node<'_>) {
111        self.need_frame_update = false;
112        log::trace!(target: "kas_core::event", "Processing frame update");
113        if let Some((target, affine)) = self.mouse.frame_update() {
114            self.send_event(widget.re(), target, Event::Pan(affine));
115        }
116        self.touch_frame_update(widget.re());
117
118        let frame_updates = std::mem::take(&mut self.frame_updates);
119        for (id, handle) in frame_updates.into_iter() {
120            self.send_event(widget.re(), id, Event::Timer(handle));
121        }
122
123        // Set IME cursor area, if moved.
124        if self.ime.is_some()
125            && let Some(target) = self.sel_focus.as_ref()
126            && let Some((mut rect, translation)) = widget.as_tile().find_tile_rect(target)
127        {
128            if self.ime_cursor_area.size != Size::ZERO {
129                rect = self.ime_cursor_area;
130            }
131            rect += translation;
132            if rect != self.last_ime_rect {
133                self.window.set_ime_cursor_area(rect);
134                self.last_ime_rect = rect;
135            }
136        }
137    }
138
139    /// Update widgets due to timer
140    pub(crate) fn update_timer(&mut self, mut widget: Node<'_>) {
141        let now = Instant::now();
142
143        // assumption: time_updates are sorted in reverse order
144        while !self.time_updates.is_empty() {
145            if self.time_updates.last().unwrap().0 > now {
146                break;
147            }
148
149            let update = self.time_updates.pop().unwrap();
150            self.send_event(widget.re(), update.1, Event::Timer(update.2));
151        }
152
153        self.time_updates.sort_by(|a, b| b.0.cmp(&a.0)); // reverse sort
154    }
155}