diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d23a38..dceef1c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web: fix position of touch events to be relative to the canvas. - On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during a transient activation. +- On Web, fix pointer button events not being processed when a buttons is already pressed. # 0.28.6 diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 0405f83a..c21b8b06 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -249,7 +249,7 @@ impl EventLoopWindowTarget { let modifiers = self.modifiers.clone(); let has_focus_clone = has_focus.clone(); canvas.on_cursor_move( - move |pointer_id, position, delta, active_modifiers| { + move |pointer_id, position, delta, active_modifiers, buttons, button| { let modifiers_changed = (has_focus_clone.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -259,21 +259,48 @@ impl EventLoopWindowTarget { } }); - runner.send_events(modifiers_changed.into_iter().chain([ - 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), - }, - }, - ])); + let button_event = button.map(|button| { + if buttons.contains(button.into()) { + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Pressed, + button, + }, + } + } else { + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Released, + button, + }, + } + } + }); + + runner.send_events( + modifiers_changed + .into_iter() + .chain([ + 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), + }, + }, + ]) + .chain(button_event), + ); }, move |device_id, location, force| { runner_touch.send_event(Event::WindowEvent { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 513561fc..5ad9456b 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -1,6 +1,6 @@ -use super::event; use super::event_handle::EventListenerHandle; use super::media_query_handle::MediaQueryListHandle; +use super::{event, ButtonsState}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::error::OsError as RootOE; use crate::event::{Force, MouseButton, MouseScrollDelta}; @@ -273,7 +273,15 @@ impl Canvas { touch_handler: T, prevent_default: bool, ) where - M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition, ModifiersState), + M: 'static + + FnMut( + i32, + PhysicalPosition, + PhysicalPosition, + ModifiersState, + ButtonsState, + Option, + ), T: 'static + FnMut(i32, PhysicalPosition, Force), { match &mut self.mouse_state { 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 117a67ed..6ee70cf5 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -7,6 +7,7 @@ use crate::keyboard::ModifiersState; use std::cell::RefCell; use std::rc::Rc; +use event::ButtonsState; use web_sys::{EventTarget, MouseEvent}; type MouseLeaveHandler = Rc>>>; @@ -108,8 +109,11 @@ impl MouseHandler { MouseCaptureState::Captured => {} } event.stop_propagation(); - let modifiers = event::mouse_modifiers(&event); - handler(0, event::mouse_button(&event), modifiers); + handler( + 0, + event::mouse_button(&event).expect("no mouse button released"), + event::mouse_modifiers(&event), + ); if event .target() .map_or(false, |target| target != EventTarget::from(canvas)) @@ -158,12 +162,11 @@ impl MouseHandler { } *mouse_capture_state = MouseCaptureState::Captured; event.stop_propagation(); - let modifiers = event::mouse_modifiers(&event); handler( 0, event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event), - modifiers, + event::mouse_button(&event).expect("no mouse button pressed"), + event::mouse_modifiers(&event), ); }, )); @@ -171,7 +174,15 @@ impl MouseHandler { pub fn on_cursor_move(&mut self, canvas_common: &super::Common, mut handler: F) where - F: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition, ModifiersState), + F: 'static + + FnMut( + i32, + PhysicalPosition, + PhysicalPosition, + ModifiersState, + ButtonsState, + Option, + ), { let mouse_capture_state = self.mouse_capture_state.clone(); let canvas = canvas_common.raw.clone(); @@ -202,12 +213,13 @@ impl MouseHandler { event::mouse_position_by_client(&event, &canvas) }; let mouse_delta = event::mouse_delta(&event); - let modifiers = event::mouse_modifiers(&event); handler( 0, mouse_pos.to_physical(super::super::scale_factor()), mouse_delta.to_physical(super::super::scale_factor()), - modifiers, + 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 80f9e953..4f31a659 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -4,6 +4,7 @@ use crate::dpi::PhysicalPosition; use crate::event::{Force, MouseButton}; use crate::keyboard::ModifiersState; +use event::ButtonsState; use web_sys::PointerEvent; #[allow(dead_code)] @@ -42,8 +43,7 @@ impl PointerHandler { return; } - let modifiers = event::mouse_modifiers(&event); - handler(event.pointer_id(), modifiers); + handler(event.pointer_id(), event::mouse_modifiers(&event)); }, )); } @@ -62,8 +62,7 @@ impl PointerHandler { return; } - let modifiers = event::mouse_modifiers(&event); - handler(event.pointer_id(), modifiers); + handler(event.pointer_id(), event::mouse_modifiers(&event)); }, )); } @@ -89,8 +88,11 @@ impl PointerHandler { Force::Normalized(event.pressure() as f64), ); } else { - let modifiers = event::mouse_modifiers(&event); - mouse_handler(event.pointer_id(), event::mouse_button(&event), modifiers); + mouse_handler( + event.pointer_id(), + event::mouse_button(&event).expect("no mouse button released"), + event::mouse_modifiers(&event), + ); } }, )); @@ -117,12 +119,11 @@ impl PointerHandler { Force::Normalized(event.pressure() as f64), ); } else { - let modifiers = event::mouse_modifiers(&event); mouse_handler( event.pointer_id(), event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event), - modifiers, + event::mouse_button(&event).expect("no mouse button pressed"), + event::mouse_modifiers(&event), ); // Error is swallowed here since the error would occur every time the mouse is @@ -141,7 +142,15 @@ impl PointerHandler { mut touch_handler: T, prevent_default: bool, ) where - M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition, ModifiersState), + M: 'static + + FnMut( + i32, + PhysicalPosition, + PhysicalPosition, + ModifiersState, + ButtonsState, + Option, + ), T: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); @@ -160,12 +169,13 @@ impl PointerHandler { Force::Normalized(event.pressure() as f64), ); } else { - let modifiers = event::mouse_modifiers(&event); mouse_handler( event.pointer_id(), event::mouse_position(&event).to_physical(super::super::scale_factor()), event::mouse_delta(&event).to_physical(super::super::scale_factor()), - modifiers, + event::mouse_modifiers(&event), + event::mouse_buttons(&event), + event::mouse_button(&event), ); } }, diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index a1e7d6f4..55497d1d 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -6,12 +6,50 @@ use smol_str::SmolStr; use std::convert::TryInto; use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, PointerEvent, WheelEvent}; -pub fn mouse_button(event: &MouseEvent) -> MouseButton { +bitflags! { + pub struct ButtonsState: u16 { + const LEFT = 0b001; + const RIGHT = 0b010; + const MIDDLE = 0b100; + } +} + +impl From for MouseButton { + fn from(value: ButtonsState) -> Self { + match value { + ButtonsState::LEFT => MouseButton::Left, + ButtonsState::RIGHT => MouseButton::Right, + ButtonsState::MIDDLE => MouseButton::Middle, + _ => MouseButton::Other(value.bits()), + } + } +} + +impl From for ButtonsState { + fn from(value: MouseButton) -> Self { + match value { + MouseButton::Left => ButtonsState::LEFT, + MouseButton::Right => ButtonsState::RIGHT, + MouseButton::Middle => ButtonsState::MIDDLE, + MouseButton::Other(value) => unsafe { ButtonsState::from_bits_unchecked(value) }, + } + } +} + +pub fn mouse_buttons(event: &MouseEvent) -> ButtonsState { + unsafe { ButtonsState::from_bits_unchecked(event.buttons()) } +} + +pub fn mouse_button(event: &MouseEvent) -> Option { match event.button() { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - i => MouseButton::Other((i - 3).try_into().expect("very large mouse button value")), + -1 => None, + 0 => Some(MouseButton::Left), + 1 => Some(MouseButton::Middle), + 2 => Some(MouseButton::Right), + i => Some(MouseButton::Other( + i.try_into() + .expect("unexpected negative mouse button value"), + )), } } diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index abfa1381..454204c7 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -6,6 +6,7 @@ mod scaling; mod timeout; pub use self::canvas::Canvas; +pub use self::event::ButtonsState; pub use self::scaling::ScaleChangeDetector; pub use self::timeout::{AnimationFrameRequest, Timeout};