use std::collections::HashMap;
use std::rc::Rc;
use std::sync::atomic::{AtomicU8, Ordering};
use blinc_core::events::{event_types, EventType};
use crate::tree::LayoutNodeId;
static CURRENT_MOUSE_BUTTON: AtomicU8 = AtomicU8::new(0);
pub fn set_current_mouse_button(button: u8) {
CURRENT_MOUSE_BUTTON.store(button, Ordering::Relaxed);
}
pub fn current_mouse_button() -> u8 {
CURRENT_MOUSE_BUTTON.load(Ordering::Relaxed)
}
pub type EventCallback = Rc<dyn Fn(&EventContext)>;
#[derive(Clone, Debug)]
pub struct EventContext {
pub event_type: EventType,
pub node_id: LayoutNodeId,
pub mouse_x: f32,
pub mouse_y: f32,
pub local_x: f32,
pub local_y: f32,
pub bounds_x: f32,
pub bounds_y: f32,
pub bounds_width: f32,
pub bounds_height: f32,
pub scroll_delta_x: f32,
pub scroll_delta_y: f32,
pub scroll_time: Option<f64>,
pub drag_delta_x: f32,
pub drag_delta_y: f32,
pub pinch_center_x: f32,
pub pinch_center_y: f32,
pub pinch_scale: f32,
pub rotation_delta: f32,
pub key_char: Option<char>,
pub key_code: u32,
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
pub meta: bool,
pub mouse_button: u8,
}
impl EventContext {
pub fn new(event_type: EventType, node_id: LayoutNodeId) -> Self {
Self {
event_type,
node_id,
mouse_x: 0.0,
mouse_y: 0.0,
local_x: 0.0,
local_y: 0.0,
bounds_x: 0.0,
bounds_y: 0.0,
bounds_width: 0.0,
bounds_height: 0.0,
scroll_delta_x: 0.0,
scroll_delta_y: 0.0,
scroll_time: None,
drag_delta_x: 0.0,
drag_delta_y: 0.0,
pinch_center_x: 0.0,
pinch_center_y: 0.0,
pinch_scale: 1.0,
rotation_delta: 0.0,
key_char: None,
key_code: 0,
shift: false,
ctrl: false,
alt: false,
meta: false,
mouse_button: current_mouse_button(),
}
}
pub fn with_mouse_pos(mut self, x: f32, y: f32) -> Self {
self.mouse_x = x;
self.mouse_y = y;
self
}
pub fn with_local_pos(mut self, x: f32, y: f32) -> Self {
self.local_x = x;
self.local_y = y;
self
}
pub fn with_bounds(mut self, width: f32, height: f32) -> Self {
self.bounds_width = width;
self.bounds_height = height;
self
}
pub fn with_bounds_pos(mut self, x: f32, y: f32) -> Self {
self.bounds_x = x;
self.bounds_y = y;
self
}
pub fn with_scroll_delta(mut self, dx: f32, dy: f32) -> Self {
self.scroll_delta_x = dx;
self.scroll_delta_y = dy;
self
}
pub fn with_scroll_time(mut self, time: f64) -> Self {
self.scroll_time = Some(time);
self
}
pub fn with_drag_delta(mut self, dx: f32, dy: f32) -> Self {
self.drag_delta_x = dx;
self.drag_delta_y = dy;
self
}
pub fn with_pinch(mut self, scale: f32, center_x: f32, center_y: f32) -> Self {
self.pinch_scale = scale;
self.pinch_center_x = center_x;
self.pinch_center_y = center_y;
self
}
pub fn with_key_char(mut self, c: char) -> Self {
self.key_char = Some(c);
self
}
pub fn with_key_code(mut self, code: u32) -> Self {
self.key_code = code;
self
}
pub fn with_modifiers(mut self, shift: bool, ctrl: bool, alt: bool, meta: bool) -> Self {
self.shift = shift;
self.ctrl = ctrl;
self.alt = alt;
self.meta = meta;
self
}
}
#[derive(Default, Clone)]
pub struct EventHandlers {
handlers: HashMap<EventType, Vec<EventCallback>>,
}
impl EventHandlers {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
pub fn has_handler(&self, event_type: EventType) -> bool {
self.handlers.contains_key(&event_type)
}
pub fn on<F>(&mut self, event_type: EventType, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.handlers
.entry(event_type)
.or_default()
.push(Rc::new(handler));
}
pub fn get(&self, event_type: EventType) -> Option<&[EventCallback]> {
self.handlers.get(&event_type).map(|v| v.as_slice())
}
pub fn event_types(&self) -> impl Iterator<Item = EventType> + '_ {
self.handlers.keys().copied()
}
pub fn dispatch(&self, ctx: &EventContext) {
if let Some(handlers) = self.handlers.get(&ctx.event_type) {
for handler in handlers {
handler(ctx);
}
}
}
pub fn merge(&mut self, other: EventHandlers) {
for (event_type, handlers) in other.handlers {
self.handlers
.entry(event_type)
.or_default()
.extend(handlers);
}
}
pub fn on_click<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::POINTER_UP, handler);
}
pub fn on_mouse_down<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::POINTER_DOWN, handler);
}
pub fn on_mouse_up<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::POINTER_UP, handler);
}
pub fn on_hover_enter<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::POINTER_ENTER, handler);
}
pub fn on_hover_leave<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::POINTER_LEAVE, handler);
}
pub fn on_focus<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::FOCUS, handler);
}
pub fn on_blur<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::BLUR, handler);
}
pub fn on_mount<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::MOUNT, handler);
}
pub fn on_unmount<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::UNMOUNT, handler);
}
pub fn on_key_down<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::KEY_DOWN, handler);
}
pub fn on_key_up<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::KEY_UP, handler);
}
pub fn on_scroll<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::SCROLL, handler);
}
pub fn on_pinch<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::PINCH, handler);
}
pub fn on_resize<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::RESIZE, handler);
}
pub fn on_text_input<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::TEXT_INPUT, handler);
}
pub fn on_drag<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::DRAG, handler);
}
pub fn on_drag_end<F>(&mut self, handler: F)
where
F: Fn(&EventContext) + 'static,
{
self.on(event_types::DRAG_END, handler);
}
}
#[derive(Default)]
pub struct HandlerRegistry {
nodes: HashMap<LayoutNodeId, EventHandlers>,
}
impl HandlerRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, node_id: LayoutNodeId, handlers: EventHandlers) {
if !handlers.is_empty() {
self.nodes.insert(node_id, handlers);
}
}
pub fn get(&self, node_id: LayoutNodeId) -> Option<&EventHandlers> {
self.nodes.get(&node_id)
}
pub fn dispatch(&self, ctx: &EventContext) {
if let Some(handlers) = self.nodes.get(&ctx.node_id) {
handlers.dispatch(ctx);
}
}
pub fn has_handler(&self, node_id: LayoutNodeId, event_type: EventType) -> bool {
self.nodes
.get(&node_id)
.map(|h| h.get(event_type).is_some())
.unwrap_or(false)
}
pub fn remove(&mut self, node_id: LayoutNodeId) {
self.nodes.remove(&node_id);
}
pub fn clear(&mut self) {
self.nodes.clear();
}
pub fn broadcast(&self, event_type: EventType, base_ctx: &EventContext) {
for (node_id, handlers) in &self.nodes {
if handlers.get(event_type).is_some() {
let ctx = EventContext {
event_type,
node_id: *node_id,
..base_ctx.clone()
};
handlers.dispatch(&ctx);
}
}
}
pub fn nodes_with_handler(&self, event_type: EventType) -> Vec<LayoutNodeId> {
self.nodes
.iter()
.filter(|(_, handlers)| handlers.get(event_type).is_some())
.map(|(node_id, _)| *node_id)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use slotmap::SlotMap;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
fn create_node_id() -> LayoutNodeId {
let mut sm: SlotMap<LayoutNodeId, ()> = SlotMap::with_key();
sm.insert(())
}
#[test]
fn test_event_handlers_registration() {
let mut handlers = EventHandlers::new();
let call_count = Arc::new(AtomicU32::new(0));
let count = Arc::clone(&call_count);
handlers.on_click(move |_| {
count.fetch_add(1, Ordering::SeqCst);
});
assert!(!handlers.is_empty());
assert!(handlers.get(event_types::POINTER_UP).is_some());
}
#[test]
fn test_event_dispatch() {
let mut handlers = EventHandlers::new();
let call_count = Arc::new(AtomicU32::new(0));
let node_id = create_node_id();
let count = Arc::clone(&call_count);
handlers.on_click(move |_| {
count.fetch_add(1, Ordering::SeqCst);
});
let ctx = EventContext::new(event_types::POINTER_UP, node_id);
handlers.dispatch(&ctx);
assert_eq!(call_count.load(Ordering::SeqCst), 1);
handlers.dispatch(&ctx);
assert_eq!(call_count.load(Ordering::SeqCst), 2);
}
#[test]
fn test_multiple_handlers() {
let mut handlers = EventHandlers::new();
let call_count = Arc::new(AtomicU32::new(0));
let node_id = create_node_id();
let count1 = Arc::clone(&call_count);
handlers.on_click(move |_| {
count1.fetch_add(1, Ordering::SeqCst);
});
let count2 = Arc::clone(&call_count);
handlers.on_click(move |_| {
count2.fetch_add(10, Ordering::SeqCst);
});
let ctx = EventContext::new(event_types::POINTER_UP, node_id);
handlers.dispatch(&ctx);
assert_eq!(call_count.load(Ordering::SeqCst), 11);
}
#[test]
fn test_handler_registry() {
let mut registry = HandlerRegistry::new();
let node_id = create_node_id();
let call_count = Arc::new(AtomicU32::new(0));
let mut handlers = EventHandlers::new();
let count = Arc::clone(&call_count);
handlers.on_hover_enter(move |_| {
count.fetch_add(1, Ordering::SeqCst);
});
registry.register(node_id, handlers);
assert!(registry.has_handler(node_id, event_types::POINTER_ENTER));
assert!(!registry.has_handler(node_id, event_types::POINTER_DOWN));
let ctx = EventContext::new(event_types::POINTER_ENTER, node_id);
registry.dispatch(&ctx);
assert_eq!(call_count.load(Ordering::SeqCst), 1);
}
}