use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct BubbaEvent {
pub kind: &'static str,
pub value: Option<String>,
pub key: Option<String>,
}
pub type Callback = Arc<dyn Fn(BubbaEvent) + Send + Sync + 'static>;
#[derive(Clone)]
pub struct EventHandler {
pub event: &'static str,
pub callback: Callback,
}
impl std::fmt::Debug for EventHandler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventHandler")
.field("event", &self.event)
.field("callback", &"<fn>")
.finish()
}
}
impl EventHandler {
pub fn new<F>(event: &'static str, f: F) -> Self
where
F: Fn(BubbaEvent) + Send + Sync + 'static,
{
Self {
event,
callback: Arc::new(f),
}
}
pub fn onclick<F: Fn(BubbaEvent) + Send + Sync + 'static>(f: F) -> Self {
Self::new("click", f)
}
pub fn oninput<F: Fn(BubbaEvent) + Send + Sync + 'static>(f: F) -> Self {
Self::new("input", f)
}
pub fn onkeypress<F: Fn(BubbaEvent) + Send + Sync + 'static>(f: F) -> Self {
Self::new("keypress", f)
}
pub fn onfocus<F: Fn(BubbaEvent) + Send + Sync + 'static>(f: F) -> Self {
Self::new("focus", f)
}
pub fn onblur<F: Fn(BubbaEvent) + Send + Sync + 'static>(f: F) -> Self {
Self::new("blur", f)
}
pub fn dispatch(&self, event: BubbaEvent) {
(self.callback)(event);
}
}
pub struct EventDispatcher {
queue: std::sync::Mutex<Vec<(String, BubbaEvent)>>,
}
impl EventDispatcher {
pub fn new() -> Self {
Self {
queue: std::sync::Mutex::new(Vec::new()),
}
}
pub fn push(&self, element_id: impl Into<String>, event: BubbaEvent) {
let mut q = self.queue.lock().unwrap();
q.push((element_id.into(), event));
}
pub fn flush(&self, handlers: &[(String, EventHandler)]) {
let events: Vec<(String, BubbaEvent)> = {
let mut q = self.queue.lock().unwrap();
std::mem::take(&mut *q)
};
for (element_id, event) in events {
for (handler_id, handler) in handlers {
if *handler_id == element_id && handler.event == event.kind {
handler.dispatch(event.clone());
}
}
}
}
}
impl Default for EventDispatcher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, Ordering};
#[test]
fn handler_fires() {
let fired = Arc::new(AtomicBool::new(false));
let fired_clone = Arc::clone(&fired);
let h = EventHandler::onclick(move |_e| {
fired_clone.store(true, Ordering::SeqCst);
});
h.dispatch(BubbaEvent {
kind: "click",
value: None,
key: None,
});
assert!(fired.load(Ordering::SeqCst));
}
}