zng_app/event/
args.rs

1use std::{
2    any::Any,
3    fmt,
4    sync::{
5        Arc,
6        atomic::{AtomicBool, Ordering::Relaxed},
7    },
8};
9
10/// [`Event<A>`] arguments.
11///
12/// See [`AnyEventArgs`] for the object safe part of event arguments.
13///
14/// [`Event<A>`]: crate::event::Event
15pub trait EventArgs: AnyEventArgs + Clone {
16    /// Calls `handler` and stops propagation if propagation is still allowed.
17    ///
18    /// Returns the `handler` result if it was called.
19    fn handle<F, R>(&self, handler: F) -> Option<R>
20    where
21        F: FnOnce(&Self) -> R,
22    {
23        if self.propagation().is_stopped() {
24            None
25        } else {
26            let r = handler(self);
27            self.propagation().stop();
28            Some(r)
29        }
30    }
31}
32
33/// Methods of [`EventArgs`] that are object safe.
34pub trait AnyEventArgs: fmt::Debug + Send + Sync + Any {
35    /// Clone the event into a type erased box.
36    fn clone_any(&self) -> Box<dyn AnyEventArgs>;
37
38    /// Access to `dyn Any` methods.
39    fn as_any(&self) -> &dyn Any;
40
41    /// Gets the instant this event happened.
42    fn timestamp(&self) -> crate::DInstant;
43
44    /// Insert all targets of this event on the [`UpdateDeliveryList`].
45    fn delivery_list(&self, list: &mut UpdateDeliveryList);
46
47    /// Propagation handle associated with this event instance.
48    ///
49    /// Cloned arguments share the same handle, some arguments may also share the handle
50    /// of another event if they share the same cause.
51    fn propagation(&self) -> &EventPropagationHandle;
52}
53
54/// Event propagation handle associated with one or multiple [`EventArgs`].
55///
56/// Event handlers can use this to signal subsequent handlers that the event is already handled and they should
57/// operate as if the event was not received.
58///
59/// You can get the propagation handle of any event argument by using the [`AnyEventArgs::propagation`] method.
60#[derive(Debug, Clone)]
61pub struct EventPropagationHandle(Arc<AtomicBool>);
62impl EventPropagationHandle {
63    /// New in the not stopped default state.
64    pub fn new() -> Self {
65        EventPropagationHandle(Arc::new(AtomicBool::new(false)))
66    }
67
68    /// Signal subsequent handlers that the event is already handled.
69    pub fn stop(&self) {
70        // Is `Arc` to make `EventArgs` send, but stop handle is only useful in the UI thread, so
71        // we don't need any ordering.
72        self.0.store(true, Relaxed);
73    }
74
75    /// If the handler must skip this event instance.
76    ///
77    /// Note that property level handlers don't need to check this, as those handlers are
78    /// not called when this is `true`. Direct event listeners in [`UiNode`] and [`AppExtension`]
79    /// must check if this is `true`.
80    ///
81    /// [`UiNode`]: crate::widget::node::UiNode
82    /// [`AppExtension`]: crate::AppExtension
83    pub fn is_stopped(&self) -> bool {
84        self.0.load(Relaxed)
85    }
86}
87impl Default for EventPropagationHandle {
88    fn default() -> Self {
89        EventPropagationHandle::new()
90    }
91}
92impl PartialEq for EventPropagationHandle {
93    fn eq(&self, other: &Self) -> bool {
94        Arc::ptr_eq(&self.0, &other.0)
95    }
96}
97impl Eq for EventPropagationHandle {}
98impl std::hash::Hash for EventPropagationHandle {
99    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
100        let ptr = Arc::as_ptr(&self.0) as usize;
101        std::hash::Hash::hash(&ptr, state);
102    }
103}
104
105///<span data-del-macro-root></span> Declares new [`EventArgs`] types.
106///
107/// The macro syntax is similar to `struct` declaration, but after the args struct members you must add `..` and then
108/// the `fn delivery_list(&self, list: &mut UpdateDeliveryList) {}` method that inserts the widget targets.
109///
110/// After the `delivery_list` method you can also optionally add a `fn validate(&self) -> Result<(), Txt> { }` method
111/// that validates the arguments.
112///
113/// The macro expansion implements the [`EventArgs`] and [`AnyEventArgs`] traits for the new structs, it generates a public `timestamp`
114/// member and a `new` and `now` associated functions. The `new` function instantiates args with custom timestamp and propagation handle,
115/// the `now` function provides the timestamp and propagation handle and is the primary way to instantiate args.
116///
117/// # Examples
118///
119/// ```
120/// # use zng_app::{event::event_args, widget::info::WidgetPath};
121/// # use zng_txt::*;
122/// #
123/// event_args! {
124///     /// My event arguments.
125///     pub struct MyEventArgs {
126///         /// My argument.
127///         pub arg: String,
128///         /// My event target.
129///         pub target: WidgetPath,
130///
131///         ..
132///         
133///         fn delivery_list(&self, list: &mut UpdateDeliveryList) {
134///             list.insert_wgt(&self.target);
135///         }
136///
137///         /// Optional validation, if defined the generated `new` and `now` functions call it and unwrap the result.
138///         ///
139///         /// The error type can be any type that implement `Debug`.
140///         fn validate(&self) -> Result<(), Txt> {
141///             if self.arg.contains("error") {
142///                 return Err(formatx!("invalid arg `{}`", self.arg));
143///             }
144///             Ok(())
145///         }
146///     }
147///
148///     // multiple structs can be declared in the same call.
149///     // pub struct MyOtherEventArgs { /**/ }
150/// }
151/// ```
152///
153/// [`EventArgs`]: crate::event::EventArgs
154#[macro_export]
155macro_rules! event_args {
156    ($(
157        $(#[$outer:meta])*
158        $vis:vis struct $Args:ident {
159            $($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
160            ..
161            $(#[$delivery_list_outer:meta])*
162            fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
163
164            $(
165                $(#[$validate_outer:meta])*
166                fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
167            )?
168        }
169    )+) => {$(
170        $crate::__event_args! {
171            $(#[$outer])*
172            $vis struct $Args {
173                $($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
174
175                ..
176
177                $(#[$delivery_list_outer])*
178                fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
179
180                $(
181                    $(#[$validate_outer])*
182                    fn validate(&$self_v) -> Result<(), $ValidationError> { $($validate)+ }
183                )?
184            }
185        }
186    )+};
187}
188#[doc(hidden)]
189#[macro_export]
190macro_rules! __event_args {
191    // match validate
192    (
193        $(#[$outer:meta])*
194        $vis:vis struct $Args:ident {
195            $($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
196            ..
197            $(#[$delivery_list_outer:meta])*
198            fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
199
200            $(#[$validate_outer:meta])*
201            fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
202        }
203    ) => {
204        $crate::__event_args! {common=>
205
206            $(#[$outer])*
207            $vis struct $Args {
208                $($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
209                ..
210                $(#[$delivery_list_outer])*
211                fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
212            }
213        }
214        impl $Args {
215            /// New args from values that convert [into](Into) the argument types.
216            ///
217            /// # Panics
218            ///
219            /// Panics if the arguments are invalid.
220            #[allow(clippy::too_many_arguments)]
221            pub fn new(
222                timestamp: impl Into<$crate::DInstant>,
223                propagation_handle: $crate::event::EventPropagationHandle,
224                $($arg : impl Into<$arg_ty>),*
225            ) -> Self {
226                let args = $Args {
227                    timestamp: timestamp.into(),
228                    $($arg: $arg.into(),)*
229                    propagation_handle,
230                };
231                args.assert_valid();
232                args
233            }
234
235            /// New args from values that convert [into](Into) the argument types.
236            ///
237            /// Returns an error if the constructed arguments are invalid.
238            #[allow(clippy::too_many_arguments)]
239            pub fn try_new(
240                timestamp: impl Into<$crate::DInstant>,
241                propagation_handle: $crate::event::EventPropagationHandle,
242                $($arg : impl Into<$arg_ty>),*
243            ) -> Result<Self, $ValidationError> {
244                let args = $Args {
245                    timestamp: timestamp.into(),
246                    $($arg: $arg.into(),)*
247                    propagation_handle,
248                };
249                args.validate()?;
250                Ok(args)
251            }
252
253            /// Arguments for event that happened now (`INSTANT.now`).
254            ///
255            /// # Panics
256            ///
257            /// Panics if the arguments are invalid.
258            #[allow(clippy::too_many_arguments)]
259            pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
260                Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
261            }
262
263            /// Arguments for event that happened now (`INSTANT.now`).
264            ///
265            /// Returns an error if the constructed arguments are invalid.
266            #[allow(clippy::too_many_arguments)]
267            pub fn try_now($($arg : impl Into<$arg_ty>),*) -> Result<Self, $ValidationError> {
268                Self::try_new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
269            }
270
271            $(#[$validate_outer])*
272            pub fn validate(&$self_v) -> Result<(), $ValidationError> {
273                $($validate)+
274            }
275
276            /// Panics if the arguments are invalid.
277            #[track_caller]
278            pub fn assert_valid(&self) {
279                if let Err(e) = self.validate() {
280                    panic!("invalid `{}`, {e:?}", stringify!($Args));
281                }
282            }
283        }
284    };
285
286    // match no validate
287    (
288        $(#[$outer:meta])*
289        $vis:vis struct $Args:ident {
290            $($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
291            ..
292            $(#[$delivery_list_outer:meta])*
293            fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
294        }
295    ) => {
296        $crate::__event_args! {common=>
297
298            $(#[$outer])*
299            $vis struct $Args {
300                $($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
301                ..
302                $(#[$delivery_list_outer])*
303                fn delivery_list(&$self, $delivery_list_ident: &mut UpdateDeliveryList) { $($delivery_list)* }
304            }
305        }
306
307        impl $Args {
308            /// New args from values that convert [into](Into) the argument types.
309            #[allow(clippy::too_many_arguments)]
310            pub fn new(
311                timestamp: impl Into<$crate::DInstant>,
312                propagation_handle: $crate::event::EventPropagationHandle,
313                $($arg : impl Into<$arg_ty>),*
314            ) -> Self {
315                $Args {
316                    timestamp: timestamp.into(),
317                    $($arg: $arg.into(),)*
318                    propagation_handle,
319                }
320            }
321
322            /// Arguments for event that happened now (`INSTANT.now`).
323            #[allow(clippy::too_many_arguments)]
324            pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
325                Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
326            }
327        }
328    };
329
330    // common code between validating and not.
331    (common=>
332
333        $(#[$outer:meta])*
334        $vis:vis struct $Args:ident {
335            $($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
336            ..
337            $(#[$delivery_list_outer:meta])*
338            fn delivery_list(&$self:ident, $delivery_list_ident:ident: &mut UpdateDeliveryList) { $($delivery_list:tt)* }
339        }
340    ) => {
341        $(#[$outer])*
342        #[derive(Debug, Clone)]
343        $vis struct $Args {
344            /// Instant the event happened.
345            pub timestamp: $crate::DInstant,
346            $($(#[$arg_outer])* $arg_vis $arg : $arg_ty,)*
347
348            propagation_handle: $crate::event::EventPropagationHandle,
349        }
350        impl $crate::event::EventArgs for $Args {
351        }
352        impl $crate::event::AnyEventArgs for $Args {
353            fn clone_any(&self) -> std::boxed::Box<dyn $crate::event::AnyEventArgs> {
354                Box::new(self.clone())
355            }
356
357            fn as_any(&self) -> &dyn std::any::Any {
358                self
359            }
360
361            fn timestamp(&self) -> $crate::DInstant {
362                self.timestamp
363            }
364
365            $(#[$delivery_list_outer])*
366            fn delivery_list(&$self, $delivery_list_ident: &mut $crate::update::UpdateDeliveryList) {
367                #[allow(unused_imports)]
368                use $crate::update::UpdateDeliveryList;
369
370                $($delivery_list)*
371            }
372
373            fn propagation(&self) -> &$crate::event::EventPropagationHandle {
374                &self.propagation_handle
375            }
376        }
377    };
378}
379#[doc(inline)]
380pub use crate::event_args;
381use crate::update::UpdateDeliveryList;