zng_app/handler.rs
1//! Handler types and macros.
2
3use std::any::Any;
4use std::marker::PhantomData;
5use std::time::Duration;
6use std::{mem, thread};
7
8#[doc(hidden)]
9pub use zng_clone_move::*;
10
11use zng_handle::{Handle, WeakHandle};
12use zng_task::{self as task, UiTask};
13
14use crate::INSTANT;
15
16/// Represents a handler in a widget context.
17///
18/// There are different flavors of handlers, you can use macros to declare then.
19/// See [`hn!`], [`hn_once!`] or [`async_hn!`], [`async_hn_once!`] to start.
20#[diagnostic::on_unimplemented(
21 note = "use `hn!(|args: &{A}| {{ }})` to declare a widget handler from a `FnMut` closure",
22 note = "use `hn_once!`, `async_hn!` or `async_hn_once!` for other closure types"
23)]
24pub trait WidgetHandler<A: Clone + 'static>: Any + Send {
25 /// Called every time the handler's event happens in the widget context.
26 ///
27 /// Returns `true` when the event handler is async and it has not finished handling the event.
28 ///
29 /// [`update`]: WidgetHandler::update
30 /// [`info`]: crate::widget::node::UiNode::info
31 fn event(&mut self, args: &A) -> bool;
32
33 /// Called every widget update.
34 ///
35 /// Returns `false` when all pending async tasks are completed. Note that event properties
36 /// will call this method every update even if it is returning `false`.
37 ///
38 /// [`update`]: WidgetHandler::update
39 fn update(&mut self) -> bool {
40 false
41 }
42
43 /// Box the handler.
44 ///
45 /// The type `Box<dyn WidgetHandler<A>>` implements `WidgetHandler<A>` and just returns itself
46 /// in this method, avoiding double boxing.
47 fn boxed(self) -> Box<dyn WidgetHandler<A>>
48 where
49 Self: Sized,
50 {
51 Box::new(self)
52 }
53 /// Boxes the handler if the `feature = "dyn_closure"` is active, otherwise retains the same handler type.
54 #[cfg(feature = "dyn_closure")]
55 fn cfg_boxed(self) -> Box<dyn WidgetHandler<A>>
56 where
57 Self: Sized,
58 {
59 self.boxed()
60 }
61 /// Boxes the handler if the `feature = "dyn_closure"` is active, otherwise retains the same handler type.
62 #[cfg(not(feature = "dyn_closure"))]
63 fn cfg_boxed(self) -> Self
64 where
65 Self: Sized,
66 {
67 self
68 }
69}
70impl<A: Clone + 'static> WidgetHandler<A> for Box<dyn WidgetHandler<A>> {
71 fn event(&mut self, args: &A) -> bool {
72 self.as_mut().event(args)
73 }
74
75 fn update(&mut self) -> bool {
76 self.as_mut().update()
77 }
78
79 fn boxed(self) -> Box<dyn WidgetHandler<A>>
80 where
81 Self: Sized,
82 {
83 self
84 }
85}
86
87#[doc(hidden)]
88pub struct FnMutWidgetHandler<H> {
89 handler: H,
90}
91impl<A, H> WidgetHandler<A> for FnMutWidgetHandler<H>
92where
93 A: Clone + 'static,
94 H: FnMut(&A) + Send + 'static,
95{
96 fn event(&mut self, args: &A) -> bool {
97 (self.handler)(args);
98 false
99 }
100}
101
102#[doc(hidden)]
103#[cfg(not(feature = "dyn_closure"))]
104pub fn hn<A, H>(handler: H) -> FnMutWidgetHandler<H>
105where
106 A: Clone + 'static,
107 H: FnMut(&A) + Send + 'static,
108{
109 FnMutWidgetHandler { handler }
110}
111#[doc(hidden)]
112#[cfg(feature = "dyn_closure")]
113pub fn hn<A, H>(handler: H) -> FnMutWidgetHandler<Box<dyn FnMut(&A) + Send>>
114where
115 A: Clone + 'static,
116 H: FnMut(&A) + Send + 'static,
117{
118 FnMutWidgetHandler {
119 handler: Box::new(handler),
120 }
121}
122
123///<span data-del-macro-root></span> Declare a mutable *clone-move* event handler.
124///
125/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
126/// the input is the same syntax.
127///
128/// # Examples
129///
130/// The example declares an event handler for the `on_click` property.
131///
132/// ```
133/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
134/// # use zng_app::handler::hn;
135/// # let _scope = zng_app::APP.minimal();
136/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
137/// # let
138/// on_click = hn!(|_| {
139/// println!("Clicked!");
140/// });
141/// # on_click }
142/// ```
143///
144/// The closure input is `&ClickArgs` for this property. Note that
145/// if you want to use the event args you must annotate the input type, the context type is inferred.
146///
147/// ```
148/// # #[derive(Clone)] pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize }
149/// # use zng_app::handler::hn;
150/// # let _scope = zng_app::APP.minimal();
151/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
152/// # let
153/// on_click = hn!(|args: &ClickArgs| {
154/// println!("Clicked {}!", args.click_count);
155/// });
156/// # on_click }
157/// ```
158///
159/// Internally the [`clmv!`] macro is used so you can *clone-move* variables into the handler.
160///
161/// ```
162/// # #[derive(Clone)] pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize }
163/// # use zng_txt::formatx;
164/// # use zng_var::{var, Var};
165/// # use zng_app::handler::hn;
166/// # let _scope = zng_app::APP.minimal();
167/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
168/// let foo = var(0);
169///
170/// // ..
171///
172/// # let
173/// on_click = hn!(foo, |args: &ClickArgs| {
174/// foo.set(args.click_count);
175/// });
176///
177/// // can still use after:
178/// let bar = foo.map(|c| formatx!("click_count: {c}"));
179///
180/// # on_click }
181/// ```
182///
183/// 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
184/// listed in the *clone-move* section it would not be available after the handler is created. See [`clmv!`] for details.
185///
186/// [`clmv!`]: zng_clone_move::clmv
187#[macro_export]
188macro_rules! hn {
189 ($($tt:tt)+) => {
190 $crate::handler::hn($crate::handler::clmv!{ $($tt)+ })
191 }
192}
193#[doc(inline)]
194pub use crate::hn;
195use crate::{AppControlFlow, HeadlessApp};
196
197#[doc(hidden)]
198pub struct FnOnceWidgetHandler<H> {
199 handler: Option<H>,
200}
201impl<A, H> WidgetHandler<A> for FnOnceWidgetHandler<H>
202where
203 A: Clone + 'static,
204 H: FnOnce(&A) + Send + 'static,
205{
206 fn event(&mut self, args: &A) -> bool {
207 if let Some(handler) = self.handler.take() {
208 handler(args);
209 }
210 false
211 }
212}
213#[doc(hidden)]
214#[cfg(not(feature = "dyn_closure"))]
215pub fn hn_once<A, H>(handler: H) -> FnOnceWidgetHandler<H>
216where
217 A: Clone + 'static,
218 H: FnOnce(&A) + Send + 'static,
219{
220 FnOnceWidgetHandler { handler: Some(handler) }
221}
222#[doc(hidden)]
223#[cfg(feature = "dyn_closure")]
224pub fn hn_once<A, H>(handler: H) -> FnOnceWidgetHandler<Box<dyn FnOnce(&A) + Send>>
225where
226 A: Clone + 'static,
227 H: FnOnce(&A) + Send + 'static,
228{
229 FnOnceWidgetHandler {
230 handler: Some(Box::new(handler)),
231 }
232}
233
234///<span data-del-macro-root></span> Declare a *clone-move* event handler that is only called once.
235///
236/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
237/// the input is the same syntax.
238///
239/// # Examples
240///
241/// The example captures `data` by move and then destroys it in the first call, this cannot be done using [`hn!`] because
242/// the `data` needs to be available for all event calls. In this case the closure is only called once, subsequent events
243/// are ignored by the handler.
244///
245/// ```
246/// # use zng_app::handler::hn_once;
247/// # let _scope = zng_app::APP.minimal();
248/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<()> {
249/// let data = vec![1, 2, 3];
250/// # let
251/// on_click = hn_once!(|_| {
252/// for i in data {
253/// print!("{i}, ");
254/// }
255/// });
256/// # on_click }
257/// ```
258///
259/// Other then declaring a `FnOnce` this macro behaves like [`hn!`], so the same considerations apply. You can *clone-move* variables,
260/// the type of the input is the event arguments and must be annotated.
261///
262/// ```
263/// # use zng_app::handler::hn_once;
264/// # let _scope = zng_app::APP.minimal();
265/// # #[derive(Clone)]
266/// # pub struct ClickArgs { click_count: usize }
267/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
268/// let data = vec![1, 2, 3];
269/// # let
270/// on_click = hn_once!(data, |args: &ClickArgs| {
271/// drop(data);
272/// });
273///
274/// println!("{data:?}");
275/// # on_click }
276/// ```
277///
278/// [`clmv!`]: zng_clone_move::clmv
279#[macro_export]
280macro_rules! hn_once {
281 ($($tt:tt)+) => {
282 $crate::handler::hn_once($crate::handler::clmv! { $($tt)+ })
283 }
284}
285#[doc(inline)]
286pub use crate::hn_once;
287
288#[doc(hidden)]
289pub struct AsyncFnMutWidgetHandler<H> {
290 handler: H,
291 tasks: Vec<UiTask<()>>,
292}
293impl<A, F, H> WidgetHandler<A> for AsyncFnMutWidgetHandler<H>
294where
295 A: Clone + 'static,
296 F: Future<Output = ()> + Send + 'static,
297 H: FnMut(A) -> F + Send + 'static,
298{
299 fn event(&mut self, args: &A) -> bool {
300 let handler = &mut self.handler;
301 let mut task = UiTask::new(Some(WIDGET.id()), handler(args.clone()));
302 let need_update = task.update().is_none();
303 if need_update {
304 self.tasks.push(task);
305 }
306 need_update
307 }
308
309 fn update(&mut self) -> bool {
310 self.tasks.retain_mut(|t| t.update().is_none());
311 !self.tasks.is_empty()
312 }
313}
314#[doc(hidden)]
315#[cfg(not(feature = "dyn_closure"))]
316pub fn async_hn<A, F, H>(handler: H) -> AsyncFnMutWidgetHandler<H>
317where
318 A: Clone + 'static,
319 F: Future<Output = ()> + Send + 'static,
320 H: FnMut(A) -> F + Send + 'static,
321{
322 AsyncFnMutWidgetHandler { handler, tasks: vec![] }
323}
324
325#[cfg(feature = "dyn_closure")]
326type BoxedAsyncHn<A> = Box<dyn FnMut(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
327
328#[doc(hidden)]
329#[cfg(feature = "dyn_closure")]
330pub fn async_hn<A, F, H>(mut handler: H) -> AsyncFnMutWidgetHandler<BoxedAsyncHn<A>>
331where
332 A: Clone + 'static,
333 F: Future<Output = ()> + Send + 'static,
334 H: FnMut(A) -> F + Send + 'static,
335{
336 AsyncFnMutWidgetHandler {
337 handler: Box::new(move |args| Box::pin(handler(args))),
338 tasks: vec![],
339 }
340}
341
342///<span data-del-macro-root></span> Declare an async *clone-move* event handler.
343///
344/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn!`] so
345/// the input is the same syntax.
346///
347/// # Examples
348///
349/// The example declares an async event handler for the `on_click` property.
350///
351/// ```
352/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
353/// # use zng_app::handler::async_hn;
354/// # use zng_task as task;
355/// # let _scope = zng_app::APP.minimal();
356/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
357/// # let
358/// on_click = async_hn!(|_| {
359/// println!("Clicked!");
360///
361/// task::run(async {
362/// println!("In other thread!");
363/// }).await;
364///
365/// println!("Back in UI thread, in a widget update.");
366/// });
367/// # on_click }
368/// ```
369///
370/// The closure input is `ClickArgs` for this property. Note that
371/// if you want to use the event args you must annotate the input type.
372///
373/// ```
374/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
375/// # use zng_app::handler::async_hn;
376/// # use zng_app::widget::WIDGET;
377/// # let _scope = zng_app::APP.minimal();
378/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
379/// # let
380/// on_click = async_hn!(|args: ClickArgs| {
381/// println!("Clicked {} {} times!", WIDGET.id(), args.click_count);
382///
383/// });
384/// # on_click }
385/// ```
386///
387/// Internally the [`async_clmv_fn!`] macro is used so you can *clone-move* variables into the handler.
388///
389/// ```
390/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
391/// # use zng_app::handler::async_hn;
392/// # use zng_var::{var, Var};
393/// # use zng_task as task;
394/// # use zng_txt::formatx;
395/// # let _scope = zng_app::APP.minimal();
396/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
397/// let enabled = var(true);
398///
399/// // ..
400///
401/// # let
402/// on_click = async_hn!(enabled, |args: ClickArgs| {
403/// enabled.set(false);
404///
405/// task::run(async move {
406/// println!("do something {}", args.click_count);
407/// }).await;
408///
409/// enabled.set(true);
410/// });
411///
412/// // can still use after:
413/// # let
414/// text = enabled.map(|&e| if e { "Click Me!" } else { "Busy.." });
415/// enabled;
416///
417/// # on_click }
418/// ```
419///
420/// 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
421/// listed in the *clone-move* section it would not be available after the handler is created. See [`async_clmv_fn!`] for details.
422///
423/// The example also demonstrates a common pattern with async handlers, most events are only raised when the widget is enabled, so you can
424/// 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
425/// task while the first is still running.
426///
427/// ## Futures and Clone-Move
428///
429/// You want to always *clone-move* captures for async handlers, because they then automatically get cloned again for each event. This
430/// 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.
431///
432/// This second cloning can be avoided by using the [`async_hn_once!`] macro instead, but only if you expect a single event.
433///
434/// Note that this means you are declaring a normal closure that returns a `'static` future, not an async closure, see [`async_clmv_fn!`].
435///
436/// [`async_clmv_fn!`]: zng_clone_move::async_clmv_fn
437#[macro_export]
438macro_rules! async_hn {
439 ($($tt:tt)+) => {
440 $crate::handler::async_hn($crate::handler::async_clmv_fn! { $($tt)+ })
441 }
442}
443#[doc(inline)]
444pub use crate::async_hn;
445
446enum AsyncFnOnceWhState<H> {
447 NotCalled(H),
448 Pending(UiTask<()>),
449 Done,
450}
451#[doc(hidden)]
452pub struct AsyncFnOnceWidgetHandler<H> {
453 state: AsyncFnOnceWhState<H>,
454}
455impl<A, F, H> WidgetHandler<A> for AsyncFnOnceWidgetHandler<H>
456where
457 A: Clone + 'static,
458 F: Future<Output = ()> + Send + 'static,
459 H: FnOnce(A) -> F + Send + 'static,
460{
461 fn event(&mut self, args: &A) -> bool {
462 match mem::replace(&mut self.state, AsyncFnOnceWhState::Done) {
463 AsyncFnOnceWhState::NotCalled(handler) => {
464 let mut task = UiTask::new(Some(WIDGET.id()), handler(args.clone()));
465 let is_pending = task.update().is_none();
466 if is_pending {
467 self.state = AsyncFnOnceWhState::Pending(task);
468 }
469 is_pending
470 }
471 AsyncFnOnceWhState::Pending(t) => {
472 self.state = AsyncFnOnceWhState::Pending(t);
473 false
474 }
475 AsyncFnOnceWhState::Done => false,
476 }
477 }
478
479 fn update(&mut self) -> bool {
480 let mut is_pending = false;
481 if let AsyncFnOnceWhState::Pending(t) = &mut self.state {
482 is_pending = t.update().is_none();
483 if !is_pending {
484 self.state = AsyncFnOnceWhState::Done;
485 }
486 }
487 is_pending
488 }
489}
490#[doc(hidden)]
491#[cfg(not(feature = "dyn_closure"))]
492pub fn async_hn_once<A, F, H>(handler: H) -> AsyncFnOnceWidgetHandler<H>
493where
494 A: Clone + 'static,
495 F: Future<Output = ()> + Send + 'static,
496 H: FnOnce(A) -> F + Send + 'static,
497{
498 AsyncFnOnceWidgetHandler {
499 state: AsyncFnOnceWhState::NotCalled(handler),
500 }
501}
502
503#[cfg(feature = "dyn_closure")]
504type BoxedAsyncHnOnce<A> = Box<dyn FnOnce(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
505
506#[doc(hidden)]
507#[cfg(feature = "dyn_closure")]
508pub fn async_hn_once<A, F, H>(handler: H) -> AsyncFnOnceWidgetHandler<BoxedAsyncHnOnce<A>>
509where
510 A: Clone + 'static,
511 F: Future<Output = ()> + Send + 'static,
512 H: FnOnce(A) -> F + Send + 'static,
513{
514 AsyncFnOnceWidgetHandler {
515 state: AsyncFnOnceWhState::NotCalled(Box::new(move |args| Box::pin(handler(args)))),
516 }
517}
518
519///<span data-del-macro-root></span> Declare an async *clone-move* event handler that is only called once.
520///
521/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn_once!`] so
522/// the input is the same syntax.
523///
524/// # Examples
525///
526/// The example captures `data` by move and then moves it again to another thread. This is not something you can do using [`async_hn!`]
527/// 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
528/// *clone-move* here just to use `data`.
529///
530/// ```
531/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
532/// # use zng_app::handler::async_hn_once;
533/// # use zng_task as task;
534/// # let _scope = zng_app::APP.minimal();
535/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
536/// let data = vec![1, 2, 3];
537/// # let
538/// on_open = async_hn_once!(|_| {
539/// task::run(async move {
540/// for i in data {
541/// print!("{i}, ");
542/// }
543/// }).await;
544///
545/// println!("Done!");
546/// });
547/// # on_open }
548/// ```
549///
550/// 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
551/// but will just be moved to the other thread, avoiding a needless clone.
552///
553/// ```
554/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
555/// # use zng_app::handler::async_hn_once;
556/// # use zng_task as task;
557/// # let _scope = zng_app::APP.minimal();
558/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
559/// let data = vec![1, 2, 3];
560/// # let
561/// on_open = async_hn_once!(data, |_| {
562/// task::run(async move {
563/// for i in data {
564/// print!("{i}, ");
565/// }
566/// }).await;
567///
568/// println!("Done!");
569/// });
570/// println!("{data:?}");
571/// # on_open }
572/// ```
573///
574/// [`async_clmv_fn_once!`]: zng_clone_move::async_clmv_fn_once
575#[macro_export]
576macro_rules! async_hn_once {
577 ($($tt:tt)+) => {
578 $crate::handler::async_hn_once($crate::handler::async_clmv_fn_once! { $($tt)+ })
579 }
580}
581#[doc(inline)]
582pub use crate::async_hn_once;
583
584/// Represents a weak handle to an [`AppHandler`] subscription.
585pub trait AppWeakHandle: Send {
586 /// Dynamic clone.
587 fn clone_boxed(&self) -> Box<dyn AppWeakHandle>;
588
589 /// Unsubscribes the [`AppHandler`].
590 ///
591 /// This stops the handler from being called again and causes it to be dropped in a future app update.
592 fn unsubscribe(&self);
593}
594impl<D: Send + Sync + 'static> AppWeakHandle for WeakHandle<D> {
595 fn clone_boxed(&self) -> Box<dyn AppWeakHandle> {
596 Box::new(self.clone())
597 }
598
599 fn unsubscribe(&self) {
600 if let Some(handle) = self.upgrade() {
601 handle.force_drop();
602 }
603 }
604}
605
606/// Arguments for a call of [`AppHandler::event`].
607#[non_exhaustive]
608pub struct AppHandlerArgs<'a> {
609 /// Handle to the [`AppHandler`] subscription.
610 pub handle: &'a dyn AppWeakHandle,
611 /// If the handler is invoked in a *preview* context.
612 pub is_preview: bool,
613}
614
615/// Represents an event handler in the app context.
616///
617/// There are different flavors of handlers, you can use macros to declare then.
618/// See [`app_hn!`], [`app_hn_once!`] or [`async_app_hn!`], [`async_app_hn_once!`] to start.
619#[diagnostic::on_unimplemented(
620 note = "use `app_hn!(|args: &{A}, _| {{ }})` to declare an app handler closure",
621 note = "use `app_hn_once!`, `async_app_hn!` or `async_app_hn_once!` for other closure types"
622)]
623pub trait AppHandler<A: Clone + 'static>: Any + Send {
624 /// Called every time the event happens.
625 ///
626 /// The `handler_args` can be used to unsubscribe the handler. Async handlers are expected to schedule
627 /// their tasks to run somewhere in the app, usually in the [`UPDATES.on_update`]. The `handle` is
628 /// **not** expected to cancel running async tasks, only to drop `self` before the next event happens.
629 ///
630 /// [`UPDATES.on_update`]: crate::update::UPDATES::on_update
631 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs);
632
633 /// Boxes the handler.
634 ///
635 /// The type `Box<dyn AppHandler<A>>` implements `AppHandler<A>` and just returns itself
636 /// in this method, avoiding double boxing.
637 fn boxed(self) -> Box<dyn AppHandler<A>>
638 where
639 Self: Sized,
640 {
641 Box::new(self)
642 }
643
644 /// Boxes the handler if the `feature = "dyn_closure"` is enabled, otherwise maintain the same type.
645 #[cfg(feature = "dyn_closure")]
646 fn cfg_boxed(self) -> Box<dyn AppHandler<A>>
647 where
648 Self: Sized,
649 {
650 self.boxed()
651 }
652
653 /// Boxes the handler if the `feature = "dyn_closure"` is enabled, otherwise maintain the same type.
654 #[cfg(not(feature = "dyn_closure"))]
655 fn cfg_boxed(self) -> Self
656 where
657 Self: Sized,
658 {
659 self
660 }
661}
662impl<A: Clone + 'static> AppHandler<A> for Box<dyn AppHandler<A>> {
663 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
664 self.as_mut().event(args, handler_args)
665 }
666
667 fn boxed(self) -> Box<dyn AppHandler<A>> {
668 self
669 }
670}
671
672#[doc(hidden)]
673pub struct FnMutAppHandler<H> {
674 handler: H,
675}
676impl<A, H> AppHandler<A> for FnMutAppHandler<H>
677where
678 A: Clone + 'static,
679 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
680{
681 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
682 (self.handler)(args, handler_args.handle);
683 }
684}
685#[doc(hidden)]
686#[cfg(not(feature = "dyn_closure"))]
687pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<H>
688where
689 A: Clone + 'static,
690 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
691{
692 FnMutAppHandler { handler }
693}
694
695#[cfg(feature = "dyn_closure")]
696type BoxedAppHn<A> = Box<dyn FnMut(&A, &dyn AppWeakHandle) + Send>;
697
698#[doc(hidden)]
699#[cfg(feature = "dyn_closure")]
700pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<BoxedAppHn<A>>
701where
702 A: Clone + 'static,
703 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
704{
705 FnMutAppHandler {
706 handler: Box::new(handler),
707 }
708}
709
710///<span data-del-macro-root></span> Declare a mutable *clone-move* app event handler.
711///
712/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
713/// the input is the same syntax.
714///
715/// # Examples
716///
717/// The example declares an event handler for the `CLICK_EVENT`.
718///
719/// ```
720/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
721/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
722/// # use zng_app::handler::app_hn;
723/// # let _scope = zng_app::APP.minimal();
724/// # fn assert_type() {
725/// CLICK_EVENT.on_event(app_hn!(|_, _| {
726/// println!("Clicked Somewhere!");
727/// })).perm();
728/// # }
729/// ```
730///
731/// The closure input is `&A, &dyn AppWeakHandle` with `&A` equaling `&ClickArgs` for this event. Note that
732/// if you want to use the event args you must annotate the input type, the context and handle type is inferred.
733///
734/// The handle can be used to unsubscribe the event handler, if [`unsubscribe`](AppWeakHandle::unsubscribe) is called the handler
735/// will be dropped some time before the next event update.
736///
737/// ```
738/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
739/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
740/// # use zng_app::handler::app_hn;
741/// # let _scope = zng_app::APP.minimal();
742/// # fn assert_type() {
743/// CLICK_EVENT.on_event(app_hn!(|args: &ClickArgs, handle| {
744/// println!("Clicked {}!", args.target);
745/// handle.unsubscribe();
746/// })).perm();
747/// # }
748/// ```
749///
750/// Internally the [`clmv!`] macro is used so you can *clone-move* variables into the handler.
751///
752/// ```
753/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
754/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
755/// # use zng_txt::{formatx, ToTxt};
756/// # use zng_var::{var, Var};
757/// # use zng_app::handler::app_hn;
758/// # let _scope = zng_app::APP.minimal();
759/// # fn assert_type() {
760/// let foo = var("".to_txt());
761///
762/// CLICK_EVENT.on_event(app_hn!(foo, |args: &ClickArgs, _| {
763/// foo.set(args.target.to_txt());
764/// })).perm();
765///
766/// // can still use after:
767/// let bar = foo.map(|c| formatx!("last click: {c}"));
768///
769/// # }
770/// ```
771///
772/// 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
773/// listed in the *clone-move* section it would not be available after the handler is created. See [`clmv!`] for details.
774///
775/// [`clmv!`]: zng_clone_move::clmv
776#[macro_export]
777macro_rules! app_hn {
778 ($($tt:tt)+) => {
779 $crate::handler::app_hn($crate::handler::clmv!{ $($tt)+ })
780 }
781}
782#[doc(inline)]
783pub use crate::app_hn;
784
785#[doc(hidden)]
786pub struct FnOnceAppHandler<H> {
787 handler: Option<H>,
788}
789impl<A, H> AppHandler<A> for FnOnceAppHandler<H>
790where
791 A: Clone + 'static,
792 H: FnOnce(&A) + Send + 'static,
793{
794 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
795 if let Some(handler) = self.handler.take() {
796 handler(args);
797 handler_args.handle.unsubscribe();
798 } else {
799 tracing::error!("`app_hn_once!` called after requesting unsubscribe");
800 }
801 }
802}
803#[doc(hidden)]
804#[cfg(not(feature = "dyn_closure"))]
805pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<H>
806where
807 A: Clone + 'static,
808 H: FnOnce(&A) + Send + 'static,
809{
810 FnOnceAppHandler { handler: Some(handler) }
811}
812#[doc(hidden)]
813#[cfg(feature = "dyn_closure")]
814pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<Box<dyn FnOnce(&A) + Send>>
815where
816 A: Clone + 'static,
817 H: FnOnce(&A) + Send + 'static,
818{
819 FnOnceAppHandler {
820 handler: Some(Box::new(handler)),
821 }
822}
823
824///<span data-del-macro-root></span> Declare a *clone-move* app event handler that is only called once.
825///
826/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
827/// the input is the same syntax.
828///
829/// # Examples
830///
831/// The example captures `data` by move and then destroys it in the first call, this cannot be done using [`app_hn!`] because
832/// the `data` needs to be available for all event calls. In this case the closure is only called once, subsequent events
833/// are ignored by the handler and it automatically requests unsubscribe.
834///
835/// ```
836/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
837/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
838/// # use zng_app::handler::app_hn_once;
839/// # let _scope = zng_app::APP.minimal();
840/// # fn assert_type() {
841/// let data = vec![1, 2, 3];
842///
843/// CLICK_EVENT.on_event(app_hn_once!(|_| {
844/// for i in data {
845/// print!("{i}, ");
846/// }
847/// })).perm();
848/// # }
849/// ```
850///
851/// Other then declaring a `FnOnce` this macro behaves like [`app_hn!`], so the same considerations apply. You can *clone-move* variables,
852/// the type of the input is the event arguments and must be annotated.
853///
854/// ```
855/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
856/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
857/// # use zng_app::handler::app_hn_once;
858/// # let _scope = zng_app::APP.minimal();
859/// # fn assert_type() {
860/// let data = vec![1, 2, 3];
861///
862/// CLICK_EVENT.on_event(app_hn_once!(data, |args: &ClickArgs| {
863/// drop(data);
864/// })).perm();
865///
866/// println!("{data:?}");
867/// # }
868/// ```
869///
870/// [`clmv!`]: zng_clone_move::clmv
871#[macro_export]
872macro_rules! app_hn_once {
873 ($($tt:tt)+) => {
874 $crate::handler::app_hn_once($crate::handler::clmv! { $($tt)+ })
875 }
876}
877#[doc(inline)]
878pub use crate::app_hn_once;
879
880#[doc(hidden)]
881pub struct AsyncFnMutAppHandler<H> {
882 handler: H,
883}
884impl<A, F, H> AppHandler<A> for AsyncFnMutAppHandler<H>
885where
886 A: Clone + 'static,
887 F: Future<Output = ()> + Send + 'static,
888 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
889{
890 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
891 let handler = &mut self.handler;
892 let mut task = UiTask::new(None, handler(args.clone(), handler_args.handle.clone_boxed()));
893 if task.update().is_none() {
894 if handler_args.is_preview {
895 UPDATES
896 .on_pre_update(app_hn!(|_, handle| {
897 if task.update().is_some() {
898 handle.unsubscribe();
899 }
900 }))
901 .perm();
902 } else {
903 UPDATES
904 .on_update(app_hn!(|_, handle| {
905 if task.update().is_some() {
906 handle.unsubscribe();
907 }
908 }))
909 .perm();
910 }
911 }
912 }
913}
914#[doc(hidden)]
915#[cfg(not(feature = "dyn_closure"))]
916pub fn async_app_hn<A, F, H>(handler: H) -> AsyncFnMutAppHandler<H>
917where
918 A: Clone + 'static,
919 F: Future<Output = ()> + Send + 'static,
920 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
921{
922 AsyncFnMutAppHandler { handler }
923}
924
925#[cfg(feature = "dyn_closure")]
926type BoxedAsyncAppHn<A> = Box<dyn FnMut(A, Box<dyn AppWeakHandle>) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
927
928#[doc(hidden)]
929#[cfg(feature = "dyn_closure")]
930pub fn async_app_hn<A, F, H>(mut handler: H) -> AsyncFnMutAppHandler<BoxedAsyncAppHn<A>>
931where
932 A: Clone + 'static,
933 F: Future<Output = ()> + Send + 'static,
934 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
935{
936 AsyncFnMutAppHandler {
937 handler: Box::new(move |args, handle| Box::pin(handler(args, handle))),
938 }
939}
940
941///<span data-del-macro-root></span> Declare an async *clone-move* app event handler.
942///
943/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn!`] so
944/// the input is the same syntax.
945///
946/// The handler generates a future for each event, the future is polled immediately if it does not finish it is scheduled
947/// to update in [`on_pre_update`](crate::update::UPDATES::on_pre_update) or [`on_update`](crate::update::UPDATES::on_update) depending
948/// on if the handler was assigned to a *preview* event or not.
949///
950/// Note that this means [`propagation`](crate::event::AnyEventArgs::propagation) can only be meaningfully stopped before the
951/// first `.await`, after, the event has already propagated.
952///
953/// # Examples
954///
955/// The example declares an async event handler for the `CLICK_EVENT`.
956///
957/// ```
958/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
959/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
960/// # use zng_app::handler::async_app_hn;
961/// # use zng_task as task;
962/// # let _scope = zng_app::APP.minimal();
963/// # fn assert_type() {
964/// CLICK_EVENT.on_event(async_app_hn!(|_, _| {
965/// println!("Clicked Somewhere!");
966///
967/// task::run(async {
968/// println!("In other thread!");
969/// }).await;
970///
971/// println!("Back in UI thread, in an app update.");
972/// })).perm();
973/// # }
974/// ```
975///
976/// The closure input is `A, Box<dyn AppWeakHandle>` for all handlers and `A` is `ClickArgs` for this example. Note that
977/// if you want to use the event args you must annotate the input type, the context and handle types are inferred.
978///
979/// The handle can be used to unsubscribe the event handler, if [`unsubscribe`](AppWeakHandle::unsubscribe) is called the handler
980/// will be dropped some time before the next event update. Running tasks are not canceled by unsubscribing, the only way to *cancel*
981/// then is by returning early inside the async blocks.
982///
983/// ```
984/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
985/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
986/// # use zng_app::handler::async_app_hn;
987/// # use zng_task as task;
988/// # let _scope = zng_app::APP.minimal();
989/// # fn assert_type() {
990/// CLICK_EVENT.on_event(async_app_hn!(|args: ClickArgs, handle| {
991/// println!("Clicked {}!", args.target);
992/// task::run(async move {
993/// handle.unsubscribe();
994/// });
995/// })).perm();
996/// # }
997/// ```
998///
999/// Internally the [`async_clmv_fn!`] macro is used so you can *clone-move* variables into the handler.
1000///
1001/// ```
1002/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1003/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
1004/// # use zng_app::handler::async_app_hn;
1005/// # use zng_var::{var, Var};
1006/// # use zng_task as task;
1007/// # use zng_txt::{formatx, ToTxt};
1008/// #
1009/// # let _scope = zng_app::APP.minimal();
1010/// # fn assert_type() {
1011/// let status = var("pending..".to_txt());
1012///
1013/// CLICK_EVENT.on_event(async_app_hn!(status, |args: ClickArgs, _| {
1014/// status.set(formatx!("processing {}..", args.target));
1015///
1016/// task::run(async move {
1017/// println!("do something slow");
1018/// }).await;
1019///
1020/// status.set(formatx!("finished {}", args.target));
1021/// })).perm();
1022///
1023/// // can still use after:
1024/// let text = status;
1025///
1026/// # }
1027/// ```
1028///
1029/// In the example above only a clone of `status` is moved into the handler. Note that handlers always capture by move, if `status` was not
1030/// listed in the *clone-move* section it would not be available after the handler is created. See [`async_clmv_fn!`] for details.
1031///
1032/// ## Futures and Clone-Move
1033///
1034/// You may want to always *clone-move* captures for async handlers, because they then automatically get cloned again for each event. This
1035/// 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.
1036///
1037/// This second cloning can be avoided by using the [`async_hn_once!`] macro instead, but only if you expect a single event.
1038///
1039/// [`async_clmv_fn!`]: zng_clone_move::async_clmv_fn
1040#[macro_export]
1041macro_rules! async_app_hn {
1042 ($($tt:tt)+) => {
1043 $crate::handler::async_app_hn($crate::handler::async_clmv_fn! { $($tt)+ })
1044 }
1045}
1046#[doc(inline)]
1047pub use crate::async_app_hn;
1048
1049#[doc(hidden)]
1050pub struct AsyncFnOnceAppHandler<H> {
1051 handler: Option<H>,
1052}
1053
1054impl<A, F, H> AppHandler<A> for AsyncFnOnceAppHandler<H>
1055where
1056 A: Clone + 'static,
1057 F: Future<Output = ()> + Send + 'static,
1058 H: FnOnce(A) -> F + Send + 'static,
1059{
1060 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
1061 if let Some(handler) = self.handler.take() {
1062 handler_args.handle.unsubscribe();
1063
1064 let mut task = UiTask::new(None, handler(args.clone()));
1065 if task.update().is_none() {
1066 if handler_args.is_preview {
1067 UPDATES
1068 .on_pre_update(app_hn!(|_, handle| {
1069 if task.update().is_some() {
1070 handle.unsubscribe();
1071 }
1072 }))
1073 .perm();
1074 } else {
1075 UPDATES
1076 .on_update(app_hn!(|_, handle| {
1077 if task.update().is_some() {
1078 handle.unsubscribe();
1079 }
1080 }))
1081 .perm();
1082 }
1083 }
1084 } else {
1085 tracing::error!("`async_app_hn_once!` called after requesting unsubscribe");
1086 }
1087 }
1088}
1089#[doc(hidden)]
1090#[cfg(not(feature = "dyn_closure"))]
1091pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<H>
1092where
1093 A: Clone + 'static,
1094 F: Future<Output = ()> + Send + 'static,
1095 H: FnOnce(A) -> F + Send + 'static,
1096{
1097 AsyncFnOnceAppHandler { handler: Some(handler) }
1098}
1099
1100#[cfg(feature = "dyn_closure")]
1101type BoxedAsyncAppHnOnce<A> = Box<dyn FnOnce(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
1102
1103#[doc(hidden)]
1104#[cfg(feature = "dyn_closure")]
1105pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<BoxedAsyncAppHnOnce<A>>
1106where
1107 A: Clone + 'static,
1108 F: Future<Output = ()> + Send + 'static,
1109 H: FnOnce(A) -> F + Send + 'static,
1110{
1111 AsyncFnOnceAppHandler {
1112 handler: Some(Box::new(move |args| Box::pin(handler(args)))),
1113 }
1114}
1115
1116///<span data-del-macro-root></span> Declare an async *clone-move* app event handler that is only called once.
1117///
1118/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn_once!`] so
1119/// the input is the same syntax.
1120///
1121/// # Examples
1122///
1123/// The example captures `data` by move and then moves it again to another thread. This is not something you can do using [`async_app_hn!`]
1124/// because that handler expects to be called many times. We want to handle `CLICK_EVENT` once in this example, so we can don't need
1125/// to capture by *clone-move* just to use `data`.
1126///
1127/// ```
1128/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1129/// # use zng_app::handler::async_hn_once;
1130/// # use zng_task as task;
1131/// # let _scope = zng_app::APP.minimal();
1132/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
1133/// let data = vec![1, 2, 3];
1134/// # let
1135/// on_open = async_hn_once!(|_| {
1136/// task::run(async move {
1137/// for i in data {
1138/// print!("{i}, ");
1139/// }
1140/// }).await;
1141///
1142/// println!("Done!");
1143/// });
1144/// # on_open }
1145/// ```
1146///
1147/// 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
1148/// but will just be moved to the other thread, avoiding a needless clone.
1149///
1150/// ```
1151/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1152/// # use zng_app::handler::async_hn_once;
1153/// # use zng_task as task;
1154/// # let _scope = zng_app::APP.minimal();
1155/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
1156/// let data = vec![1, 2, 3];
1157/// # let
1158/// on_open = async_hn_once!(data, |_| {
1159/// task::run(async move {
1160/// for i in data {
1161/// print!("{i}, ");
1162/// }
1163/// }).await;
1164///
1165/// println!("Done!");
1166/// });
1167/// println!("{data:?}");
1168/// # on_open }
1169/// ```
1170///
1171/// [`async_clmv_fn_once!`]: zng_clone_move::async_clmv_fn_once
1172#[macro_export]
1173macro_rules! async_app_hn_once {
1174 ($($tt:tt)+) => {
1175 $crate::handler::async_app_hn_once($crate::handler::async_clmv_fn_once! { $($tt)+ })
1176 }
1177}
1178#[doc(inline)]
1179pub use crate::async_app_hn_once;
1180use crate::update::UPDATES;
1181use crate::widget::{UiTaskWidget, WIDGET};
1182
1183/// Widget handler wrapper that filters the events, only delegating to `self` when `filter` returns `true`.
1184pub struct FilterWidgetHandler<A, H, F> {
1185 _args: PhantomData<fn() -> A>,
1186 handler: H,
1187 filter: F,
1188}
1189impl<A, H, F> FilterWidgetHandler<A, H, F>
1190where
1191 A: Clone + 'static,
1192 H: WidgetHandler<A>,
1193 F: FnMut(&A) -> bool + Send + 'static,
1194{
1195 /// New filter handler.
1196 pub fn new(handler: H, filter: F) -> Self {
1197 Self {
1198 handler,
1199 filter,
1200 _args: PhantomData,
1201 }
1202 }
1203}
1204impl<A, H, F> WidgetHandler<A> for FilterWidgetHandler<A, H, F>
1205where
1206 A: Clone + 'static,
1207 H: WidgetHandler<A>,
1208 F: FnMut(&A) -> bool + Send + 'static,
1209{
1210 fn event(&mut self, args: &A) -> bool {
1211 if (self.filter)(args) { self.handler.event(args) } else { false }
1212 }
1213
1214 fn update(&mut self) -> bool {
1215 self.handler.update()
1216 }
1217}
1218
1219/// App handler wrapper that filters the events, only delegating to `self` when `filter` returns `true`.
1220pub struct FilterAppHandler<A, H, F> {
1221 _args: PhantomData<fn() -> A>,
1222 handler: H,
1223 filter: F,
1224}
1225impl<A, H, F> FilterAppHandler<A, H, F>
1226where
1227 A: Clone + 'static,
1228 H: AppHandler<A>,
1229 F: FnMut(&A) -> bool + Send + 'static,
1230{
1231 /// New filter handler.
1232 pub fn new(handler: H, filter: F) -> Self {
1233 Self {
1234 handler,
1235 filter,
1236 _args: PhantomData,
1237 }
1238 }
1239}
1240impl<A, H, F> AppHandler<A> for FilterAppHandler<A, H, F>
1241where
1242 A: Clone + 'static,
1243 H: AppHandler<A>,
1244 F: FnMut(&A) -> bool + Send + 'static,
1245{
1246 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
1247 if (self.filter)(args) {
1248 self.handler.event(args, handler_args);
1249 }
1250 }
1251}
1252
1253impl HeadlessApp {
1254 /// Calls an [`AppHandler<A>`] once and blocks until the update tasks started during the call complete.
1255 ///
1256 /// This function *spins* until all update tasks are completed. Timers or send events can
1257 /// be received during execution but the loop does not sleep, it just spins requesting an update
1258 /// for each pass.
1259 pub fn block_on<A>(&mut self, handler: &mut dyn AppHandler<A>, args: &A, timeout: Duration) -> Result<(), String>
1260 where
1261 A: Clone + 'static,
1262 {
1263 self.block_on_multi(vec![handler], args, timeout)
1264 }
1265
1266 /// Calls multiple [`AppHandler<A>`] once each and blocks until all update tasks are complete.
1267 ///
1268 /// This function *spins* until all update tasks are completed. Timers or send events can
1269 /// be received during execution but the loop does not sleep, it just spins requesting an update
1270 /// for each pass.
1271 pub fn block_on_multi<A>(&mut self, handlers: Vec<&mut dyn AppHandler<A>>, args: &A, timeout: Duration) -> Result<(), String>
1272 where
1273 A: Clone + 'static,
1274 {
1275 let (pre_len, pos_len) = UPDATES.handler_lens();
1276
1277 let handler_args = AppHandlerArgs {
1278 handle: &Handle::dummy(()).downgrade(),
1279 is_preview: false,
1280 };
1281 for handler in handlers {
1282 handler.event(args, &handler_args);
1283 }
1284
1285 let mut pending = UPDATES.new_update_handlers(pre_len, pos_len);
1286
1287 if !pending.is_empty() {
1288 let start_time = INSTANT.now();
1289 while {
1290 pending.retain(|h| h());
1291 !pending.is_empty()
1292 } {
1293 UPDATES.update(None);
1294 let flow = self.update(false);
1295 if INSTANT.now().duration_since(start_time) >= timeout {
1296 return Err(format!(
1297 "block_on reached timeout of {timeout:?} before the handler task could finish",
1298 ));
1299 }
1300
1301 match flow {
1302 AppControlFlow::Poll => continue,
1303 AppControlFlow::Wait => {
1304 thread::yield_now();
1305 continue;
1306 }
1307 AppControlFlow::Exit => return Ok(()),
1308 }
1309 }
1310 }
1311
1312 Ok(())
1313 }
1314
1315 /// Polls a `future` and updates the app repeatedly until it completes or the `timeout` is reached.
1316 pub fn block_on_fut<F: Future>(&mut self, future: F, timeout: Duration) -> Result<F::Output, String> {
1317 let future = task::with_deadline(future, timeout);
1318 let mut future = std::pin::pin!(future);
1319
1320 let waker = UPDATES.waker(None);
1321 let mut cx = std::task::Context::from_waker(&waker);
1322
1323 loop {
1324 let mut fut_poll = future.as_mut().poll(&mut cx);
1325 let flow = self.update_observe(
1326 || {
1327 if fut_poll.is_pending() {
1328 fut_poll = future.as_mut().poll(&mut cx);
1329 }
1330 },
1331 true,
1332 );
1333
1334 match fut_poll {
1335 std::task::Poll::Ready(r) => match r {
1336 Ok(r) => return Ok(r),
1337 Err(e) => return Err(e.to_string()),
1338 },
1339 std::task::Poll::Pending => {}
1340 }
1341
1342 match flow {
1343 AppControlFlow::Poll => continue,
1344 AppControlFlow::Wait => {
1345 thread::yield_now();
1346 continue;
1347 }
1348 AppControlFlow::Exit => return Err("app exited".to_owned()),
1349 }
1350 }
1351 }
1352
1353 /// Calls the `handler` once and [`block_on`] it with a 60 seconds timeout using the minimal headless app.
1354 ///
1355 /// [`block_on`]: Self::block_on
1356 #[track_caller]
1357 #[cfg(any(test, doc, feature = "test_util"))]
1358 pub fn doc_test<A, H>(args: A, mut handler: H)
1359 where
1360 A: Clone + 'static,
1361 H: AppHandler<A>,
1362 {
1363 let mut app = crate::APP.minimal().run_headless(false);
1364 app.block_on(&mut handler, &args, DOC_TEST_BLOCK_ON_TIMEOUT).unwrap();
1365 }
1366
1367 /// Calls the `handlers` once each and [`block_on_multi`] with a 60 seconds timeout.
1368 ///
1369 /// [`block_on_multi`]: Self::block_on_multi
1370 #[track_caller]
1371 #[cfg(any(test, doc, feature = "test_util"))]
1372 pub fn doc_test_multi<A>(args: A, mut handlers: Vec<Box<dyn AppHandler<A>>>)
1373 where
1374 A: Clone + 'static,
1375 {
1376 let mut app = crate::APP.minimal().run_headless(false);
1377 app.block_on_multi(handlers.iter_mut().map(|h| h.as_mut()).collect(), &args, DOC_TEST_BLOCK_ON_TIMEOUT)
1378 .unwrap()
1379 }
1380}
1381
1382#[cfg(any(test, doc, feature = "test_util"))]
1383const DOC_TEST_BLOCK_ON_TIMEOUT: Duration = Duration::from_secs(60);