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`].
607pub struct AppHandlerArgs<'a> {
608 /// Handle to the [`AppHandler`] subscription.
609 pub handle: &'a dyn AppWeakHandle,
610 /// If the handler is invoked in a *preview* context.
611 pub is_preview: bool,
612}
613
614/// Represents an event handler in the app context.
615///
616/// There are different flavors of handlers, you can use macros to declare then.
617/// See [`app_hn!`], [`app_hn_once!`] or [`async_app_hn!`], [`async_app_hn_once!`] to start.
618#[diagnostic::on_unimplemented(
619 note = "use `app_hn!(|args: &{A}, _| {{ }})` to declare an app handler closure",
620 note = "use `app_hn_once!`, `async_app_hn!` or `async_app_hn_once!` for other closure types"
621)]
622pub trait AppHandler<A: Clone + 'static>: Any + Send {
623 /// Called every time the event happens.
624 ///
625 /// The `handler_args` can be used to unsubscribe the handler. Async handlers are expected to schedule
626 /// their tasks to run somewhere in the app, usually in the [`UPDATES.on_update`]. The `handle` is
627 /// **not** expected to cancel running async tasks, only to drop `self` before the next event happens.
628 ///
629 /// [`UPDATES.on_update`]: crate::update::UPDATES::on_update
630 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs);
631
632 /// Boxes the handler.
633 ///
634 /// The type `Box<dyn AppHandler<A>>` implements `AppHandler<A>` and just returns itself
635 /// in this method, avoiding double boxing.
636 fn boxed(self) -> Box<dyn AppHandler<A>>
637 where
638 Self: Sized,
639 {
640 Box::new(self)
641 }
642
643 /// Boxes the handler if the `feature = "dyn_closure"` is enabled, otherwise maintain the same type.
644 #[cfg(feature = "dyn_closure")]
645 fn cfg_boxed(self) -> Box<dyn AppHandler<A>>
646 where
647 Self: Sized,
648 {
649 self.boxed()
650 }
651
652 /// Boxes the handler if the `feature = "dyn_closure"` is enabled, otherwise maintain the same type.
653 #[cfg(not(feature = "dyn_closure"))]
654 fn cfg_boxed(self) -> Self
655 where
656 Self: Sized,
657 {
658 self
659 }
660}
661impl<A: Clone + 'static> AppHandler<A> for Box<dyn AppHandler<A>> {
662 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
663 self.as_mut().event(args, handler_args)
664 }
665
666 fn boxed(self) -> Box<dyn AppHandler<A>> {
667 self
668 }
669}
670
671#[doc(hidden)]
672pub struct FnMutAppHandler<H> {
673 handler: H,
674}
675impl<A, H> AppHandler<A> for FnMutAppHandler<H>
676where
677 A: Clone + 'static,
678 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
679{
680 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
681 (self.handler)(args, handler_args.handle);
682 }
683}
684#[doc(hidden)]
685#[cfg(not(feature = "dyn_closure"))]
686pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<H>
687where
688 A: Clone + 'static,
689 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
690{
691 FnMutAppHandler { handler }
692}
693
694#[cfg(feature = "dyn_closure")]
695type BoxedAppHn<A> = Box<dyn FnMut(&A, &dyn AppWeakHandle) + Send>;
696
697#[doc(hidden)]
698#[cfg(feature = "dyn_closure")]
699pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<BoxedAppHn<A>>
700where
701 A: Clone + 'static,
702 H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
703{
704 FnMutAppHandler {
705 handler: Box::new(handler),
706 }
707}
708
709///<span data-del-macro-root></span> Declare a mutable *clone-move* app event handler.
710///
711/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
712/// the input is the same syntax.
713///
714/// # Examples
715///
716/// The example declares an event handler for the `CLICK_EVENT`.
717///
718/// ```
719/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
720/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
721/// # use zng_app::handler::app_hn;
722/// # let _scope = zng_app::APP.minimal();
723/// # fn assert_type() {
724/// CLICK_EVENT.on_event(app_hn!(|_, _| {
725/// println!("Clicked Somewhere!");
726/// })).perm();
727/// # }
728/// ```
729///
730/// The closure input is `&A, &dyn AppWeakHandle` with `&A` equaling `&ClickArgs` for this event. Note that
731/// if you want to use the event args you must annotate the input type, the context and handle type is inferred.
732///
733/// The handle can be used to unsubscribe the event handler, if [`unsubscribe`](AppWeakHandle::unsubscribe) is called the handler
734/// will be dropped some time before the next event update.
735///
736/// ```
737/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
738/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
739/// # use zng_app::handler::app_hn;
740/// # let _scope = zng_app::APP.minimal();
741/// # fn assert_type() {
742/// CLICK_EVENT.on_event(app_hn!(|args: &ClickArgs, handle| {
743/// println!("Clicked {}!", args.target);
744/// handle.unsubscribe();
745/// })).perm();
746/// # }
747/// ```
748///
749/// Internally the [`clmv!`] macro is used so you can *clone-move* variables into the handler.
750///
751/// ```
752/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
753/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
754/// # use zng_txt::{formatx, ToTxt};
755/// # use zng_var::{var, Var};
756/// # use zng_app::handler::app_hn;
757/// # let _scope = zng_app::APP.minimal();
758/// # fn assert_type() {
759/// let foo = var("".to_txt());
760///
761/// CLICK_EVENT.on_event(app_hn!(foo, |args: &ClickArgs, _| {
762/// foo.set(args.target.to_txt());
763/// })).perm();
764///
765/// // can still use after:
766/// let bar = foo.map(|c| formatx!("last click: {c}"));
767///
768/// # }
769/// ```
770///
771/// 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
772/// listed in the *clone-move* section it would not be available after the handler is created. See [`clmv!`] for details.
773///
774/// [`clmv!`]: zng_clone_move::clmv
775#[macro_export]
776macro_rules! app_hn {
777 ($($tt:tt)+) => {
778 $crate::handler::app_hn($crate::handler::clmv!{ $($tt)+ })
779 }
780}
781#[doc(inline)]
782pub use crate::app_hn;
783
784#[doc(hidden)]
785pub struct FnOnceAppHandler<H> {
786 handler: Option<H>,
787}
788impl<A, H> AppHandler<A> for FnOnceAppHandler<H>
789where
790 A: Clone + 'static,
791 H: FnOnce(&A) + Send + 'static,
792{
793 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
794 if let Some(handler) = self.handler.take() {
795 handler(args);
796 handler_args.handle.unsubscribe();
797 } else {
798 tracing::error!("`app_hn_once!` called after requesting unsubscribe");
799 }
800 }
801}
802#[doc(hidden)]
803#[cfg(not(feature = "dyn_closure"))]
804pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<H>
805where
806 A: Clone + 'static,
807 H: FnOnce(&A) + Send + 'static,
808{
809 FnOnceAppHandler { handler: Some(handler) }
810}
811#[doc(hidden)]
812#[cfg(feature = "dyn_closure")]
813pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<Box<dyn FnOnce(&A) + Send>>
814where
815 A: Clone + 'static,
816 H: FnOnce(&A) + Send + 'static,
817{
818 FnOnceAppHandler {
819 handler: Some(Box::new(handler)),
820 }
821}
822
823///<span data-del-macro-root></span> Declare a *clone-move* app event handler that is only called once.
824///
825/// The macro input is a closure with optional *clone-move* variables, internally it uses [`clmv!`] so
826/// the input is the same syntax.
827///
828/// # Examples
829///
830/// The example captures `data` by move and then destroys it in the first call, this cannot be done using [`app_hn!`] because
831/// the `data` needs to be available for all event calls. In this case the closure is only called once, subsequent events
832/// are ignored by the handler and it automatically requests unsubscribe.
833///
834/// ```
835/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
836/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
837/// # use zng_app::handler::app_hn_once;
838/// # let _scope = zng_app::APP.minimal();
839/// # fn assert_type() {
840/// let data = vec![1, 2, 3];
841///
842/// CLICK_EVENT.on_event(app_hn_once!(|_| {
843/// for i in data {
844/// print!("{i}, ");
845/// }
846/// })).perm();
847/// # }
848/// ```
849///
850/// Other then declaring a `FnOnce` this macro behaves like [`app_hn!`], so the same considerations apply. You can *clone-move* variables,
851/// the type of the input is the event arguments and must be annotated.
852///
853/// ```
854/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
855/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
856/// # use zng_app::handler::app_hn_once;
857/// # let _scope = zng_app::APP.minimal();
858/// # fn assert_type() {
859/// let data = vec![1, 2, 3];
860///
861/// CLICK_EVENT.on_event(app_hn_once!(data, |args: &ClickArgs| {
862/// drop(data);
863/// })).perm();
864///
865/// println!("{data:?}");
866/// # }
867/// ```
868///
869/// [`clmv!`]: zng_clone_move::clmv
870#[macro_export]
871macro_rules! app_hn_once {
872 ($($tt:tt)+) => {
873 $crate::handler::app_hn_once($crate::handler::clmv! { $($tt)+ })
874 }
875}
876#[doc(inline)]
877pub use crate::app_hn_once;
878
879#[doc(hidden)]
880pub struct AsyncFnMutAppHandler<H> {
881 handler: H,
882}
883impl<A, F, H> AppHandler<A> for AsyncFnMutAppHandler<H>
884where
885 A: Clone + 'static,
886 F: Future<Output = ()> + Send + 'static,
887 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
888{
889 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
890 let handler = &mut self.handler;
891 let mut task = UiTask::new(None, handler(args.clone(), handler_args.handle.clone_boxed()));
892 if task.update().is_none() {
893 if handler_args.is_preview {
894 UPDATES
895 .on_pre_update(app_hn!(|_, handle| {
896 if task.update().is_some() {
897 handle.unsubscribe();
898 }
899 }))
900 .perm();
901 } else {
902 UPDATES
903 .on_update(app_hn!(|_, handle| {
904 if task.update().is_some() {
905 handle.unsubscribe();
906 }
907 }))
908 .perm();
909 }
910 }
911 }
912}
913#[doc(hidden)]
914#[cfg(not(feature = "dyn_closure"))]
915pub fn async_app_hn<A, F, H>(handler: H) -> AsyncFnMutAppHandler<H>
916where
917 A: Clone + 'static,
918 F: Future<Output = ()> + Send + 'static,
919 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
920{
921 AsyncFnMutAppHandler { handler }
922}
923
924#[cfg(feature = "dyn_closure")]
925type BoxedAsyncAppHn<A> = Box<dyn FnMut(A, Box<dyn AppWeakHandle>) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
926
927#[doc(hidden)]
928#[cfg(feature = "dyn_closure")]
929pub fn async_app_hn<A, F, H>(mut handler: H) -> AsyncFnMutAppHandler<BoxedAsyncAppHn<A>>
930where
931 A: Clone + 'static,
932 F: Future<Output = ()> + Send + 'static,
933 H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
934{
935 AsyncFnMutAppHandler {
936 handler: Box::new(move |args, handle| Box::pin(handler(args, handle))),
937 }
938}
939
940///<span data-del-macro-root></span> Declare an async *clone-move* app event handler.
941///
942/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn!`] so
943/// the input is the same syntax.
944///
945/// The handler generates a future for each event, the future is polled immediately if it does not finish it is scheduled
946/// to update in [`on_pre_update`](crate::update::UPDATES::on_pre_update) or [`on_update`](crate::update::UPDATES::on_update) depending
947/// on if the handler was assigned to a *preview* event or not.
948///
949/// Note that this means [`propagation`](crate::event::AnyEventArgs::propagation) can only be meaningfully stopped before the
950/// first `.await`, after, the event has already propagated.
951///
952/// # Examples
953///
954/// The example declares an async event handler for the `CLICK_EVENT`.
955///
956/// ```
957/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
958/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
959/// # use zng_app::handler::async_app_hn;
960/// # use zng_task as task;
961/// # let _scope = zng_app::APP.minimal();
962/// # fn assert_type() {
963/// CLICK_EVENT.on_event(async_app_hn!(|_, _| {
964/// println!("Clicked Somewhere!");
965///
966/// task::run(async {
967/// println!("In other thread!");
968/// }).await;
969///
970/// println!("Back in UI thread, in an app update.");
971/// })).perm();
972/// # }
973/// ```
974///
975/// The closure input is `A, Box<dyn AppWeakHandle>` for all handlers and `A` is `ClickArgs` for this example. Note that
976/// if you want to use the event args you must annotate the input type, the context and handle types are inferred.
977///
978/// The handle can be used to unsubscribe the event handler, if [`unsubscribe`](AppWeakHandle::unsubscribe) is called the handler
979/// will be dropped some time before the next event update. Running tasks are not canceled by unsubscribing, the only way to *cancel*
980/// then is by returning early inside the async blocks.
981///
982/// ```
983/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
984/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
985/// # use zng_app::handler::async_app_hn;
986/// # use zng_task as task;
987/// # let _scope = zng_app::APP.minimal();
988/// # fn assert_type() {
989/// CLICK_EVENT.on_event(async_app_hn!(|args: ClickArgs, handle| {
990/// println!("Clicked {}!", args.target);
991/// task::run(async move {
992/// handle.unsubscribe();
993/// });
994/// })).perm();
995/// # }
996/// ```
997///
998/// Internally the [`async_clmv_fn!`] macro is used so you can *clone-move* variables into the handler.
999///
1000/// ```
1001/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1002/// # zng_app::event::event! { pub static CLICK_EVENT: ClickArgs; }
1003/// # use zng_app::handler::async_app_hn;
1004/// # use zng_var::{var, Var};
1005/// # use zng_task as task;
1006/// # use zng_txt::{formatx, ToTxt};
1007/// #
1008/// # let _scope = zng_app::APP.minimal();
1009/// # fn assert_type() {
1010/// let status = var("pending..".to_txt());
1011///
1012/// CLICK_EVENT.on_event(async_app_hn!(status, |args: ClickArgs, _| {
1013/// status.set(formatx!("processing {}..", args.target));
1014///
1015/// task::run(async move {
1016/// println!("do something slow");
1017/// }).await;
1018///
1019/// status.set(formatx!("finished {}", args.target));
1020/// })).perm();
1021///
1022/// // can still use after:
1023/// let text = status;
1024///
1025/// # }
1026/// ```
1027///
1028/// 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
1029/// listed in the *clone-move* section it would not be available after the handler is created. See [`async_clmv_fn!`] for details.
1030///
1031/// ## Futures and Clone-Move
1032///
1033/// You may want to always *clone-move* captures for async handlers, because they then automatically get cloned again for each event. This
1034/// 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.
1035///
1036/// This second cloning can be avoided by using the [`async_hn_once!`] macro instead, but only if you expect a single event.
1037///
1038/// [`async_clmv_fn!`]: zng_clone_move::async_clmv_fn
1039#[macro_export]
1040macro_rules! async_app_hn {
1041 ($($tt:tt)+) => {
1042 $crate::handler::async_app_hn($crate::handler::async_clmv_fn! { $($tt)+ })
1043 }
1044}
1045#[doc(inline)]
1046pub use crate::async_app_hn;
1047
1048#[doc(hidden)]
1049pub struct AsyncFnOnceAppHandler<H> {
1050 handler: Option<H>,
1051}
1052
1053impl<A, F, H> AppHandler<A> for AsyncFnOnceAppHandler<H>
1054where
1055 A: Clone + 'static,
1056 F: Future<Output = ()> + Send + 'static,
1057 H: FnOnce(A) -> F + Send + 'static,
1058{
1059 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
1060 if let Some(handler) = self.handler.take() {
1061 handler_args.handle.unsubscribe();
1062
1063 let mut task = UiTask::new(None, handler(args.clone()));
1064 if task.update().is_none() {
1065 if handler_args.is_preview {
1066 UPDATES
1067 .on_pre_update(app_hn!(|_, handle| {
1068 if task.update().is_some() {
1069 handle.unsubscribe();
1070 }
1071 }))
1072 .perm();
1073 } else {
1074 UPDATES
1075 .on_update(app_hn!(|_, handle| {
1076 if task.update().is_some() {
1077 handle.unsubscribe();
1078 }
1079 }))
1080 .perm();
1081 }
1082 }
1083 } else {
1084 tracing::error!("`async_app_hn_once!` called after requesting unsubscribe");
1085 }
1086 }
1087}
1088#[doc(hidden)]
1089#[cfg(not(feature = "dyn_closure"))]
1090pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<H>
1091where
1092 A: Clone + 'static,
1093 F: Future<Output = ()> + Send + 'static,
1094 H: FnOnce(A) -> F + Send + 'static,
1095{
1096 AsyncFnOnceAppHandler { handler: Some(handler) }
1097}
1098
1099#[cfg(feature = "dyn_closure")]
1100type BoxedAsyncAppHnOnce<A> = Box<dyn FnOnce(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
1101
1102#[doc(hidden)]
1103#[cfg(feature = "dyn_closure")]
1104pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<BoxedAsyncAppHnOnce<A>>
1105where
1106 A: Clone + 'static,
1107 F: Future<Output = ()> + Send + 'static,
1108 H: FnOnce(A) -> F + Send + 'static,
1109{
1110 AsyncFnOnceAppHandler {
1111 handler: Some(Box::new(move |args| Box::pin(handler(args)))),
1112 }
1113}
1114
1115///<span data-del-macro-root></span> Declare an async *clone-move* app event handler that is only called once.
1116///
1117/// The macro input is a closure with optional *clone-move* variables, internally it uses [`async_clmv_fn_once!`] so
1118/// the input is the same syntax.
1119///
1120/// # Examples
1121///
1122/// 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!`]
1123/// 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
1124/// to capture by *clone-move* just to use `data`.
1125///
1126/// ```
1127/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1128/// # use zng_app::handler::async_hn_once;
1129/// # use zng_task as task;
1130/// # let _scope = zng_app::APP.minimal();
1131/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
1132/// let data = vec![1, 2, 3];
1133/// # let
1134/// on_open = async_hn_once!(|_| {
1135/// task::run(async move {
1136/// for i in data {
1137/// print!("{i}, ");
1138/// }
1139/// }).await;
1140///
1141/// println!("Done!");
1142/// });
1143/// # on_open }
1144/// ```
1145///
1146/// 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
1147/// but will just be moved to the other thread, avoiding a needless clone.
1148///
1149/// ```
1150/// # zng_app::event::event_args! { pub struct ClickArgs { pub target: zng_txt::Txt, pub click_count: usize, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
1151/// # use zng_app::handler::async_hn_once;
1152/// # use zng_task as task;
1153/// # let _scope = zng_app::APP.minimal();
1154/// # fn assert_type() -> impl zng_app::handler::WidgetHandler<ClickArgs> {
1155/// let data = vec![1, 2, 3];
1156/// # let
1157/// on_open = async_hn_once!(data, |_| {
1158/// task::run(async move {
1159/// for i in data {
1160/// print!("{i}, ");
1161/// }
1162/// }).await;
1163///
1164/// println!("Done!");
1165/// });
1166/// println!("{data:?}");
1167/// # on_open }
1168/// ```
1169///
1170/// [`async_clmv_fn_once!`]: zng_clone_move::async_clmv_fn_once
1171#[macro_export]
1172macro_rules! async_app_hn_once {
1173 ($($tt:tt)+) => {
1174 $crate::handler::async_app_hn_once($crate::handler::async_clmv_fn_once! { $($tt)+ })
1175 }
1176}
1177#[doc(inline)]
1178pub use crate::async_app_hn_once;
1179use crate::update::UPDATES;
1180use crate::widget::{UiTaskWidget, WIDGET};
1181
1182/// Widget handler wrapper that filters the events, only delegating to `self` when `filter` returns `true`.
1183pub struct FilterWidgetHandler<A, H, F> {
1184 _args: PhantomData<fn() -> A>,
1185 handler: H,
1186 filter: F,
1187}
1188impl<A, H, F> FilterWidgetHandler<A, H, F>
1189where
1190 A: Clone + 'static,
1191 H: WidgetHandler<A>,
1192 F: FnMut(&A) -> bool + Send + 'static,
1193{
1194 /// New filter handler.
1195 pub fn new(handler: H, filter: F) -> Self {
1196 Self {
1197 handler,
1198 filter,
1199 _args: PhantomData,
1200 }
1201 }
1202}
1203impl<A, H, F> WidgetHandler<A> for FilterWidgetHandler<A, H, F>
1204where
1205 A: Clone + 'static,
1206 H: WidgetHandler<A>,
1207 F: FnMut(&A) -> bool + Send + 'static,
1208{
1209 fn event(&mut self, args: &A) -> bool {
1210 if (self.filter)(args) { self.handler.event(args) } else { false }
1211 }
1212
1213 fn update(&mut self) -> bool {
1214 self.handler.update()
1215 }
1216}
1217
1218/// App handler wrapper that filters the events, only delegating to `self` when `filter` returns `true`.
1219pub struct FilterAppHandler<A, H, F> {
1220 _args: PhantomData<fn() -> A>,
1221 handler: H,
1222 filter: F,
1223}
1224impl<A, H, F> FilterAppHandler<A, H, F>
1225where
1226 A: Clone + 'static,
1227 H: AppHandler<A>,
1228 F: FnMut(&A) -> bool + Send + 'static,
1229{
1230 /// New filter handler.
1231 pub fn new(handler: H, filter: F) -> Self {
1232 Self {
1233 handler,
1234 filter,
1235 _args: PhantomData,
1236 }
1237 }
1238}
1239impl<A, H, F> AppHandler<A> for FilterAppHandler<A, H, F>
1240where
1241 A: Clone + 'static,
1242 H: AppHandler<A>,
1243 F: FnMut(&A) -> bool + Send + 'static,
1244{
1245 fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
1246 if (self.filter)(args) {
1247 self.handler.event(args, handler_args);
1248 }
1249 }
1250}
1251
1252impl HeadlessApp {
1253 /// Calls an [`AppHandler<A>`] once and blocks until the update tasks started during the call complete.
1254 ///
1255 /// This function *spins* until all update tasks are completed. Timers or send events can
1256 /// be received during execution but the loop does not sleep, it just spins requesting an update
1257 /// for each pass.
1258 pub fn block_on<A>(&mut self, handler: &mut dyn AppHandler<A>, args: &A, timeout: Duration) -> Result<(), String>
1259 where
1260 A: Clone + 'static,
1261 {
1262 self.block_on_multi(vec![handler], args, timeout)
1263 }
1264
1265 /// Calls multiple [`AppHandler<A>`] once each and blocks until all update tasks are complete.
1266 ///
1267 /// This function *spins* until all update tasks are completed. Timers or send events can
1268 /// be received during execution but the loop does not sleep, it just spins requesting an update
1269 /// for each pass.
1270 pub fn block_on_multi<A>(&mut self, handlers: Vec<&mut dyn AppHandler<A>>, args: &A, timeout: Duration) -> Result<(), String>
1271 where
1272 A: Clone + 'static,
1273 {
1274 let (pre_len, pos_len) = UPDATES.handler_lens();
1275
1276 let handler_args = AppHandlerArgs {
1277 handle: &Handle::dummy(()).downgrade(),
1278 is_preview: false,
1279 };
1280 for handler in handlers {
1281 handler.event(args, &handler_args);
1282 }
1283
1284 let mut pending = UPDATES.new_update_handlers(pre_len, pos_len);
1285
1286 if !pending.is_empty() {
1287 let start_time = INSTANT.now();
1288 while {
1289 pending.retain(|h| h());
1290 !pending.is_empty()
1291 } {
1292 UPDATES.update(None);
1293 let flow = self.update(false);
1294 if INSTANT.now().duration_since(start_time) >= timeout {
1295 return Err(format!(
1296 "block_on reached timeout of {timeout:?} before the handler task could finish",
1297 ));
1298 }
1299
1300 match flow {
1301 AppControlFlow::Poll => continue,
1302 AppControlFlow::Wait => {
1303 thread::yield_now();
1304 continue;
1305 }
1306 AppControlFlow::Exit => return Ok(()),
1307 }
1308 }
1309 }
1310
1311 Ok(())
1312 }
1313
1314 /// Polls a `future` and updates the app repeatedly until it completes or the `timeout` is reached.
1315 pub fn block_on_fut<F: Future>(&mut self, future: F, timeout: Duration) -> Result<F::Output, String> {
1316 let future = task::with_deadline(future, timeout);
1317 let mut future = std::pin::pin!(future);
1318
1319 let waker = UPDATES.waker(None);
1320 let mut cx = std::task::Context::from_waker(&waker);
1321
1322 loop {
1323 let mut fut_poll = future.as_mut().poll(&mut cx);
1324 let flow = self.update_observe(
1325 || {
1326 if fut_poll.is_pending() {
1327 fut_poll = future.as_mut().poll(&mut cx);
1328 }
1329 },
1330 true,
1331 );
1332
1333 match fut_poll {
1334 std::task::Poll::Ready(r) => match r {
1335 Ok(r) => return Ok(r),
1336 Err(e) => return Err(e.to_string()),
1337 },
1338 std::task::Poll::Pending => {}
1339 }
1340
1341 match flow {
1342 AppControlFlow::Poll => continue,
1343 AppControlFlow::Wait => {
1344 thread::yield_now();
1345 continue;
1346 }
1347 AppControlFlow::Exit => return Err("app exited".to_owned()),
1348 }
1349 }
1350 }
1351
1352 /// Calls the `handler` once and [`block_on`] it with a 60 seconds timeout using the minimal headless app.
1353 ///
1354 /// [`block_on`]: Self::block_on
1355 #[track_caller]
1356 #[cfg(any(test, doc, feature = "test_util"))]
1357 pub fn doc_test<A, H>(args: A, mut handler: H)
1358 where
1359 A: Clone + 'static,
1360 H: AppHandler<A>,
1361 {
1362 let mut app = crate::APP.minimal().run_headless(false);
1363 app.block_on(&mut handler, &args, DOC_TEST_BLOCK_ON_TIMEOUT).unwrap();
1364 }
1365
1366 /// Calls the `handlers` once each and [`block_on_multi`] with a 60 seconds timeout.
1367 ///
1368 /// [`block_on_multi`]: Self::block_on_multi
1369 #[track_caller]
1370 #[cfg(any(test, doc, feature = "test_util"))]
1371 pub fn doc_test_multi<A>(args: A, mut handlers: Vec<Box<dyn AppHandler<A>>>)
1372 where
1373 A: Clone + 'static,
1374 {
1375 let mut app = crate::APP.minimal().run_headless(false);
1376 app.block_on_multi(handlers.iter_mut().map(|h| h.as_mut()).collect(), &args, DOC_TEST_BLOCK_ON_TIMEOUT)
1377 .unwrap()
1378 }
1379}
1380
1381#[cfg(any(test, doc, feature = "test_util"))]
1382const DOC_TEST_BLOCK_ON_TIMEOUT: Duration = Duration::from_secs(60);