use std::cell::Cell; use std::rc::Rc; use dpi::PhysicalPosition; use web_sys::PointerEvent; use winit_core::event::{ButtonSource, DeviceId, ElementState, Force, PointerKind, PointerSource}; use winit_core::keyboard::ModifiersState; use super::canvas::Common; use super::event; use super::event_handle::EventListenerHandle; use crate::platform_impl::web::event::mkdid; use crate::platform_impl::web::web_sys::event::mouse_button_to_id; #[allow(dead_code)] pub(super) struct PointerHandler { on_cursor_leave: Option>, on_cursor_enter: Option>, on_cursor_move: Option>, on_pointer_press: Option>, on_pointer_release: Option>, on_touch_cancel: Option>, } impl PointerHandler { pub fn new() -> Self { Self { on_cursor_leave: None, on_cursor_enter: None, on_cursor_move: None, on_pointer_press: None, on_pointer_release: None, on_touch_cancel: None, } } pub fn on_pointer_leave(&mut self, canvas_common: &Common, mut handler: F) where F: 'static + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), { let window = canvas_common.window.clone(); self.on_cursor_leave = Some(canvas_common.add_event("pointerout", move |event: PointerEvent| { let modifiers = event::mouse_modifiers(&event); let pointer_id = event.pointer_id(); let device_id = mkdid(pointer_id); let position = event::mouse_position(&event).to_physical(super::scale_factor(&window)); let kind = event::pointer_type(&event, pointer_id); handler(modifiers, device_id, event.is_primary(), position, kind); })); } pub fn on_pointer_enter(&mut self, canvas_common: &Common, mut handler: F) where F: 'static + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), { let window = canvas_common.window.clone(); self.on_cursor_enter = Some(canvas_common.add_event("pointerover", move |event: PointerEvent| { let modifiers = event::mouse_modifiers(&event); let pointer_id = event.pointer_id(); let device_id = mkdid(pointer_id); let position = event::mouse_position(&event).to_physical(super::scale_factor(&window)); let kind = event::pointer_type(&event, pointer_id); handler(modifiers, device_id, event.is_primary(), position, kind); })); } pub fn on_pointer_release(&mut self, canvas_common: &Common, mut handler: C) where C: 'static + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), { let window = canvas_common.window.clone(); self.on_pointer_release = Some(canvas_common.add_event("pointerup", move |event: PointerEvent| { let modifiers = event::mouse_modifiers(&event); let pointer_id = event.pointer_id(); let kind = event::pointer_type(&event, pointer_id); let button = event::mouse_button(&event).expect("no mouse button pressed"); let source = match kind { PointerKind::Mouse => ButtonSource::Mouse(button), PointerKind::Touch(finger_id) => ButtonSource::Touch { finger_id, force: Some(Force::Normalized(event.pressure().into())), }, PointerKind::Unknown => ButtonSource::Unknown(mouse_button_to_id(button)), }; handler( modifiers, mkdid(pointer_id), event.is_primary(), event::mouse_position(&event).to_physical(super::scale_factor(&window)), source, ) })); } pub fn on_pointer_press( &mut self, canvas_common: &Common, mut handler: C, prevent_default: Rc>, ) where C: 'static + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); self.on_pointer_press = Some(canvas_common.add_event("pointerdown", move |event: PointerEvent| { if prevent_default.get() { // prevent text selection event.prevent_default(); // but still focus element let _ = canvas.focus(); } let modifiers = event::mouse_modifiers(&event); let pointer_id = event.pointer_id(); let kind = event::pointer_type(&event, pointer_id); let button = event::mouse_button(&event).expect("no mouse button pressed"); let source = match kind { PointerKind::Mouse => { // Error is swallowed here since the error would occur every time the // mouse is clicked when the cursor is // grabbed, and there is probably not a // situation where this could fail, that we // care if it fails. let _e = canvas.set_pointer_capture(pointer_id); ButtonSource::Mouse(button) }, PointerKind::Touch(finger_id) => ButtonSource::Touch { finger_id, force: Some(Force::Normalized(event.pressure().into())), }, PointerKind::Unknown => ButtonSource::Unknown(mouse_button_to_id(button)), }; handler( modifiers, mkdid(pointer_id), event.is_primary(), event::mouse_position(&event).to_physical(super::scale_factor(&window)), source, ) })); } pub fn on_pointer_move( &mut self, canvas_common: &Common, mut cursor_handler: C, mut button_handler: B, prevent_default: Rc>, ) where C: 'static + FnMut( Option, &mut dyn Iterator< Item = (ModifiersState, bool, PhysicalPosition, PointerSource), >, ), B: 'static + FnMut( ModifiersState, Option, bool, PhysicalPosition, ElementState, ButtonSource, ), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); self.on_cursor_move = Some(canvas_common.add_event("pointermove", move |event: PointerEvent| { let pointer_id = event.pointer_id(); let device_id = mkdid(pointer_id); let kind = event::pointer_type(&event, pointer_id); let primary = event.is_primary(); // chorded button event if let Some(button) = event::mouse_button(&event) { if prevent_default.get() { // prevent text selection event.prevent_default(); // but still focus element let _ = canvas.focus(); } let state = if event::mouse_buttons(&event).contains(button.into()) { ElementState::Pressed } else { ElementState::Released }; let button = match kind { PointerKind::Mouse => ButtonSource::Mouse(button), PointerKind::Touch(finger_id) => { let button_id = mouse_button_to_id(button); if button_id != 1 { tracing::error!("unexpected touch button id: {button_id}"); } ButtonSource::Touch { finger_id, force: Some(Force::Normalized(event.pressure().into())), } }, PointerKind::Unknown => todo!(), }; button_handler( event::mouse_modifiers(&event), device_id, primary, event::mouse_position(&event).to_physical(super::scale_factor(&window)), state, button, ); return; } // pointer move event let scale = super::scale_factor(&window); cursor_handler( device_id, &mut event::pointer_move_event(event).map(|event| { ( event::mouse_modifiers(&event), event.is_primary(), event::mouse_position(&event).to_physical(scale), match kind { PointerKind::Mouse => PointerSource::Mouse, PointerKind::Touch(finger_id) => PointerSource::Touch { finger_id, force: Some(Force::Normalized(event.pressure().into())), }, PointerKind::Unknown => PointerSource::Unknown, }, ) }), ); })); } pub fn remove_listeners(&mut self) { self.on_cursor_leave = None; self.on_cursor_enter = None; self.on_cursor_move = None; self.on_pointer_press = None; self.on_pointer_release = None; self.on_touch_cancel = None; } }