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