zng_app/event/
args.rs

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