use std::{ any::{Any, TypeId}, cell::RefMut, collections::HashSet, }; use glam::Vec2; use slotmap::{DenseSlotMap, new_key_type}; use crate::{ animation::{self, Animation}, i18n::I18n, layout::{LayoutState, LayoutTask, WidgetID}, stack::{ScissorStack, Transform, TransformStack}, widget::{EventResult, WidgetData, WidgetObj}, }; #[derive(Debug, Clone, Copy)] pub enum MouseButtonIndex { Left, Right, Middle, } #[derive(Debug, Clone, Copy)] pub struct MouseButton { pub index: MouseButtonIndex, pub pos: Vec2, pub device: usize, } #[derive(Debug, Clone, Copy)] pub struct MousePosition { pub pos: Vec2, pub device: usize, } pub struct MouseDownEvent { pub pos: Vec2, pub index: MouseButtonIndex, pub device: usize, } pub struct MouseLeaveEvent { pub device: usize, } pub struct MouseMotionEvent { pub pos: Vec2, pub device: usize, } pub struct MouseUpEvent { pub pos: Vec2, pub index: MouseButtonIndex, pub device: usize, } pub struct MouseWheelEvent { pub pos: Vec2, /* mouse position */ pub delta: Vec2, /* wheel delta */ pub device: usize, } pub struct InternalStateChangeEvent { pub metadata: usize, } pub enum Event { InternalStateChange(InternalStateChangeEvent), MouseDown(MouseDownEvent), MouseLeave(MouseLeaveEvent), MouseMotion(MouseMotionEvent), MouseUp(MouseUpEvent), MouseWheel(MouseWheelEvent), } impl Event { fn test_transform_pos(transform: &Transform, pos: Vec2) -> bool { pos.x >= transform.abs_pos.x && pos.x < transform.abs_pos.x + transform.visual_dim.x && pos.y >= transform.abs_pos.y && pos.y < transform.abs_pos.y + transform.visual_dim.y } pub fn test_mouse_within_transform(&self, transform: &Transform) -> bool { match self { Self::MouseDown(evt) => Self::test_transform_pos(transform, evt.pos), Self::MouseMotion(evt) => Self::test_transform_pos(transform, evt.pos), Self::MouseUp(evt) => Self::test_transform_pos(transform, evt.pos), Self::MouseWheel(evt) => Self::test_transform_pos(transform, evt.pos), _ => false, } } } pub enum StyleSetRequest { Display(taffy::Display), Margin(taffy::Rect), Width(taffy::Dimension), Height(taffy::Dimension), } // alterables which will be dispatched in the next loop iteration phase #[derive(Default)] pub struct EventAlterables { pub dirty_nodes: Vec, pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>, pub animations: Vec, pub widgets_to_tick: HashSet, // widgets which needs to be ticked in the next `Layout::update()` fn pub transform_stack: TransformStack, pub scissor_stack: ScissorStack, pub tasks: Vec, pub needs_redraw: bool, pub trigger_haptics: bool, } // helper functions impl EventAlterables { pub const fn mark_redraw(&mut self) { self.needs_redraw = true; } pub fn set_style(&mut self, widget_id: WidgetID, request: StyleSetRequest) { self.style_set_requests.push((widget_id, request)); } pub fn mark_dirty(&mut self, node_id: taffy::NodeId) { self.dirty_nodes.push(node_id); } pub fn mark_tick(&mut self, widget_id: WidgetID) { self.widgets_to_tick.insert(widget_id); } pub const fn trigger_haptics(&mut self) { self.trigger_haptics = true; } pub fn animate(&mut self, animation: Animation) { self.animations.push(animation); } } pub struct CallbackDataCommon<'a> { pub state: &'a LayoutState, pub alterables: &'a mut EventAlterables, } impl CallbackDataCommon<'_> { pub fn i18n(&self) -> RefMut<'_, I18n> { self.state.globals.i18n() } // helper function pub fn mark_widget_dirty(&mut self, id: WidgetID) { if let Some(node_id) = self.state.nodes.get(id) { self.alterables.mark_dirty(*node_id); } self.alterables.mark_redraw(); } } pub struct CallbackData<'a> { pub obj: &'a mut dyn WidgetObj, pub widget_data: &'a mut WidgetData, pub widget_id: WidgetID, pub node_id: taffy::NodeId, pub metadata: CallbackMetadata, } pub enum CallbackMetadata { None, MouseButton(MouseButton), MousePosition(MousePosition), Custom(usize), } impl CallbackMetadata { // helper function pub const fn get_mouse_pos_absolute(&self) -> Option { match *self { Self::MouseButton(b) => Some(b.pos), Self::MousePosition(b) => Some(b.pos), Self::Custom(_) | Self::None => None, } } pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option { let mouse_pos_abs = self.get_mouse_pos_absolute()?; Some(mouse_pos_abs - transform_stack.get().abs_pos) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EventListenerKind { MousePress, MouseRelease, MouseEnter, MouseMotion, MouseLeave, InternalStateChange, } pub type EventCallbackInternal = Box< dyn for<'a, 'b, 'c, 'd> Fn( &'a mut CallbackDataCommon<'b>, &'a mut CallbackData<'c>, &'d mut dyn Any, &'d mut dyn Any, ) -> anyhow::Result, >; pub type EventCallback = Box< dyn for<'a, 'b, 'c, 'd> Fn( &'a mut CallbackDataCommon<'b>, &'a mut CallbackData<'c>, &'d mut U1, &'d mut U2, ) -> anyhow::Result, >; new_key_type! { pub struct EventListenerID; } pub struct EventListener { kind: EventListenerKind, callback: EventCallbackInternal, tid1: Option, tid2: Option, } impl EventListener { pub fn call_with( &self, common: &mut CallbackDataCommon, data: &mut CallbackData, user_data: &mut (&mut U1, &mut U2), ) -> anyhow::Result { let a1: &mut (dyn Any + 'static) = if self.tid1.is_none() { &mut () } else { user_data.0 }; let a2: &mut (dyn Any + 'static) = if self.tid2.is_none() { &mut () } else { user_data.1 }; (self.callback)(common, data, a1, a2) } } #[derive(Default)] pub struct EventListenerCollection { inner: DenseSlotMap, } impl EventListenerCollection { /// Iterates over event handlers with a matching U type pub fn iter_filtered( &self, kind: EventListenerKind, ) -> impl Iterator { let tid1 = TypeId::of::(); let tid2 = TypeId::of::(); self .inner .values() .filter(move |p| p.tid1.is_none_or(|a| a == tid1) && p.tid2.is_none_or(|a| a == tid2) && p.kind == kind) } pub fn register( &mut self, kind: EventListenerKind, callback: EventCallback, ) -> EventListenerID { let tid_unit = TypeId::of::<()>(); let tid1 = TypeId::of::(); let tid2 = TypeId::of::(); let callback_inner: EventCallbackInternal = Box::new(move |common, data, u1_any, u2_any| { if let Some(u1) = u1_any.downcast_mut::() && let Some(u2) = u2_any.downcast_mut::() { callback(common, data, u1, u2) } else { Ok(EventResult::Pass) } }); let new_item = EventListener { kind, callback: callback_inner, tid1: (tid1 != tid_unit).then_some(tid1), tid2: (tid2 != tid_unit).then_some(tid2), }; self.inner.insert(new_item) } pub fn remove(&mut self, event_listener_id: EventListenerID) -> Option { self.inner.remove(event_listener_id) } }