zng_app/handler.rs
1//! Handler types and macros.
2
3use std::pin::Pin;
4use std::sync::Arc;
5use std::thread;
6use std::time::Duration;
7
8use parking_lot::Mutex;
9#[doc(hidden)]
10pub use zng_clone_move::*;
11
12use crate::update::UPDATES;
13use crate::widget::{UiTaskWidget as _, WIDGET};
14use crate::{AppControlFlow, HeadlessApp};
15use zng_handle::{Handle, WeakHandle};
16use zng_task::{self as task, UiTask};
17
18use crate::INSTANT;
19
20/// Output of [`Handler<A>`].
21pub enum HandlerResult {
22 /// Handler already finished.
23 Done,
24 /// Handler is async and the future was pending after first poll. The caller must run the future in the same context the handler was called.
25 Continue(Pin<Box<dyn Future<Output = ()> + Send + 'static>>),
26}
27
28/// Represents a handler in a widget context.
29///
30/// There are different flavors of handlers, you can use macros to declare then.
31/// See [`hn!`], [`hn_once!`] or [`async_hn!`], [`async_hn_once!`] to start.
32///
33/// # Type Inference Limitations
34///
35/// This type is not a full struct because the closure args type inference only works with `Box`, if this was
36/// a full `struct` all handler declarations that use the args would have to declare the args type.
37/// Methods for this type are implemented in [`HandlerExt`]. Also note that the `A` type must be `Clone + 'static`,
38/// unfortunately Rust does not enforce bounds in type alias.
39#[allow(type_alias_bounds)] // we need a type alias here
40pub type Handler<A: Clone + 'static> = Box<dyn FnMut(&A) -> HandlerResult + Send + 'static>;
41
42/// Extension methods for [`Handler<A>`].
43pub trait HandlerExt<A: Clone + 'static> {
44 /// Notify the handler in a widget context.
45 ///
46 /// If the handler is async polls once immediately and returns an [`UiTask`] if the future is pending.
47 /// The caller must update the task until completion in the same widget context.
48 fn widget_event(&mut self, args: &A) -> Option<UiTask<()>>;
49
50 /// Notify the handler outside of any widget or window context, inside a [`APP_HANDLER`] context.
51 ///
52 /// If the handler is async polls once and continue execution in [`UPDATES`].
53 fn app_event(&mut self, handle: Box<dyn AppWeakHandle>, is_preview: bool, args: &A);
54
55 /// New handler that only calls for arguments approved by `filter`.
56 fn filtered(self, filter: impl FnMut(&A) -> bool + Send + 'static) -> Handler<A>;
57
58 /// New handler that calls this one only once.
59 fn into_once(self) -> Handler<A>;
60
61 /// Into cloneable handler.
62 ///
63 /// Note that [`hn_once!`] and [`async_hn_once!`] handlers will still only run once.
64 fn into_arc(self) -> ArcHandler<A>;
65
66 /// Wrap the handler into a type that implements the async task management in an widget context.
67 fn into_wgt_runner(self) -> WidgetRunner<A>;
68}
69impl<A: Clone + 'static> HandlerExt<A> for Handler<A> {
70 fn widget_event(&mut self, args: &A) -> Option<UiTask<()>> {
71 match self(args) {
72 HandlerResult::Done => None,
73 HandlerResult::Continue(future) => {
74 let mut task = UiTask::new_boxed(Some(WIDGET.id()), future);
75 if task.update().is_none() { Some(task) } else { None }
76 }
77 }
78 }
79
80 fn app_event(&mut self, handle: Box<dyn AppWeakHandle>, is_preview: bool, args: &A) {
81 match APP_HANDLER.with(handle.clone_boxed(), is_preview, || self(args)) {
82 HandlerResult::Done => {}
83 HandlerResult::Continue(future) => {
84 let mut task = UiTask::new_boxed(None, future);
85 if APP_HANDLER.with(handle.clone_boxed(), is_preview, || task.update().is_none()) {
86 if is_preview {
87 UPDATES
88 .on_pre_update(hn!(|_| {
89 if APP_HANDLER.with(handle.clone_boxed(), is_preview, || task.update().is_some()) {
90 APP_HANDLER.unsubscribe();
91 }
92 }))
93 .perm();
94 } else {
95 UPDATES
96 .on_update(hn!(|_| {
97 if APP_HANDLER.with(handle.clone_boxed(), is_preview, || task.update().is_some()) {
98 APP_HANDLER.unsubscribe();
99 }
100 }))
101 .perm();
102 }
103 }
104 }
105 }
106 }
107
108 fn filtered(mut self, mut filter: impl FnMut(&A) -> bool + Send + 'static) -> Self {
109 Box::new(move |a| if filter(a) { self(a) } else { HandlerResult::Done })
110 }
111
112 fn into_once(self) -> Self {
113 let mut f = Some(self);
114 Box::new(move |a| {
115 if let Some(mut f) = f.take() {
116 APP_HANDLER.unsubscribe();
117 f(a)
118 } else {
119 HandlerResult::Done
120 }
121 })
122 }
123
124 fn into_arc(self) -> ArcHandler<A> {
125 ArcHandler(Arc::new(Mutex::new(self)))
126 }
127
128 fn into_wgt_runner(self) -> WidgetRunner<A> {
129 WidgetRunner::new(self)
130 }
131}
132
133/// Represents a cloneable handler.
134///
135/// See [`Handler::into_arc`] for more details.
136#[derive(Clone)]
137pub struct ArcHandler<A: Clone + 'static>(Arc<Mutex<Handler<A>>>);
138impl<A: Clone + 'static> ArcHandler<A> {
139 /// Calls [`HandlerExt::widget_event`].
140 pub fn widget_event(&self, args: &A) -> Option<UiTask<()>> {
141 self.0.lock().widget_event(args)
142 }
143
144 /// Calls [`HandlerExt::app_event`].
145 pub fn app_event(&self, handle: Box<dyn AppWeakHandle>, is_preview: bool, args: &A) {
146 self.0.lock().app_event(handle, is_preview, args)
147 }
148
149 /// Calls the handler.
150 pub fn call(&self, args: &A) -> HandlerResult {
151 self.0.lock()(args)
152 }
153
154 /// Make a handler from this arc handler.
155 pub fn handler(&self) -> Handler<A> {
156 self.clone().into()
157 }
158}
159impl<A: Clone + 'static> From<ArcHandler<A>> for Handler<A> {
160 fn from(f: ArcHandler<A>) -> Self {
161 Box::new(move |a| f.0.lock()(a))
162 }
163}
164
165/// Represents an widget [`Handler<A>`] caller that manages the async tasks if needed.
166///
167/// See [`Handler::into_wgt_runner`] for more details.
168pub struct WidgetRunner<A: Clone + 'static> {
169 handler: Handler<A>,
170 tasks: Vec<UiTask<()>>,
171}
172
173impl<A: Clone + 'static> WidgetRunner<A> {
174 fn new(handler: Handler<A>) -> Self {
175 Self { handler, tasks: vec![] }
176 }
177
178 /// Call [`HandlerExt::widget_event`] and start UI task is needed.
179 pub fn event(&mut self, args: &A) {
180 if let Some(task) = self.handler.widget_event(args) {
181 self.tasks.push(task);
182 }
183 }
184
185 /// Update async tasks.
186 ///
187 /// UI node implementers must call this on [`UiNodeOp::Update`].
188 /// For preview events before delegation to child, for other after delegation.
189 ///
190 /// [`UiNodeOp::Update`]: crate::widget::node::UiNodeOp::Update
191 pub fn update(&mut self) {
192 self.tasks.retain_mut(|t| t.update().is_none());
193 }
194
195 /// Drop pending tasks.
196 ///
197 /// Dropped tasks will log a warning.
198 ///
199 /// UI node implementers must call this on [`UiNodeOp::Deinit`], async tasks must not run across widget reinit.
200 ///
201 /// [`UiNodeOp::Deinit`]: crate::widget::node::UiNodeOp::Deinit
202 pub fn deinit(&mut self) {
203 self.tasks.clear();
204 }
205}
206
207///<span data-del-macro-root></span> Declare a mutable *clone-move* event handler.
208///
209/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
210/// the input is the same syntax.
211///
212/// # Examples
213///
214/// The example declares an event handler for the `on_click` property.
215///
216/// ```
217/// # macro_rules! example { () => {
218/// on_click = hn!(|_| {
219/// println!("Clicked {}!", args.click_count);
220/// });
221/// # }}
222/// ```
223///
224/// Internally the [`clmv!`] macro is used so you can *clone-move* variables into the handler.
225///
226/// ```
227/// # macro_rules! example { () => {
228/// let foo = var(0);
229///
230/// // ..
231///
232/// # let
233/// on_click = hn!(foo, |args| {
234/// foo.set(args.click_count);
235/// });
236///
237/// // can still use after:
238/// let bar = foo.map(|c| formatx!("click_count: {c}"));
239///
240/// # }}
241/// ```
242///
243/// In the example above only a clone of `foo` is moved into the handler. Note that handlers always capture by move, if `foo` was not
244/// listed in the *clone-move* section it would not be available after the handler is created. See [`clmv!`] for details.
245///
246/// # App Scope
247///
248/// When used in app scopes the [`APP_HANDLER`] contextual service can be used to unsubscribe from inside the handler.
249///
250/// The example declares an event handler for the `CLICK_EVENT`. Unlike in an widget this handler will run in the app scope, in this case
251/// the `APP_HANDLER` is available during handler calls, in the example the subscription handle is marked `perm`, but the event still unsubscribes
252/// from the inside.
253///
254/// ```
255/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
256/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
257/// # use zng_app::handler::{hn, APP_HANDLER};
258/// # let _scope = zng_app::APP.minimal();
259/// # fn assert_type() {
260/// CLICK_EVENT
261/// .on_event(hn!(|args| {
262/// println!("Clicked Somewhere!");
263/// if args.target == "something" {
264/// APP_HANDLER.unsubscribe();
265/// }
266/// }))
267/// .perm();
268/// # }
269/// ```
270///
271/// [`clmv!`]: zng_clone_move::clmv
272#[macro_export]
273macro_rules! hn {
274 ($($clmv:ident,)* |_| $body:expr) => {
275 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |_| {
276 #[allow(clippy::redundant_closure_call)] // closure is to support `return;`
277 (||{
278 $body
279 })();
280 #[allow(unused)]
281 {
282 $crate::handler::HandlerResult::Done
283 }
284 }))
285 };
286 ($($clmv:ident,)* |$args:ident| $body:expr) => {
287 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args| {
288 #[allow(clippy::redundant_closure_call)]
289 (||{
290 $body
291 })();
292 #[allow(unused)]
293 {
294 $crate::handler::HandlerResult::Done
295 }
296 }))
297 };
298 ($($clmv:ident,)* |$args:ident : & $Args:ty| $body:expr) => {
299 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &$Args| {
300 #[allow(clippy::redundant_closure_call)]
301 (||{
302 $body
303 })();
304 #[allow(unused)]
305 {
306 $crate::handler::HandlerResult::Done
307 }
308 }))
309 };
310}
311#[doc(inline)]
312pub use crate::hn;
313
314///<span data-del-macro-root></span> Declare a *clone-move* event handler that is only called once.
315///
316/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
317/// the input is the same syntax.
318///
319/// # Examples
320///
321/// The example captures `data` by move and then destroys it in the first call, this cannot be done using [`hn!`] because
322/// the `data` needs to be available for all event calls. In this case the closure is only called once, subsequent events
323/// are ignored by the handler.
324///
325/// ```
326/// # macro_rules! example { () => {
327/// let data = vec![1, 2, 3];
328/// # let
329/// on_click = hn_once!(|_| {
330/// for i in data {
331/// print!("{i}, ");
332/// }
333/// });
334/// # }}
335/// ```
336///
337/// [`clmv!`]: zng_clone_move::clmv
338#[macro_export]
339macro_rules! hn_once {
340 ($($clmv:ident,)* |_| $body:expr) => {{
341 let mut once: Option<std::boxed::Box<dyn FnOnce() + Send + 'static>> =
342 Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* || { $body })));
343 $crate::handler::hn!(|_| if let Some(f) = once.take() {
344 $crate::handler::APP_HANDLER.unsubscribe();
345 f();
346 })
347 }};
348 ($($clmv:ident,)* |$args:ident| $body:expr) => {{
349 // type inference fails here, error message slightly better them not having this pattern
350 let mut once: std::boxed::Box<dyn FnOnce(&_) + Send + 'static> =
351 Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &_| { $body })));
352 $crate::handler::hn!(|$args: &_| if let Some(f) = once.take() {
353 $crate::handler::APP_HANDLER.unsubscribe();
354 f($args);
355 })
356 }};
357 ($($clmv:ident,)* |$args:ident : & $Args:ty| $body:expr) => {{
358 // type inference fails here, error message slightly better them not having this pattern
359 let mut once: Option<std::boxed::Box<dyn FnOnce(&$Args) + Send + 'static>> =
360 Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &$Args| { $body })));
361 $crate::handler::hn!(|$args: &$Args| if let Some(f) = once.take() {
362 $crate::handler::APP_HANDLER.unsubscribe();
363 f($args);
364 })
365 }};
366}
367#[doc(inline)]
368pub use crate::hn_once;
369
370///<span data-del-macro-root></span> Declare an async *clone-move* event handler.
371///
372/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
373/// the input is the same syntax, for each call is also uses [`async_clmv!`] to clone the args and other cloning captures.
374///
375/// # Examples
376///
377/// The example declares an async event handler for the `on_click` property.
378///
379/// ```
380/// # macro_rules! example { () => {
381/// on_click = async_hn!(|args| {
382/// println!("Clicked {} {} times!", WIDGET.id(), args.click_count);
383///
384/// task::run(async {
385/// println!("In other thread!");
386/// })
387/// .await;
388///
389/// println!("Back in UI thread, in a widget update.");
390/// });
391/// # }}
392/// ```
393///
394/// Internally the [`clmv!`] macro is used so you can *clone-move* variables into the handler.
395///
396/// ```
397/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
398/// # use zng_app::handler::async_hn;
399/// # use zng_var::{var, Var};
400/// # use zng_task as task;
401/// # use zng_txt::formatx;
402/// # let _scope = zng_app::APP.minimal();
403/// # fn assert_type() -> zng_app::handler::Handler<ClickArgs> {
404/// let enabled = var(true);
405///
406/// // ..
407///
408/// # let
409/// on_click = async_hn!(enabled, |args: &ClickArgs| {
410/// enabled.set(false);
411///
412/// task::run(async move {
413/// println!("do something {}", args.click_count);
414/// })
415/// .await;
416///
417/// enabled.set(true);
418/// });
419///
420/// // can still use after:
421/// # let
422/// text = enabled.map(|&e| if e { "Click Me!" } else { "Busy.." });
423/// enabled;
424///
425/// # on_click }
426/// ```
427///
428/// In the example above only a clone of `enabled` is moved into the handler. Note that handlers always capture by move, if `enabled` was not
429/// listed in the *clone-move* section it would not be available after the handler is created. See [`async_clmv_fn!`] for details.
430///
431/// The example also demonstrates a common pattern with async handlers, most events are only raised when the widget is enabled, so you can
432/// disable the widget while the async task is running. This way you don't block the UI running a task but the user cannot spawn a second
433/// task while the first is still running.
434///
435/// ## Futures and Clone-Move
436///
437/// You want to always *clone-move* captures for async handlers, because they then automatically get cloned again for each event. This
438/// needs to happen because you can have more then one *handler task* running at the same type, and both want access to the captured variables.
439///
440/// This second cloning can be avoided by using the [`async_hn_once!`] macro instead, but only if you expect a single event.
441///
442/// Note that this means you are declaring a normal closure that returns a `'static` future, not an async closure, see [`async_clmv_fn!`].
443///
444/// [`async_clmv_fn!`]: zng_clone_move::async_clmv_fn
445#[macro_export]
446macro_rules! async_hn {
447 ($($clmv:ident,)* |_| $body:expr) => {
448 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |_| {
449 $crate::handler::HandlerResult::Continue(std::boxed::Box::pin($crate::handler::async_clmv!($($clmv,)* {$body})))
450 }))
451 };
452 ($($clmv:ident,)* |$args:ident| $body:expr) => {
453 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args| {
454 $crate::handler::HandlerResult::Continue(std::boxed::Box::pin($crate::handler::async_clmv!($args, $($clmv,)* {$body})))
455 }))
456 };
457 ($($clmv:ident,)* |$args:ident : & $Args:ty| $body:expr) => {
458 std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &$Args| {
459 $crate::handler::HandlerResult::Continue(std::boxed::Box::pin($crate::handler::async_clmv!($args, $($clmv,)* {$body})))
460 }))
461 };
462}
463#[doc(inline)]
464pub use crate::async_hn;
465
466///<span data-del-macro-root></span> Declare an async *clone-move* event handler that is only called once.
467///
468/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
469/// the input is the same syntax.
470///
471/// # Examples
472///
473/// The example captures `data` by move and then moves it again to another thread. This is not something you can do using [`async_hn!`]
474/// because that handler expects to be called many times. We expect `on_open` to only be called once, so we can don't need to capture by
475/// *clone-move* here just to use `data`.
476///
477/// ```
478/// # macro_rules! example { () => {
479/// let data = vec![1, 2, 3];
480/// # let
481/// on_open = async_hn_once!(|_| {
482/// task::run(async move {
483/// for i in data {
484/// print!("{i}, ");
485/// }
486/// })
487/// .await;
488///
489/// println!("Done!");
490/// });
491/// # }}
492/// ```
493///
494/// You can still *clone-move* to have access to the variable after creating the handler, in this case the `data` will be cloned into the handler
495/// but will just be moved to the other thread, avoiding a needless clone.
496///
497/// ```
498/// # macro_rules! example { () => {
499/// let data = vec![1, 2, 3];
500/// # let
501/// on_open = async_hn_once!(data, |_| {
502/// task::run(async move {
503/// for i in data {
504/// print!("{i}, ");
505/// }
506/// })
507/// .await;
508///
509/// println!("Done!");
510/// });
511/// println!("{data:?}");
512/// # }}
513/// ```
514///
515/// [`async_clmv_fn_once!`]: zng_clone_move::async_clmv_fn_once
516#[macro_export]
517macro_rules! async_hn_once {
518 ($($clmv:ident,)* |_| $body:expr) => {
519 {
520 let mut once: Option<std::boxed::Box<dyn FnOnce() -> std::pin::Pin<std::boxed::Box<dyn Future<Output = ()> + Send + 'static>> + Send + 'static>>
521 = Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* || {
522 $crate::handler::APP_HANDLER.unsubscribe();
523 std::boxed::Box::pin($crate::handler::async_clmv!($($clmv,)* { $body }))
524 })));
525
526 std::boxed::Box::new(move |_| if let Some(f) = once.take() {
527 $crate::handler::HandlerResult::Continue(f())
528 } else {
529 $crate::handler::HandlerResult::Done
530 })
531 }
532 };
533 ($($clmv:ident,)* |$args:ident| $body:expr) => {
534 {
535 let mut once: Option<std::boxed::Box<dyn FnOnce(&_) -> std::pin::Pin<std::boxed::Box<dyn Future<Output = ()> + Send + 'static>> + Send + 'static>>
536 = Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &_| {
537 $crate::handler::APP_HANDLER.unsubscribe();
538 std::boxed::Box::pin($crate::handler::async_clmv!($args, $($clmv,)* { $body }))
539 })));
540
541 std::boxed::Box::new(move |$args: &_| if let Some(f) = once.take() {
542 $crate::handler::HandlerResult::Continue(f($args))
543 } else {
544 $crate::handler::HandlerResult::Done
545 })
546 }
547 };
548 ($($clmv:ident,)* |$args:ident : & $Args:ty| $body:expr) => {
549 {
550 let mut once: Option<std::boxed::Box<dyn FnOnce(&$Args) -> std::pin::Pin<std::boxed::Box<dyn Future<Output = ()> + Send + 'static>> + Send + 'static>>
551 = Some(std::boxed::Box::new($crate::handler::clmv!($($clmv,)* |$args: &$Args| {
552 $crate::handler::APP_HANDLER.unsubscribe();
553 std::boxed::Box::pin($crate::handler::async_clmv!($args, $($clmv,)* { $body }))
554 })));
555
556 std::boxed::Box::new(move |$args: &$Args| if let Some(f) = once.take() {
557 $crate::handler::HandlerResult::Continue(f($args))
558 } else {
559 $crate::handler::HandlerResult::Done
560 })
561 }
562 };
563}
564#[doc(inline)]
565pub use crate::async_hn_once;
566
567/// Represents a weak handle to a [`Handler`] subscription in the app context.
568///
569/// Inside the handler use [`APP_HANDLER`] to access this handle.
570pub trait AppWeakHandle: Send + Sync + 'static {
571 /// Dynamic clone.
572 fn clone_boxed(&self) -> Box<dyn AppWeakHandle>;
573
574 /// Unsubscribes the [`Handler`].
575 ///
576 /// This stops the handler from being called again and causes it to be dropped in a future app update.
577 fn unsubscribe(&self);
578}
579impl<D: Send + Sync + 'static> AppWeakHandle for WeakHandle<D> {
580 fn clone_boxed(&self) -> Box<dyn AppWeakHandle> {
581 Box::new(self.clone())
582 }
583
584 fn unsubscribe(&self) {
585 if let Some(handle) = self.upgrade() {
586 handle.force_drop();
587 }
588 }
589}
590
591/// Service available in app scoped [`Handler<A>`] calls.
592#[allow(non_camel_case_types)]
593pub struct APP_HANDLER;
594
595impl APP_HANDLER {
596 /// Acquire a weak reference to the event subscription handle if the handler is being called in the app scope.
597 pub fn weak_handle(&self) -> Option<Box<dyn AppWeakHandle>> {
598 if let Some(ctx) = &*APP_HANDLER_CTX.get() {
599 Some(ctx.handle.clone_boxed())
600 } else {
601 None
602 }
603 }
604
605 /// Unsubscribe, if the handler is being called in the app scope.
606 pub fn unsubscribe(&self) {
607 if let Some(h) = self.weak_handle() {
608 h.unsubscribe();
609 }
610 }
611
612 /// If the handler is being called in the *preview* track.
613 pub fn is_preview(&self) -> bool {
614 if let Some(ctx) = &*APP_HANDLER_CTX.get() {
615 ctx.is_preview
616 } else {
617 false
618 }
619 }
620
621 /// Calls `f` with the `handle` and `is_preview` values in context.
622 pub fn with<R>(&self, handle: Box<dyn AppWeakHandle>, is_preview: bool, f: impl FnOnce() -> R) -> R {
623 APP_HANDLER_CTX.with_context(&mut Some(Arc::new(Some(AppHandlerCtx { handle, is_preview }))), f)
624 }
625}
626zng_app_context::context_local! {
627 static APP_HANDLER_CTX: Option<AppHandlerCtx> = None;
628}
629
630struct AppHandlerCtx {
631 handle: Box<dyn AppWeakHandle>,
632 is_preview: bool,
633}
634
635impl HeadlessApp {
636 /// Calls a [`Handler<A>`] once and blocks until the update tasks started during the call complete.
637 ///
638 /// This function *spins* until all update tasks are completed. Timers or send events can
639 /// be received during execution but the loop does not sleep, it just spins requesting an update
640 /// for each pass.
641 pub fn block_on<A>(&mut self, handler: &mut Handler<A>, args: &A, timeout: Duration) -> Result<(), String>
642 where
643 A: Clone + 'static,
644 {
645 self.block_on_multi(vec![handler], args, timeout)
646 }
647
648 /// Calls multiple [`Handler<A>`] once each and blocks until all update tasks are complete.
649 ///
650 /// This function *spins* until all update tasks are completed. Timers or send events can
651 /// be received during execution but the loop does not sleep, it just spins requesting an update
652 /// for each pass.
653 pub fn block_on_multi<A>(&mut self, handlers: Vec<&mut Handler<A>>, args: &A, timeout: Duration) -> Result<(), String>
654 where
655 A: Clone + 'static,
656 {
657 let (pre_len, pos_len) = UPDATES.handler_lens();
658
659 let handle = Handle::dummy(()).downgrade();
660 for handler in handlers {
661 handler.app_event(handle.clone_boxed(), false, args);
662 }
663
664 let mut pending = UPDATES.new_update_handlers(pre_len, pos_len);
665
666 if !pending.is_empty() {
667 let start_time = INSTANT.now();
668 while {
669 pending.retain(|h| h());
670 !pending.is_empty()
671 } {
672 UPDATES.update(None);
673 let flow = self.update(false);
674 if INSTANT.now().duration_since(start_time) >= timeout {
675 return Err(format!(
676 "block_on reached timeout of {timeout:?} before the handler task could finish",
677 ));
678 }
679
680 match flow {
681 AppControlFlow::Poll => continue,
682 AppControlFlow::Wait => {
683 thread::yield_now();
684 continue;
685 }
686 AppControlFlow::Exit => return Ok(()),
687 }
688 }
689 }
690
691 Ok(())
692 }
693
694 /// Polls a `future` and updates the app repeatedly until it completes or the `timeout` is reached.
695 pub fn block_on_fut<F: Future>(&mut self, future: F, timeout: Duration) -> Result<F::Output, String> {
696 let future = task::with_deadline(future, timeout);
697 let mut future = std::pin::pin!(future);
698
699 let waker = UPDATES.waker(None);
700 let mut cx = std::task::Context::from_waker(&waker);
701
702 loop {
703 let mut fut_poll = future.as_mut().poll(&mut cx);
704 let flow = self.update_observe(
705 || {
706 if fut_poll.is_pending() {
707 fut_poll = future.as_mut().poll(&mut cx);
708 }
709 },
710 true,
711 );
712
713 match fut_poll {
714 std::task::Poll::Ready(r) => match r {
715 Ok(r) => return Ok(r),
716 Err(e) => return Err(e.to_string()),
717 },
718 std::task::Poll::Pending => {}
719 }
720
721 match flow {
722 AppControlFlow::Poll => continue,
723 AppControlFlow::Wait => {
724 thread::yield_now();
725 continue;
726 }
727 AppControlFlow::Exit => return Err("app exited".to_owned()),
728 }
729 }
730 }
731
732 /// Calls the `handler` once and [`block_on`] it with a 60 seconds timeout using the minimal headless app.
733 ///
734 /// [`block_on`]: Self::block_on
735 #[track_caller]
736 #[cfg(any(test, doc, feature = "test_util"))]
737 pub fn doc_test<A, H>(args: A, mut handler: Handler<A>)
738 where
739 A: Clone + 'static,
740 {
741 let mut app = crate::APP.minimal().run_headless(false);
742 app.block_on(&mut handler, &args, DOC_TEST_BLOCK_ON_TIMEOUT).unwrap();
743 }
744
745 /// Calls the `handlers` once each and [`block_on_multi`] with a 60 seconds timeout.
746 ///
747 /// [`block_on_multi`]: Self::block_on_multi
748 #[track_caller]
749 #[cfg(any(test, doc, feature = "test_util"))]
750 pub fn doc_test_multi<A>(args: A, mut handlers: Vec<Handler<A>>)
751 where
752 A: Clone + 'static,
753 {
754 let mut app = crate::APP.minimal().run_headless(false);
755 app.block_on_multi(handlers.iter_mut().collect(), &args, DOC_TEST_BLOCK_ON_TIMEOUT)
756 .unwrap()
757 }
758}
759
760#[cfg(any(test, doc, feature = "test_util"))]
761const DOC_TEST_BLOCK_ON_TIMEOUT: Duration = Duration::from_secs(60);
762
763#[cfg(test)]
764mod tests {
765 use crate::handler::{Handler, async_hn, async_hn_once, hn, hn_once};
766
767 #[test]
768 fn hn_return() {
769 t(hn!(|args| {
770 if args.field {
771 return;
772 }
773 println!("else");
774 }))
775 }
776
777 #[test]
778 fn hn_once_return() {
779 t(hn_once!(|args: &TestArgs| {
780 if args.field {
781 return;
782 }
783 println!("else");
784 }))
785 }
786
787 #[test]
788 fn async_hn_return() {
789 t(async_hn!(|args| {
790 if args.field {
791 return;
792 }
793 args.task().await;
794 }))
795 }
796
797 #[test]
798 fn async_hn_once_return() {
799 t(async_hn_once!(|args: &TestArgs| {
800 if args.field {
801 return;
802 }
803 args.task().await;
804 }))
805 }
806
807 fn t(_: Handler<TestArgs>) {}
808
809 #[derive(Clone, Default)]
810 struct TestArgs {
811 pub field: bool,
812 }
813
814 impl TestArgs {
815 async fn task(&self) {}
816 }
817}