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