use std::sync::{Arc, atomic::AtomicBool};
use zng_var::{AnyVarValue, VarValue};
use crate::widget::WidgetId;
use atomic::Ordering::Relaxed;
pub trait AnyEventArgs: AnyVarValue {
fn timestamp(&self) -> crate::DInstant;
fn propagation(&self) -> &EventPropagationHandle;
fn is_in_target(&self, widget: WidgetId) -> bool;
fn clone_boxed(&self) -> Box<dyn AnyEventArgs>;
}
pub trait EventArgs: AnyEventArgs + VarValue {}
#[macro_export]
macro_rules! event_args {
($(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$is_in_target_outer:meta])*
fn is_in_target(&$self:ident, $is_in_target_id:ident: WidgetId) -> bool { $($is_in_target:tt)* }
$(
$(#[$validate_outer:meta])*
fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
)?
}
)+) => {$(
$crate::__event_args! {
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$is_in_target_outer])*
fn is_in_target(&$self, $is_in_target_id: WidgetId) -> bool { $($is_in_target)* }
$(
$(#[$validate_outer])*
fn validate(&$self_v) -> Result<(), $ValidationError> { $($validate)+ }
)?
}
}
)+};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __event_args {
(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$is_in_target_outer:meta])*
fn is_in_target(&$self:ident, $is_in_target_id:ident: WidgetId) -> bool { $($is_in_target:tt)* }
$(#[$validate_outer:meta])*
fn validate(&$self_v:ident) -> Result<(), $ValidationError:path> { $($validate:tt)+ }
}
) => {
$crate::__event_args! {common=>
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$is_in_target_outer])*
fn is_in_target(&$self, $is_in_target_id: WidgetId) -> bool { $($is_in_target)* }
}
}
impl $Args {
#[allow(clippy::too_many_arguments)]
pub fn new(
timestamp: impl Into<$crate::DInstant>,
propagation: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Self {
let args = $Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation,
};
args.assert_valid();
args
}
#[allow(clippy::too_many_arguments)]
pub fn try_new(
timestamp: impl Into<$crate::DInstant>,
propagation: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Result<Self, $ValidationError> {
let args = $Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation,
};
args.validate()?;
Ok(args)
}
#[allow(clippy::too_many_arguments)]
pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
#[allow(clippy::too_many_arguments)]
pub fn try_now($($arg : impl Into<$arg_ty>),*) -> Result<Self, $ValidationError> {
Self::try_new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
$(#[$validate_outer])*
pub fn validate(&$self_v) -> Result<(), $ValidationError> {
$($validate)+
}
#[track_caller]
pub fn assert_valid(&self) {
if let Err(e) = self.validate() {
panic!("invalid `{}`, {e:?}", stringify!($Args));
}
}
}
};
(
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$is_in_target_outer:meta])*
fn is_in_target(&$self:ident, $is_in_target_id:ident: WidgetId) -> bool { $($is_in_target:tt)* }
}
) => {
$crate::__event_args! {common=>
$(#[$outer])*
$vis struct $Args {
$($(#[$arg_outer])* $arg_vis $arg: $arg_ty,)*
..
$(#[$is_in_target_outer])*
fn is_in_target(&$self, $is_in_target_id: WidgetId) -> bool { $($is_in_target)* }
}
}
impl $Args {
#[allow(clippy::too_many_arguments)]
pub fn new(
timestamp: impl Into<$crate::DInstant>,
propagation: $crate::event::EventPropagationHandle,
$($arg : impl Into<$arg_ty>),*
) -> Self {
$Args {
timestamp: timestamp.into(),
$($arg: $arg.into(),)*
propagation,
}
}
#[allow(clippy::too_many_arguments)]
pub fn now($($arg : impl Into<$arg_ty>),*) -> Self {
Self::new($crate::INSTANT.now(), $crate::event::EventPropagationHandle::new(), $($arg),*)
}
}
};
(common=>
$(#[$outer:meta])*
$vis:vis struct $Args:ident {
$($(#[$arg_outer:meta])* $arg_vis:vis $arg:ident : $arg_ty:ty,)*
..
$(#[$is_in_target_outer:meta])*
fn is_in_target(&$self:ident, $is_in_target_id:ident: WidgetId) -> bool { $($is_in_target:tt)* }
}
) => {
$(#[$outer])*
#[derive(Debug, Clone, PartialEq)]
$vis struct $Args {
/// Instant the event happened.
pub timestamp: $crate::DInstant,
$($(#[$arg_outer])* $arg_vis $arg : $arg_ty,)*
pub propagation: $crate::event::EventPropagationHandle,
}
impl $crate::event::AnyEventArgs for $Args {
fn timestamp(&self) -> $crate::DInstant {
self.timestamp
}
$(#[$is_in_target_outer])*
fn is_in_target(&$self, $is_in_target_id: $crate::widget::WidgetId) -> bool {
let _ = $is_in_target_id;
$($is_in_target)*
}
fn propagation(&self) -> &$crate::event::EventPropagationHandle {
&self.propagation
}
fn clone_boxed(&self) -> std::boxed::Box<dyn $crate::event::AnyEventArgs> {
Box::new(self.clone())
}
}
impl $crate::event::EventArgs for $Args { }
};
}
#[doc(inline)]
pub use crate::event_args;
#[derive(Debug, Clone)]
pub struct EventPropagationHandle(Arc<AtomicBool>);
impl EventPropagationHandle {
pub fn new() -> Self {
EventPropagationHandle(Arc::new(AtomicBool::new(false)))
}
pub fn stop(&self) {
self.0.store(true, Relaxed);
}
pub fn is_stopped(&self) -> bool {
self.0.load(Relaxed)
}
}
impl Default for EventPropagationHandle {
fn default() -> Self {
EventPropagationHandle::new()
}
}
impl PartialEq for EventPropagationHandle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for EventPropagationHandle {}
impl std::hash::Hash for EventPropagationHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let ptr = Arc::as_ptr(&self.0) as usize;
std::hash::Hash::hash(&ptr, state);
}
}