use std::{ cell::{RefCell, RefMut}, collections::HashSet, rc::Rc, }; use glam::Vec2; use slotmap::SecondaryMap; use crate::{ animation::{self, Animation}, i18n::I18n, layout::{LayoutState, 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, } #[derive(Debug, Clone, Copy)] pub struct MousePosition { pub pos: Vec2, } 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, pub shift: Vec2, 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, } } } #[derive(Default)] pub struct EventAlterables { pub dirty_nodes: Vec, pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>, 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 needs_redraw: bool, pub trigger_haptics: bool, } impl EventAlterables { pub const fn mark_redraw(&mut self) { self.needs_redraw = true; } pub fn set_style(&mut self, node_id: taffy::NodeId, style: taffy::Style) { self.style_set_requests.push((node_id, style)); } 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(Clone, Copy, PartialEq, Eq)] pub enum EventListenerKind { MousePress, MouseRelease, MouseEnter, MouseMotion, MouseLeave, InternalStateChange, } pub type EventCallback = Box anyhow::Result>; //for ref-counting pub struct ListenerHandle { needs_gc: Rc>, // this will be set to true on destructor } #[derive(Default)] pub struct ListenerHandleVec(Vec>); impl ListenerHandleVec { pub fn push(&mut self, handle: Rc) { self.0.push(handle); } } impl Drop for ListenerHandle { fn drop(&mut self) { *self.needs_gc.borrow_mut() = true; } } pub struct EventListener { pub kind: EventListenerKind, pub callback: EventCallback, pub handle: std::rc::Weak, } impl EventListener { pub fn callback_for_kind( &self, kind: EventListenerKind, ) -> Option<&impl Fn(&mut CallbackDataCommon, &mut CallbackData, &mut U1, &mut U2) -> anyhow::Result> { if self.kind == kind { Some(&self.callback) } else { None } } } #[derive(Default)] pub struct EventListenerVec(Vec>); impl EventListenerVec { pub fn iter(&self) -> impl Iterator> { self.0.iter().filter(|p| p.handle.strong_count() > 0) } } pub struct EventListenerCollection { map: SecondaryMap>, needs_gc: Rc>, } // derive only works if generics also implement Default impl Default for EventListenerCollection { fn default() -> Self { Self { map: SecondaryMap::default(), needs_gc: Rc::new(RefCell::new(false)), } } } impl EventListenerCollection { pub fn register( &mut self, listener_handles: &mut ListenerHandleVec, widget_id: WidgetID, kind: EventListenerKind, callback: EventCallback, ) { let res = self.add_single(widget_id, kind, callback); listener_handles.push(res); } pub fn add_single( &mut self, widget_id: WidgetID, kind: EventListenerKind, callback: EventCallback, ) -> Rc { let handle = Rc::new(ListenerHandle { needs_gc: self.needs_gc.clone(), }); let new_item = EventListener { kind, callback, handle: Rc::downgrade(&handle), }; if let Some(vec) = self.map.get_mut(widget_id) { vec.0.push(new_item); } else { self.map.insert(widget_id, EventListenerVec(vec![new_item])); } handle } // clean-up expired events pub fn gc(&mut self) { let mut needs_gc = self.needs_gc.borrow_mut(); if !*needs_gc { return; } *needs_gc = false; let mut count = 0; for (_id, vec) in &mut self.map { vec.0.retain(|listener| { if listener.handle.strong_count() != 0 { true } else { count += 1; false } }); } self.map.retain(|_k, v| !v.0.is_empty()); log::debug!("EventListenerCollection: cleaned-up {count} expired events"); } pub fn get(&self, widget_id: WidgetID) -> Option<&EventListenerVec> { self.map.get(widget_id) } }