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}