diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index c21b8b06..74b8984b 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -244,23 +244,67 @@ impl EventLoopWindowTarget { ))); }); - let runner = self.runner.clone(); - let runner_touch = self.runner.clone(); - let modifiers = self.modifiers.clone(); - let has_focus_clone = has_focus.clone(); canvas.on_cursor_move( - move |pointer_id, position, delta, active_modifiers, buttons, button| { - let modifiers_changed = - (has_focus_clone.get() && modifiers.get() != active_modifiers).then(|| { + { + let runner = self.runner.clone(); + let has_focus = has_focus.clone(); + let modifiers = self.modifiers.clone(); + + move |active_modifiers| { + if has_focus.get() && modifiers.get() != active_modifiers { modifiers.set(active_modifiers); - Event::WindowEvent { + runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::ModifiersChanged(active_modifiers.into()), - } - }); + }) + } + } + }, + { + let runner = self.runner.clone(); - let button_event = button.map(|button| { - if buttons.contains(button.into()) { + move |pointer_id, position, delta| { + runner.send_events( + [ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, + }, + }, + Event::DeviceEvent { + device_id: RootDeviceId(DeviceId(pointer_id)), + event: DeviceEvent::MouseMotion { + delta: (delta.x, delta.y), + }, + }, + ] + .into_iter(), + ); + } + }, + { + let runner = self.runner.clone(); + + move |device_id, location, force| { + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: RootDeviceId(DeviceId(device_id)), + phase: TouchPhase::Moved, + force: Some(force), + location, + }), + }); + } + }, + { + let runner = self.runner.clone(); + + move |pointer_id, position: crate::dpi::PhysicalPosition, buttons, button| { + let button_event = if buttons.contains(button.into()) { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::MouseInput { @@ -278,13 +322,13 @@ impl EventLoopWindowTarget { button, }, } - } - }); + }; - runner.send_events( - modifiers_changed - .into_iter() - .chain([ + // A chorded button event may come in without any prior CursorMoved events, + // therefore we should send a CursorMoved event to make sure that the + // user code has the correct cursor position. + runner.send_events( + [ Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::CursorMoved { @@ -292,27 +336,11 @@ impl EventLoopWindowTarget { position, }, }, - Event::DeviceEvent { - device_id: RootDeviceId(DeviceId(pointer_id)), - event: DeviceEvent::MouseMotion { - delta: (delta.x, delta.y), - }, - }, - ]) - .chain(button_event), - ); - }, - move |device_id, location, force| { - runner_touch.send_event(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), - phase: TouchPhase::Moved, - force: Some(force), - location, - }), - }); + button_event, + ] + .into_iter(), + ); + } }, prevent_default, ); diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 5ad9456b..6c9a7f78 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -267,28 +267,31 @@ impl Canvas { } } - pub fn on_cursor_move( + pub fn on_cursor_move( &mut self, + modifier_handler: MOD, mouse_handler: M, touch_handler: T, + button_handler: B, prevent_default: bool, ) where - M: 'static - + FnMut( - i32, - PhysicalPosition, - PhysicalPosition, - ModifiersState, - ButtonsState, - Option, - ), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition), T: 'static + FnMut(i32, PhysicalPosition, Force), + B: 'static + FnMut(i32, PhysicalPosition, ButtonsState, MouseButton), { match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => { - h.on_cursor_move(&self.common, mouse_handler, touch_handler, prevent_default) + MouseState::HasPointerEvent(h) => h.on_cursor_move( + &self.common, + modifier_handler, + mouse_handler, + touch_handler, + button_handler, + prevent_default, + ), + MouseState::NoPointerEvent(h) => { + h.on_cursor_move(&self.common, modifier_handler, mouse_handler) } - MouseState::NoPointerEvent(h) => h.on_cursor_move(&self.common, mouse_handler), } } diff --git a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs index 6ee70cf5..6827cac6 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -7,7 +7,6 @@ use crate::keyboard::ModifiersState; use std::cell::RefCell; use std::rc::Rc; -use event::ButtonsState; use web_sys::{EventTarget, MouseEvent}; type MouseLeaveHandler = Rc>>>; @@ -172,23 +171,22 @@ impl MouseHandler { )); } - pub fn on_cursor_move(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static - + FnMut( - i32, - PhysicalPosition, - PhysicalPosition, - ModifiersState, - ButtonsState, - Option, - ), + pub fn on_cursor_move( + &mut self, + canvas_common: &super::Common, + mut modifier_handler: MOD, + mut mouse_handler: M, + ) where + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition), { let mouse_capture_state = self.mouse_capture_state.clone(); let canvas = canvas_common.raw.clone(); self.on_mouse_move = Some(canvas_common.add_window_mouse_event( "mousemove", move |event: MouseEvent| { + modifier_handler(event::mouse_modifiers(&event)); + let canvas = canvas.clone(); let mouse_capture_state = mouse_capture_state.borrow(); let is_over_canvas = event @@ -213,13 +211,10 @@ impl MouseHandler { event::mouse_position_by_client(&event, &canvas) }; let mouse_delta = event::mouse_delta(&event); - handler( + mouse_handler( 0, mouse_pos.to_physical(super::super::scale_factor()), mouse_delta.to_physical(super::super::scale_factor()), - event::mouse_modifiers(&event), - event::mouse_buttons(&event), - event::mouse_button(&event), ); } } diff --git a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs index 80713ce9..476855f0 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -141,23 +141,19 @@ impl PointerHandler { )); } - pub fn on_cursor_move( + pub fn on_cursor_move( &mut self, canvas_common: &super::Common, + mut modifier_handler: MOD, mut mouse_handler: M, mut touch_handler: T, + mut button_handler: B, prevent_default: bool, ) where - M: 'static - + FnMut( - i32, - PhysicalPosition, - PhysicalPosition, - ModifiersState, - ButtonsState, - Option, - ), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition), T: 'static + FnMut(i32, PhysicalPosition, Force), + B: 'static + FnMut(i32, PhysicalPosition, ButtonsState, MouseButton), { let canvas = canvas_common.raw.clone(); self.on_cursor_move = Some(canvas_common.add_event( @@ -173,7 +169,11 @@ impl PointerHandler { fn has_get_coalesced_events(this: &PointerEventExt) -> JsValue; } - match event.pointer_type().as_str() { + modifier_handler(event::mouse_modifiers(&event)); + + let pointer_type = event.pointer_type(); + + match pointer_type.as_str() { "touch" => { if prevent_default { // prevent scroll on mobile web @@ -184,23 +184,32 @@ impl PointerHandler { _ => return, } - let event: PointerEventExt = event.unchecked_into(); - let id = event.pointer_id(); - // cache buttons if the pointer is a mouse - let mouse = (event.pointer_type() == "mouse").then(|| { - ( - event::mouse_modifiers(&event), + + // chorded button event + if let Some(button) = event::mouse_button(&event) { + debug_assert_eq!( + pointer_type, "mouse", + "expect pointer type of a chorded button event to be a mouse" + ); + + button_handler( + id, + event::mouse_position(&event).to_physical(super::super::scale_factor()), event::mouse_buttons(&event), - event::mouse_button(&event), - ) - }); + button, + ); + + return; + } + + // pointer move event + + let event: PointerEventExt = event.unchecked_into(); // store coalesced events to extend it's lifetime let events = (!event.has_get_coalesced_events().is_undefined()) - .then(|| event.get_coalesced_events()) - // if coalesced events is empty, it's a chorded button event - .filter(|events| events.length() != 0); + .then(|| event.get_coalesced_events()); // make a single iterator depending on the availability of coalesced events let events = if let Some(events) = &events { @@ -214,30 +223,19 @@ impl PointerHandler { }; for event in events { - // coalesced events should always have the same source as the root event - debug_assert_eq!(id, event.pointer_id()); - debug_assert_eq!(mouse.is_none(), event.pointer_type() == "touch"); - - if let Some((modifiers, buttons, button)) = mouse { - // coalesced events should have the same buttons - debug_assert_eq!(modifiers, event::mouse_modifiers(&event)); - debug_assert_eq!(buttons, event::mouse_buttons(&event)); - - mouse_handler( + match pointer_type.as_str() { + "mouse" => mouse_handler( id, event::mouse_position(&event).to_physical(super::super::scale_factor()), event::mouse_delta(&event).to_physical(super::super::scale_factor()), - modifiers, - buttons, - button, - ); - } else { - touch_handler( + ), + "touch" => touch_handler( id, event::touch_position(&event, &canvas) .to_physical(super::super::scale_factor()), Force::Normalized(event.pressure() as f64), - ); + ), + _ => unreachable!("didn't return early before"), } } },