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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// 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: event send / replay
use super::{EventCx, EventState};
use crate::event::{Command, Event, Scroll, ScrollDelta, Used};
use crate::messages::Erased;
use crate::runner::ReadMessage;
#[allow(unused)]
use crate::{Events, Tile, Widget, event::ConfigCx};
use crate::{Id, Node};
use std::fmt::Debug;
use std::task::Poll;
impl EventState {
/// Send a message to `id`
///
/// When calling this method, be aware that some widgets use an inner
/// component to handle events, thus calling with the outer widget's `id`
/// may not have the desired effect. [`Tile::try_probe`] and
/// [`EventCx::next_nav_focus`] are usually able to find the appropriate
/// event-handling target.
///
/// This uses a tree traversal as with event handling, thus ancestors will
/// have a chance to handle an unhandled event and any messages on the stack
/// after their child.
///
/// ### Special cases sent as an [`Event`]
///
/// When `M` is `Command`, this will send [`Event::Command`] to the widget.
///
/// When `M` is `ScrollDelta`, this will send [`Event::Scroll`] to the
/// widget.
///
/// ### Other messages
///
/// The message is pushed to the message stack. The target widget may
/// [pop](EventCx::try_pop) or [peek](EventCx::try_peek) the message from
/// [`Events::handle_messages`].
///
/// ### Send target
///
/// The target `id` may be under another window. In this case, sending may
/// be delayed slightly.
///
/// If `id = Id::default()` and a [send target](super::ConfigCx::set_send_target_for)
/// has been assigned for `M`, then `msg` will be sent to that target.
///
/// If `id` identifies a widget, that widget must be configured but does not
/// need to be sized.
///
/// If `id` does not resolve a widget, the message is dropped with only a
/// DEBUG log message of the form `_replay: $WIDGET cannot find path to $ID`.
/// This is not considered an error since it happens frequently (e.g. any
/// widget using asynchronous loading sends itself a message; if the view
/// changes before loading completes then that widget may no longer be
/// accessible or no longer exist).
///
/// ### Ordering and failure
///
/// Messages sent via `send` and [`send_erased`](Self::send_erased) should
/// be received in the same order sent relative to other messages sent from
/// the same window via these methods.
///
/// Message delivery may fail for widgets not currently visible. This is
/// dependent on widgets implementation of [`Widget::child_node`] (e.g. in
/// the case of virtual scrolling, the target may have scrolled out of
/// range).
pub fn send<M: Debug + 'static>(&mut self, id: Id, msg: M) {
self.send_erased(id, Erased::new(msg));
}
/// Push a type-erased message to the stack
///
/// This is a lower-level variant of [`Self::send`].
pub fn send_erased(&mut self, id: Id, msg: Erased) {
self.send_queue.push_back((id, msg));
}
/// Send a message to `id` via a [`Future`]
///
/// The future is polled after event handling and after drawing and is able
/// to wake the event loop. This future is executed on the main thread; for
/// high-CPU tasks use [`Self::send_spawn`] instead.
///
/// The future must resolve to a message on completion. Its message is then
/// sent to `id` via stack traversal identically to [`Self::send`].
///
/// ### Cancellation, ordering and failure
///
/// Futures passed to these methods should only be cancelled if they fail to
/// complete before the window is closed, and even in this case will be
/// polled at least once.
///
/// Messages sent via `send_async`,
/// [`send_async_erased`](Self::send_async_erased) and
/// [`send_spawn`](Self::send_spawn) may be received in any order.
///
/// Message delivery may fail for widgets not currently visible. This is
/// dependent on widgets implementation of [`Widget::child_node`] (e.g. in
/// the case of virtual scrolling, the target may have scrolled out of
/// range).
pub fn send_async<Fut, M>(&mut self, id: Id, fut: Fut)
where
Fut: IntoFuture<Output = M> + 'static,
M: Debug + 'static,
{
self.send_async_erased(id, async { Erased::new(fut.await) });
}
/// Send a type-erased message to `id` via a [`Future`]
///
/// This is a low-level variant of [`Self::send_async`].
pub fn send_async_erased<Fut>(&mut self, id: Id, fut: Fut)
where
Fut: IntoFuture<Output = Erased> + 'static,
{
let fut = Box::pin(fut.into_future());
self.fut_messages.push((id, fut));
}
/// Spawn a task, run on a thread pool
///
/// The future is spawned to a thread-pool before the event-handling loop
/// sleeps, and is able to wake the loop on completion. Tasks involving
/// significant CPU work should use this method over [`Self::send_async`].
///
/// This method is simply a wrapper around [`async_global_executor::spawn`]
/// and [`Self::send_async`]; if a different multi-threaded executor is
/// available, that may be used instead. See also [`async_global_executor`]
/// documentation of configuration.
#[cfg(feature = "spawn")]
pub fn send_spawn<Fut, M>(&mut self, id: Id, fut: Fut)
where
Fut: IntoFuture<Output = M> + 'static,
Fut::IntoFuture: Send,
M: Debug + Send + 'static,
{
self.send_async(id, async_global_executor::spawn(fut.into_future()));
}
}
impl<'a> EventCx<'a> {
/// Get the index of the last child visited
///
/// This is only used when unwinding (traversing back up the widget tree),
/// and returns the index of the child last visited. E.g. when
/// [`Events::handle_messages`] is called, this method returns the index of
/// the child which submitted the message (or whose descendant did).
/// Otherwise this returns `None` (including when the widget itself is the
/// submitter of the message).
pub fn last_child(&self) -> Option<usize> {
self.last_child
}
/// Push a message to the stack
///
/// The message is first type-erased by wrapping with [`Erased`],
/// then pushed to the stack.
///
/// The message may be [popped](EventCx::try_pop) or
/// [peeked](EventCx::try_peek) from [`Events::handle_messages`]
/// by the widget itself, its parent, or any ancestor.
///
/// If not handled during the widget tree traversal and
/// [a target is set for `M`](ConfigCx::set_send_target_for) then `msg` is
/// sent to this target.
pub fn push<M: Debug + 'static>(&mut self, msg: M) {
self.push_erased(Erased::new(msg));
}
/// Push a type-erased message to the stack
///
/// This is a lower-level variant of [`Self::push`].
pub fn push_erased(&mut self, msg: Erased) {
self.runner.message_stack_mut().push_erased(msg);
}
/// True if the message stack is non-empty
pub fn has_msg(&self) -> bool {
self.runner.message_stack().has_any()
}
/// Try popping the last message from the stack with the given type
///
/// This method may be called from [`Events::handle_messages`].
pub fn try_pop<M: Debug + 'static>(&mut self) -> Option<M> {
self.runner.message_stack_mut().try_pop()
}
/// Try observing the last message on the stack without popping
///
/// This method may be called from [`Events::handle_messages`].
pub fn try_peek<M: Debug + 'static>(&self) -> Option<&M> {
self.runner.message_stack().try_peek()
}
/// Debug the last message on the stack, if any
pub fn peek_debug(&self) -> Option<&dyn Debug> {
self.runner.message_stack().peek_debug()
}
/// Get the message stack operation count
///
/// This is incremented every time the message stack is changed, thus can be
/// used to test whether a message handler did anything.
#[inline]
pub fn msg_op_count(&self) -> usize {
self.runner.message_stack().get_op_count()
}
/// Drop messages above the stack base.
pub(crate) fn drop_unsent(&mut self) {
self.runner.message_stack_mut().drop_unsent();
}
/// Set a scroll action
///
/// When setting [`Scroll::Rect`], use the widget's own coordinate space.
///
/// Note that calling this method has no effect on the widget itself, but
/// affects parents via their [`Events::handle_scroll`] method.
#[inline]
pub fn set_scroll(&mut self, scroll: Scroll) {
self.scroll = scroll;
}
pub(crate) fn post_send(&mut self, index: usize) -> Option<Scroll> {
self.last_child = Some(index);
(self.scroll != Scroll::None).then_some(self.scroll.clone())
}
/// Send a few message types as an Event, replay other messages as if pushed by `id`
///
/// Optionally, push `msg` and set `scroll` as if pushed/set by `id`.
pub(super) fn send_or_replay(&mut self, mut widget: Node<'_>, id: Id, mut msg: Erased) {
if msg.is::<Command>() {
let cmd = *msg.downcast().unwrap();
if !self.send_event(widget, id, Event::Command(cmd, None)) {
match cmd {
Command::Exit => self.runner.exit(),
Command::Close => self.handle_close(),
_ => (),
}
}
} else if msg.is::<ScrollDelta>() {
let event = Event::Scroll(*msg.downcast().unwrap());
self.send_event(widget, id, event);
} else {
self.pre_recursion();
self.runner.message_stack_mut().set_base();
log::trace!(target: "kas_core::event", "replay: id={id}: {msg:?}");
self.target_is_disabled = false;
msg.set_sent();
self.push_erased(msg);
widget._replay(self, id);
self.post_recursion();
}
}
/// Replay a scroll action
#[cfg(feature = "accesskit")]
pub(super) fn replay_scroll(&mut self, mut widget: Node<'_>, id: Id, scroll: Scroll) {
log::trace!(target: "kas_core::event", "replay_scroll: id={id}: {scroll:?}");
self.pre_recursion();
self.scroll = scroll;
self.runner.message_stack_mut().set_base();
self.target_is_disabled = false;
widget._replay(self, id);
self.post_recursion();
}
// Call Widget::_send; returns true when event is used
pub(super) fn send_event(&mut self, mut widget: Node<'_>, mut id: Id, event: Event) -> bool {
self.pre_recursion();
self.runner.message_stack_mut().set_base();
log::trace!(target: "kas_core::event", "send_event: id={id}: {event:?}");
// TODO(opt): we should be able to use binary search here
let mut disabled = false;
if !event.pass_when_disabled() {
for d in &self.disabled {
if d.is_ancestor_of(&id) {
id = d.clone();
disabled = true;
}
}
if disabled {
log::trace!(target: "kas_core::event", "target is disabled; sending to ancestor {id}");
}
}
self.target_is_disabled = disabled;
let used = widget._send(self, id, event) == Used;
self.post_recursion();
used
}
pub(super) fn poll_futures(&mut self) {
let mut i = 0;
while i < self.state.fut_messages.len() {
let (_, fut) = &mut self.cx.state.fut_messages[i];
let mut cx = std::task::Context::from_waker(self.runner.waker());
match fut.as_mut().poll(&mut cx) {
Poll::Pending => {
i += 1;
}
Poll::Ready(msg) => {
let (id, _) = self.state.fut_messages.remove(i);
// Send via queue to support send targets and inter-window sending
self.send_queue.push_back((id, msg));
}
}
}
}
}