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}