From 4c1ea5b1dd6b295768b653c73340138bd000ac7d Mon Sep 17 00:00:00 2001 From: JAD3N Date: Sun, 12 Mar 2023 02:31:54 +0000 Subject: [PATCH 1/3] Use MouseEvent instead of PointerEvent --- CHANGELOG.md | 4 + .../web/event_loop/window_target.rs | 2 + src/platform_impl/web/web_sys/canvas.rs | 96 ++++++++++------- .../web/web_sys/canvas/mouse_handler.rs | 1 + .../web/web_sys/canvas/pointer_handler.rs | 100 +++--------------- 5 files changed, 74 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9711dde3c8..4621a9fba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased - Bump MSRV from `1.60` to `1.64`. +- Web: Canvases will now capture the contextmenu if prevent_default is enabled so they don't interfere with right click pointer events. +- Web: Canvases will now have `user-select: none` to prevent double clicks from selecting text outside the canvas. +- Web: Only use pointer events for touch typed events. +- Web: Use mouse events for generic mouse button events. # 0.28.3 diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 17aafd946d..33dca0cbaf 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -85,6 +85,8 @@ impl EventLoopWindowTarget { }); }); + canvas.on_contextmenu(prevent_default); + let runner = self.runner.clone(); canvas.on_keyboard_press( move |scancode, virtual_keycode, modifiers| { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 1157a4f2d0..08871376d3 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -1,3 +1,6 @@ +use self::mouse_handler::MouseHandler; +use self::pointer_handler::PointerHandler; + use super::event; use super::event_handle::EventListenerHandle; use super::media_query_handle::MediaQueryListHandle; @@ -27,13 +30,15 @@ pub struct Canvas { on_touch_end: Option>, on_focus: Option>, on_blur: Option>, + on_contextmenu: Option>, on_keyboard_release: Option>, on_keyboard_press: Option>, on_received_character: Option>, on_mouse_wheel: Option>, on_fullscreen_change: Option>, on_dark_mode: Option, - mouse_state: MouseState, + mouse_handler: MouseHandler, + pointer_handler: Option, } struct Common { @@ -72,11 +77,11 @@ impl Canvas { .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; } - let mouse_state = if has_pointer_event() { - MouseState::HasPointerEvent(pointer_handler::PointerHandler::new()) - } else { - MouseState::NoPointerEvent(mouse_handler::MouseHandler::new()) - }; + // Prevent double clicks on the canvas from causing text selections elsewhere on the page + // Calling preventDefault within a dblclick/pointerdown handler on the canvas does not work. + for prefix in ["", "-webkit-", "-moz-", "-ms-"] { + super::set_canvas_style_property(&canvas, &format!("{}user-select", prefix), "none"); + } Ok(Canvas { common: Common { @@ -87,13 +92,15 @@ impl Canvas { on_touch_end: None, on_blur: None, on_focus: None, + on_contextmenu: None, on_keyboard_release: None, on_keyboard_press: None, on_received_character: None, on_mouse_wheel: None, on_fullscreen_change: None, on_dark_mode: None, - mouse_state, + mouse_handler: mouse_handler::MouseHandler::new(), + pointer_handler: has_pointer_event().then(|| pointer_handler::PointerHandler::new()), }) } @@ -172,6 +179,19 @@ impl Canvas { })); } + pub fn on_contextmenu(&mut self, prevent_default: bool) { + // Setup a handler to prevent the default context menu from appearing. + // + // contextmenu events normally occur on right click and can interfere with the sequence of + // pointer events, e.g. https://github.com/rust-windowing/winit/issues/2608. + self.on_contextmenu = Some(self.common.add_event("contextmenu", move |event: Event| { + if prevent_default { + trace!("contextmenu event ignored because prevent_default is true"); + event.prevent_default(); + } + })); + } + pub fn on_keyboard_release(&mut self, mut handler: F, prevent_default: bool) where F: 'static + FnMut(ScanCode, Option, ModifiersState), @@ -248,20 +268,22 @@ impl Canvas { where F: 'static + FnMut(i32), { - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => h.on_cursor_leave(&self.common, handler), - MouseState::NoPointerEvent(h) => h.on_cursor_leave(&self.common, handler), - } + self.mouse_handler.on_cursor_leave(&self.common, handler); + // match &mut self.mouse_state { + // MouseState::HasPointerEvent(h) => h.on_cursor_leave(&self.common, handler), + // MouseState::NoPointerEvent(h) => h.on_cursor_leave(&self.common, handler), + // } } pub fn on_cursor_enter(&mut self, handler: F) where F: 'static + FnMut(i32), { - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => h.on_cursor_enter(&self.common, handler), - MouseState::NoPointerEvent(h) => h.on_cursor_enter(&self.common, handler), - } + self.mouse_handler.on_cursor_enter(&self.common, handler); + // match &mut self.mouse_state { + // MouseState::HasPointerEvent(h) => h.on_cursor_enter(&self.common, handler), + // MouseState::NoPointerEvent(h) => h.on_cursor_enter(&self.common, handler), + // } } pub fn on_mouse_release(&mut self, mouse_handler: M, touch_handler: T) @@ -269,11 +291,10 @@ impl Canvas { M: 'static + FnMut(i32, MouseButton, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), { - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => { - h.on_mouse_release(&self.common, mouse_handler, touch_handler) - } - MouseState::NoPointerEvent(h) => h.on_mouse_release(&self.common, mouse_handler), + self.mouse_handler + .on_mouse_release(&self.common, mouse_handler); + if let Some(pointer_handler) = &mut self.pointer_handler { + pointer_handler.on_mouse_release(&self.common, touch_handler); } } @@ -282,11 +303,10 @@ impl Canvas { M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), { - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => { - h.on_mouse_press(&self.common, mouse_handler, touch_handler) - } - MouseState::NoPointerEvent(h) => h.on_mouse_press(&self.common, mouse_handler), + self.mouse_handler + .on_mouse_press(&self.common, mouse_handler); + if let Some(pointer_handler) = &mut self.pointer_handler { + pointer_handler.on_mouse_press(&self.common, touch_handler); } } @@ -299,11 +319,10 @@ impl Canvas { M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), { - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => { - h.on_cursor_move(&self.common, mouse_handler, touch_handler, prevent_default) - } - MouseState::NoPointerEvent(h) => h.on_cursor_move(&self.common, mouse_handler), + self.mouse_handler + .on_cursor_move(&self.common, mouse_handler); + if let Some(pointer_handler) = &mut self.pointer_handler { + pointer_handler.on_cursor_move(&self.common, touch_handler, prevent_default); } } @@ -311,8 +330,8 @@ impl Canvas { where F: 'static + FnMut(i32, PhysicalPosition, Force), { - if let MouseState::HasPointerEvent(h) = &mut self.mouse_state { - h.on_touch_cancel(&self.common, handler) + if let Some(pointer_handler) = &mut self.pointer_handler { + pointer_handler.on_touch_cancel(&self.common, handler); } } @@ -364,15 +383,16 @@ impl Canvas { pub fn remove_listeners(&mut self) { self.on_focus = None; self.on_blur = None; + self.on_contextmenu = None; self.on_keyboard_release = None; self.on_keyboard_press = None; self.on_received_character = None; self.on_mouse_wheel = None; self.on_fullscreen_change = None; self.on_dark_mode = None; - match &mut self.mouse_state { - MouseState::HasPointerEvent(h) => h.remove_listeners(), - MouseState::NoPointerEvent(h) => h.remove_listeners(), + self.mouse_handler.remove_listeners(); + if let Some(pointer_handler) = &mut self.pointer_handler { + pointer_handler.remove_listeners(); } } } @@ -473,12 +493,6 @@ impl Common { } } -/// Pointer events are supported or not. -enum MouseState { - HasPointerEvent(pointer_handler::PointerHandler), - NoPointerEvent(mouse_handler::MouseHandler), -} - /// Returns whether pointer events are supported. /// Used to decide whether to use pointer events /// or plain mouse events. Note that Safari 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 1effb455c7..051660f204 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -40,6 +40,7 @@ impl MouseHandler { mouse_capture_state: Rc::new(RefCell::new(MouseCaptureState::NotCaptured)), } } + pub fn on_cursor_leave(&mut self, canvas_common: &super::Common, handler: F) where F: 'static + FnMut(i32), 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 43412c3b8b..39614f4406 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -2,14 +2,11 @@ use super::event; use super::EventListenerHandle; use crate::dpi::PhysicalPosition; use crate::event::Force; -use crate::event::{ModifiersState, MouseButton}; use web_sys::PointerEvent; #[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>, @@ -19,8 +16,6 @@ pub(super) struct PointerHandler { 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, @@ -28,121 +23,59 @@ impl PointerHandler { } } - pub fn on_cursor_leave(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static + FnMut(i32), - { - self.on_cursor_leave = Some(canvas_common.add_event( - "pointerout", - move |event: PointerEvent| { - // touch events are handled separately - // handling them here would produce duplicate mouse events, inconsistent with - // other platforms. - if event.pointer_type() == "touch" { - return; - } - - handler(event.pointer_id()); - }, - )); - } - - pub fn on_cursor_enter(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static + FnMut(i32), - { - self.on_cursor_enter = Some(canvas_common.add_event( - "pointerover", - move |event: PointerEvent| { - // touch events are handled separately - // handling them here would produce duplicate mouse events, inconsistent with - // other platforms. - if event.pointer_type() == "touch" { - return; - } - - handler(event.pointer_id()); - }, - )); - } - - pub fn on_mouse_release( + pub fn on_mouse_release( &mut self, canvas_common: &super::Common, - mut mouse_handler: M, - mut touch_handler: T, + mut handler: F, ) where - M: 'static + FnMut(i32, MouseButton, ModifiersState), - T: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); self.on_pointer_release = Some(canvas_common.add_user_event( "pointerup", move |event: PointerEvent| { if event.pointer_type() == "touch" { - touch_handler( + handler( event.pointer_id(), event::touch_position(&event, &canvas) .to_physical(super::super::scale_factor()), Force::Normalized(event.pressure() as f64), ); - } else { - mouse_handler( - event.pointer_id(), - event::mouse_button(&event), - event::mouse_modifiers(&event), - ); } }, )); } - pub fn on_mouse_press( + pub fn on_mouse_press( &mut self, canvas_common: &super::Common, - mut mouse_handler: M, - mut touch_handler: T, + mut handler: F, ) where - M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), - T: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); self.on_pointer_press = Some(canvas_common.add_user_event( "pointerdown", move |event: PointerEvent| { if event.pointer_type() == "touch" { - touch_handler( + handler( event.pointer_id(), event::touch_position(&event, &canvas) .to_physical(super::super::scale_factor()), Force::Normalized(event.pressure() as f64), ); - } else { - mouse_handler( - event.pointer_id(), - event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event), - event::mouse_modifiers(&event), - ); - - // 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(event.pointer_id()); } }, )); } - pub fn on_cursor_move( + pub fn on_cursor_move( &mut self, canvas_common: &super::Common, - mut mouse_handler: M, - mut touch_handler: T, + mut handler: F, prevent_default: bool, ) where - M: 'static + FnMut(i32, PhysicalPosition, PhysicalPosition, ModifiersState), - T: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); self.on_cursor_move = Some(canvas_common.add_event( @@ -153,19 +86,12 @@ impl PointerHandler { // prevent scroll on mobile web event.prevent_default(); } - touch_handler( + handler( event.pointer_id(), event::touch_position(&event, &canvas) .to_physical(super::super::scale_factor()), Force::Normalized(event.pressure() as f64), ); - } else { - 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()), - event::mouse_modifiers(&event), - ); } }, )); @@ -192,8 +118,6 @@ impl PointerHandler { } 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; From b0f5305cc47a45a643b0169a5251faf3c8982d13 Mon Sep 17 00:00:00 2001 From: JAD3N Date: Sun, 12 Mar 2023 02:41:24 +0000 Subject: [PATCH 2/3] Fix Formatting --- .../web/web_sys/canvas/pointer_handler.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) 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 39614f4406..213a7a1eb9 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -23,11 +23,8 @@ impl PointerHandler { } } - pub fn on_mouse_release( - &mut self, - canvas_common: &super::Common, - mut handler: F, - ) where + pub fn on_mouse_release(&mut self, canvas_common: &super::Common, mut handler: F) + where F: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); @@ -46,11 +43,8 @@ impl PointerHandler { )); } - pub fn on_mouse_press( - &mut self, - canvas_common: &super::Common, - mut handler: F, - ) where + pub fn on_mouse_press(&mut self, canvas_common: &super::Common, mut handler: F) + where F: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); From c4a9a7b5ff9f97d800b9ed212107e6de89eeb971 Mon Sep 17 00:00:00 2001 From: JAD3N Date: Wed, 12 Apr 2023 22:33:29 +0100 Subject: [PATCH 3/3] Simplify Closure --- src/platform_impl/web/web_sys/canvas.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 08871376d3..12ff2ef782 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -100,7 +100,7 @@ impl Canvas { on_fullscreen_change: None, on_dark_mode: None, mouse_handler: mouse_handler::MouseHandler::new(), - pointer_handler: has_pointer_event().then(|| pointer_handler::PointerHandler::new()), + pointer_handler: has_pointer_event().then(pointer_handler::PointerHandler::new), }) }