zng_app/
handler.rs

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