use crate::interaction::{InteractionEvent, InteractionEventKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ListenerId(u64);
struct Listener {
id: ListenerId,
kind: InteractionEventKind,
callback: Box<dyn Fn(&InteractionEvent) + Send + Sync>,
once: bool,
}
pub struct EventEmitter {
listeners: Vec<Listener>,
next_id: u64,
}
impl Default for EventEmitter {
fn default() -> Self {
Self::new()
}
}
impl EventEmitter {
pub fn new() -> Self {
Self {
listeners: Vec::new(),
next_id: 0,
}
}
pub fn on<F>(&mut self, kind: InteractionEventKind, callback: F) -> ListenerId
where
F: Fn(&InteractionEvent) + Send + Sync + 'static,
{
let id = self.alloc_id();
self.listeners.push(Listener {
id,
kind,
callback: Box::new(callback),
once: false,
});
id
}
pub fn once<F>(&mut self, kind: InteractionEventKind, callback: F) -> ListenerId
where
F: Fn(&InteractionEvent) + Send + Sync + 'static,
{
let id = self.alloc_id();
self.listeners.push(Listener {
id,
kind,
callback: Box::new(callback),
once: true,
});
id
}
pub fn off(&mut self, id: ListenerId) -> bool {
let before = self.listeners.len();
self.listeners.retain(|l| l.id != id);
self.listeners.len() < before
}
pub fn dispatch(&mut self, events: &[InteractionEvent]) {
if self.listeners.is_empty() || events.is_empty() {
return;
}
let mut to_remove: Vec<ListenerId> = Vec::new();
for event in events {
for listener in &self.listeners {
if listener.kind == event.kind {
(listener.callback)(event);
if listener.once {
to_remove.push(listener.id);
}
}
}
}
if !to_remove.is_empty() {
self.listeners.retain(|l| !to_remove.contains(&l.id));
}
}
pub fn listener_count(&self) -> usize {
self.listeners.len()
}
pub fn listener_count_for(&self, kind: InteractionEventKind) -> usize {
self.listeners.iter().filter(|l| l.kind == kind).count()
}
fn alloc_id(&mut self) -> ListenerId {
let id = ListenerId(self.next_id);
self.next_id += 1;
id
}
}
unsafe impl Send for EventEmitter {}
unsafe impl Sync for EventEmitter {}
#[cfg(test)]
mod tests {
use super::*;
use crate::interaction::{InteractionEvent, InteractionEventKind, PointerKind, ScreenPoint};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
fn make_event(kind: InteractionEventKind) -> InteractionEvent {
InteractionEvent::new(kind, PointerKind::Mouse, ScreenPoint::new(0.0, 0.0))
}
#[test]
fn on_dispatches_repeatedly() {
let mut emitter = EventEmitter::new();
let counter = Arc::new(AtomicU32::new(0));
let c = counter.clone();
emitter.on(InteractionEventKind::Click, move |_| {
c.fetch_add(1, Ordering::Relaxed);
});
let events = vec![make_event(InteractionEventKind::Click)];
emitter.dispatch(&events);
emitter.dispatch(&events);
assert_eq!(counter.load(Ordering::Relaxed), 2);
}
#[test]
fn once_dispatches_only_once() {
let mut emitter = EventEmitter::new();
let counter = Arc::new(AtomicU32::new(0));
let c = counter.clone();
emitter.once(InteractionEventKind::Click, move |_| {
c.fetch_add(1, Ordering::Relaxed);
});
let events = vec![make_event(InteractionEventKind::Click)];
emitter.dispatch(&events);
emitter.dispatch(&events);
assert_eq!(counter.load(Ordering::Relaxed), 1);
assert_eq!(emitter.listener_count(), 0);
}
#[test]
fn off_removes_listener() {
let mut emitter = EventEmitter::new();
let counter = Arc::new(AtomicU32::new(0));
let c = counter.clone();
let id = emitter.on(InteractionEventKind::Click, move |_| {
c.fetch_add(1, Ordering::Relaxed);
});
let events = vec![make_event(InteractionEventKind::Click)];
emitter.dispatch(&events);
assert!(emitter.off(id));
emitter.dispatch(&events);
assert_eq!(counter.load(Ordering::Relaxed), 1);
}
#[test]
fn different_kinds_are_isolated() {
let mut emitter = EventEmitter::new();
let click_count = Arc::new(AtomicU32::new(0));
let move_count = Arc::new(AtomicU32::new(0));
let cc = click_count.clone();
emitter.on(InteractionEventKind::Click, move |_| {
cc.fetch_add(1, Ordering::Relaxed);
});
let mc = move_count.clone();
emitter.on(InteractionEventKind::MouseMove, move |_| {
mc.fetch_add(1, Ordering::Relaxed);
});
emitter.dispatch(&[make_event(InteractionEventKind::Click)]);
assert_eq!(click_count.load(Ordering::Relaxed), 1);
assert_eq!(move_count.load(Ordering::Relaxed), 0);
}
#[test]
fn listener_count_for_kind() {
let mut emitter = EventEmitter::new();
emitter.on(InteractionEventKind::Click, |_| {});
emitter.on(InteractionEventKind::Click, |_| {});
emitter.on(InteractionEventKind::MouseMove, |_| {});
assert_eq!(emitter.listener_count(), 3);
assert_eq!(emitter.listener_count_for(InteractionEventKind::Click), 2);
assert_eq!(
emitter.listener_count_for(InteractionEventKind::MouseMove),
1
);
}
}