use crate::KvasirId;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::Arc;
pub type EventHandler<E> = Arc<dyn Fn(&E, &mut EventCtx) + Send + Sync>;
pub trait TriggerEvent: Send + 'static {}
pub struct EventCtx<'a> {
pub request_rerender: bool,
pub signals: Option<&'a mut crate::dirty_flags::DirtyFlags>,
}
impl<'a> EventCtx<'a> {
pub fn new() -> Self {
Self {
request_rerender: false,
signals: None,
}
}
}
impl<'a> Default for EventCtx<'a> {
fn default() -> Self {
Self::new()
}
}
impl TriggerEvent for crate::event::Event {}
pub struct TriggerRegistry {
handlers: HashMap<(KvasirId, TypeId), Vec<Box<dyn Any + Send + Sync>>>,
}
impl Default for TriggerRegistry {
fn default() -> Self {
Self::new()
}
}
impl TriggerRegistry {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
}
pub fn on<E: TriggerEvent>(
&mut self,
node_id: KvasirId,
handler: impl Fn(&E, &mut EventCtx) + Send + Sync + 'static,
) {
let arc: EventHandler<E> = Arc::new(handler);
let key = (node_id, TypeId::of::<E>());
self.handlers
.entry(key)
.or_default()
.push(Box::new(arc) as Box<dyn Any + Send + Sync>);
}
pub fn dispatch<E: TriggerEvent>(
&self,
node_id: KvasirId,
event: &E,
ctx: &mut EventCtx,
) -> bool {
let key = (node_id, TypeId::of::<E>());
let mut rerender = false;
if let Some(handlers) = self.handlers.get(&key) {
for h in handlers {
if let Some(f) = h.downcast_ref::<EventHandler<E>>() {
f(event, ctx);
if ctx.request_rerender {
rerender = true;
}
}
}
}
rerender
}
pub fn clear_node(&mut self, node_id: KvasirId) {
self.handlers.retain(|(id, _), _| *id != node_id);
}
pub fn handler_count(&self) -> usize {
self.handlers.values().map(|v| v.len()).sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug)]
struct MyEvent {
value: u32,
}
impl TriggerEvent for MyEvent {}
#[derive(Debug)]
struct OtherEvent;
impl TriggerEvent for OtherEvent {}
fn node() -> KvasirId {
KvasirId(42)
}
#[test]
fn test_register_and_dispatch() {
let counter = Arc::new(AtomicU32::new(0));
let c1 = counter.clone();
let mut reg = TriggerRegistry::new();
reg.on::<MyEvent>(node(), move |event, _ctx| {
assert_eq!(event.value, 7);
c1.fetch_add(1, Ordering::SeqCst);
});
let result = reg.dispatch::<MyEvent>(node(), &MyEvent { value: 7 }, &mut EventCtx::new());
assert_eq!(counter.load(Ordering::SeqCst), 1);
assert!(!result);
}
#[test]
fn test_no_handlers_returns_false() {
let reg = TriggerRegistry::new();
let result = reg.dispatch::<MyEvent>(node(), &MyEvent { value: 0 }, &mut EventCtx::new());
assert!(!result);
}
#[test]
fn test_multiple_handlers() {
let counter = Arc::new(AtomicU32::new(0));
let mut reg = TriggerRegistry::new();
for _ in 0..3 {
let c = counter.clone();
reg.on::<MyEvent>(node(), move |_, _| {
c.fetch_add(1, Ordering::SeqCst);
});
}
reg.dispatch::<MyEvent>(node(), &MyEvent { value: 0 }, &mut EventCtx::new());
assert_eq!(counter.load(Ordering::SeqCst), 3);
}
#[test]
fn test_rerender_flag_propagates() {
let mut reg = TriggerRegistry::new();
reg.on::<MyEvent>(node(), |_, ctx| {
ctx.request_rerender = true;
});
let result = reg.dispatch::<MyEvent>(node(), &MyEvent { value: 0 }, &mut EventCtx::new());
assert!(result);
}
#[test]
fn test_clear_node() {
let mut reg = TriggerRegistry::new();
reg.on::<MyEvent>(node(), |_, _| {});
reg.on::<OtherEvent>(node(), |_, _| {});
assert_eq!(reg.handler_count(), 2);
reg.clear_node(node());
assert_eq!(reg.handler_count(), 0);
}
#[test]
fn test_isolated_by_event_type() {
let counter = Arc::new(AtomicU32::new(0));
let c1 = counter.clone();
let mut reg = TriggerRegistry::new();
reg.on::<MyEvent>(node(), move |_, _| {
c1.fetch_add(1, Ordering::SeqCst);
});
reg.dispatch::<OtherEvent>(node(), &OtherEvent, &mut EventCtx::new());
assert_eq!(counter.load(Ordering::SeqCst), 0);
}
#[test]
fn test_isolated_by_node_id() {
let counter = Arc::new(AtomicU32::new(0));
let c1 = counter.clone();
let mut reg = TriggerRegistry::new();
reg.on::<MyEvent>(node(), move |_, _| {
c1.fetch_add(1, Ordering::SeqCst);
});
reg.dispatch::<MyEvent>(KvasirId(99), &MyEvent { value: 0 }, &mut EventCtx::new());
assert_eq!(counter.load(Ordering::SeqCst), 0);
}
}