From b8ba7fbde0437c0388d3163f0334698bfc3a96d3 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 01:08:03 +0200 Subject: [PATCH 01/10] Split cursor move handlers --- .../web/event_loop/window_target.rs | 104 +++++++++++------- src/platform_impl/web/web_sys/canvas.rs | 29 ++--- .../web/web_sys/canvas/mouse_handler.rs | 27 ++--- .../web/web_sys/canvas/pointer_handler.rs | 78 +++++++------ 4 files changed, 131 insertions(+), 107 deletions(-) diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index c21b8b0660..74b8984bcc 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(); + + 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(); - let button_event = button.map(|button| { - if buttons.contains(button.into()) { + 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 5ad9456b5e..6c9a7f7821 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 6ee70cf585..6827cac69f 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 80713ce9a6..476855f00e 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"), } } }, From f15f787c71a1dd15b4d46ba19a15549b363f772e Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 01:08:47 +0200 Subject: [PATCH 02/10] Send position on button release --- CHANGELOG.md | 1 + .../web/event_loop/window_target.rs | 80 +++++++++++-------- src/platform_impl/web/web_sys/canvas.rs | 2 +- .../web/web_sys/canvas/mouse_handler.rs | 3 +- .../web/web_sys/canvas/pointer_handler.rs | 3 +- 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index becb07ad0d..191fb1a2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** On Web, `instant` is now replaced by `web_time`. - On Windows, port to `windows-sys` version 0.48.0. - On Web, fix pen treated as mouse input. +- On Web, send mouse position on button release as well. # 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 74b8984bcc..ff76d35677 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -400,43 +400,59 @@ 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_mouse_release( - move |pointer_id, button, active_modifiers| { - let modifiers_changed = - (has_focus_clone.get() && modifiers.get() != active_modifiers).then(|| { - modifiers.set(active_modifiers); + { + let runner = self.runner.clone(); + let modifiers = self.modifiers.clone(); + let has_focus = has_focus.clone(); + + move |pointer_id, position, button, active_modifiers| { + let modifiers_changed = + (has_focus.get() && modifiers.get() != active_modifiers).then(|| { + modifiers.set(active_modifiers); + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers.into()), + } + }); + + // A mouse up 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(modifiers_changed.into_iter().chain([ Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(active_modifiers.into()), - } - }); - - runner.send_events(modifiers_changed.into_iter().chain(iter::once( - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Released, - button, + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, + }, }, - }, - ))); + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Released, + button, + }, + }, + ])); + } }, - 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::Ended, - force: Some(force), - location, - }), - }); + { + let runner_touch = self.runner.clone(); + + 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::Ended, + force: Some(force), + location, + }), + }); + } }, ); diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 6c9a7f7821..2b67643408 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -243,7 +243,7 @@ impl Canvas { pub fn on_mouse_release(&mut self, mouse_handler: M, touch_handler: T) where - M: 'static + FnMut(i32, MouseButton, ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), 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 6827cac69f..73213963f1 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -84,7 +84,7 @@ impl MouseHandler { pub fn on_mouse_release(&mut self, canvas_common: &super::Common, mut handler: F) where - F: 'static + FnMut(i32, MouseButton, ModifiersState), + F: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), { let on_mouse_leave_handler = self.on_mouse_leave_handler.clone(); let mouse_capture_state = self.mouse_capture_state.clone(); @@ -110,6 +110,7 @@ impl MouseHandler { event.stop_propagation(); handler( 0, + event::mouse_position(&event).to_physical(super::super::scale_factor()), event::mouse_button(&event).expect("no mouse button released"), event::mouse_modifiers(&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 476855f00e..105f645b29 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -75,7 +75,7 @@ impl PointerHandler { mut mouse_handler: M, mut touch_handler: T, ) where - M: 'static + FnMut(i32, MouseButton, ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); @@ -91,6 +91,7 @@ impl PointerHandler { ), "mouse" => mouse_handler( event.pointer_id(), + event::mouse_position(&event).to_physical(super::super::scale_factor()), event::mouse_button(&event).expect("no mouse button released"), event::mouse_modifiers(&event), ), From ace1cd36ece2238c671b5dce21a2e87a716cedb0 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 01:09:30 +0200 Subject: [PATCH 03/10] Focus window on touch press --- CHANGELOG.md | 1 + .../web/event_loop/window_target.rs | 107 ++++++++++-------- 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 191fb1a2df..550842b037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Windows, port to `windows-sys` version 0.48.0. - On Web, fix pen treated as mouse input. - On Web, send mouse position on button release as well. +- On Web, fix touch input not gaining or loosing focus. # 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 ff76d35677..3e52e63f5a 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -345,58 +345,73 @@ impl EventLoopWindowTarget { prevent_default, ); - 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_mouse_press( - move |pointer_id, position, button, active_modifiers| { - let focus_changed = - (!has_focus_clone.replace(true)).then_some(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Focused(true), - }); + { + let runner = self.runner.clone(); + let modifiers = self.modifiers.clone(); + let has_focus = has_focus.clone(); - let modifiers_changed = (modifiers.get() != active_modifiers).then(|| { - modifiers.set(active_modifiers); - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(active_modifiers.into()), - } - }); + move |pointer_id, position, button, active_modifiers| { + let focus_changed = + (!has_focus.replace(true)).then_some(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }); - // A mouse down 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(focus_changed.into_iter().chain(modifiers_changed).chain([ - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorMoved { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, + let modifiers_changed = (modifiers.get() != active_modifiers).then(|| { + modifiers.set(active_modifiers); + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers.into()), + } + }); + + // A mouse down 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(focus_changed.into_iter().chain(modifiers_changed).chain([ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, + }, }, - }, - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Pressed, - button, + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Pressed, + button, + }, }, - }, - ])); + ])); + } }, - 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::Started, - force: Some(force), - location, - }), - }); + { + let runner = self.runner.clone(); + let has_focus = has_focus.clone(); + + move |device_id, location, force| { + let focus_changed = + (!has_focus.replace(true)).then_some(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }); + + runner.send_events(focus_changed.into_iter().chain(iter::once( + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: RootDeviceId(DeviceId(device_id)), + phase: TouchPhase::Started, + force: Some(force), + location, + }), + }, + ))); + } }, ); From 560fcddb2c551b46ae9795571f7f3c0d5e0de653 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 01:44:53 +0200 Subject: [PATCH 04/10] Remove `MouseEvent` fallback support --- CHANGELOG.md | 1 + Cargo.toml | 2 - .../web/event_loop/window_target.rs | 18 +- src/platform_impl/web/web_sys/canvas.rs | 136 ++-------- .../web/web_sys/canvas/mouse_handler.rs | 234 ------------------ src/platform_impl/web/web_sys/event_handle.rs | 29 +-- src/platform_impl/web/web_sys/mod.rs | 1 + .../{canvas/pointer_handler.rs => pointer.rs} | 59 +++-- 8 files changed, 64 insertions(+), 416 deletions(-) delete mode 100644 src/platform_impl/web/web_sys/canvas/mouse_handler.rs rename src/platform_impl/web/web_sys/{canvas/pointer_handler.rs => pointer.rs} (84%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 550842b037..5052289b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, fix pen treated as mouse input. - On Web, send mouse position on button release as well. - On Web, fix touch input not gaining or loosing focus. +- **Breaking:** On Web, dropped support for Safari versions below 13. # 0.28.6 diff --git a/Cargo.toml b/Cargo.toml index 44f1d1534e..537ab4a669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,6 @@ package = "web-sys" version = "0.3.22" features = [ 'console', - "AddEventListenerOptions", 'CssStyleDeclaration', 'BeforeUnloadEvent', 'Document', @@ -147,7 +146,6 @@ features = [ 'KeyboardEvent', 'MediaQueryList', 'MediaQueryListEvent', - 'MouseEvent', 'Node', 'PointerEvent', 'Window', diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 3e52e63f5a..da35477c0b 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -352,11 +352,10 @@ impl EventLoopWindowTarget { let has_focus = has_focus.clone(); move |pointer_id, position, button, active_modifiers| { - let focus_changed = - (!has_focus.replace(true)).then_some(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Focused(true), - }); + let focus_changed = (!has_focus.replace(true)).then_some(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }); let modifiers_changed = (modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -393,11 +392,10 @@ impl EventLoopWindowTarget { let has_focus = has_focus.clone(); move |device_id, location, force| { - let focus_changed = - (!has_focus.replace(true)).then_some(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Focused(true), - }); + let focus_changed = (!has_focus.replace(true)).then_some(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }); runner.send_events(focus_changed.into_iter().chain(iter::once( Event::WindowEvent { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 2b67643408..0e8d4b9746 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -1,5 +1,6 @@ use super::event_handle::EventListenerHandle; use super::media_query_handle::MediaQueryListHandle; +use super::pointer::PointerHandler; use super::{event, ButtonsState}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::error::OsError as RootOE; @@ -16,13 +17,9 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{closure::Closure, JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; use web_sys::{ - AddEventListenerOptions, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, - MediaQueryListEvent, MouseEvent, WheelEvent, + Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, WheelEvent, }; -mod mouse_handler; -mod pointer_handler; - #[allow(dead_code)] pub struct Canvas { common: Common, @@ -35,12 +32,12 @@ pub struct Canvas { on_mouse_wheel: Option>, on_fullscreen_change: Option>, on_dark_mode: Option, - mouse_state: MouseState, + pointer_handler: PointerHandler, } -struct Common { +pub struct Common { /// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained. - raw: HtmlCanvasElement, + pub raw: HtmlCanvasElement, wants_fullscreen: Rc>, } @@ -74,12 +71,6 @@ 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()) - }; - Ok(Canvas { common: Common { raw: canvas, @@ -94,7 +85,7 @@ impl Canvas { on_mouse_wheel: None, on_fullscreen_change: None, on_dark_mode: None, - mouse_state, + pointer_handler: PointerHandler::new(), }) } @@ -225,20 +216,14 @@ impl Canvas { where F: 'static + FnMut(i32, ModifiersState), { - 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.pointer_handler.on_cursor_leave(&self.common, handler) } pub fn on_cursor_enter(&mut self, handler: F) where F: 'static + FnMut(i32, ModifiersState), { - 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.pointer_handler.on_cursor_enter(&self.common, handler) } pub fn on_mouse_release(&mut self, mouse_handler: M, touch_handler: T) @@ -246,12 +231,8 @@ 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_release(&self.common, mouse_handler, touch_handler) - } - MouseState::NoPointerEvent(h) => h.on_mouse_release(&self.common, mouse_handler), - } + self.pointer_handler + .on_mouse_release(&self.common, mouse_handler, touch_handler) } pub fn on_mouse_press(&mut self, mouse_handler: M, touch_handler: T) @@ -259,12 +240,8 @@ 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.pointer_handler + .on_mouse_press(&self.common, mouse_handler, touch_handler) } pub fn on_cursor_move( @@ -280,28 +257,21 @@ impl Canvas { 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, - modifier_handler, - mouse_handler, - touch_handler, - button_handler, - prevent_default, - ), - MouseState::NoPointerEvent(h) => { - h.on_cursor_move(&self.common, modifier_handler, mouse_handler) - } - } + self.pointer_handler.on_cursor_move( + &self.common, + modifier_handler, + mouse_handler, + touch_handler, + button_handler, + prevent_default, + ) } pub fn on_touch_cancel(&mut self, handler: F) where F: 'static + FnMut(i32, PhysicalPosition, Force), { - if let MouseState::HasPointerEvent(h) = &mut self.mouse_state { - h.on_touch_cancel(&self.common, handler) - } + self.pointer_handler.on_touch_cancel(&self.common, handler) } pub fn on_mouse_wheel(&mut self, mut handler: F, prevent_default: bool) @@ -358,15 +328,12 @@ impl Canvas { 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.pointer_handler.remove_listeners() } } impl Common { - fn add_event( + pub fn add_event( &self, event_name: &'static str, mut handler: F, @@ -391,7 +358,7 @@ impl Common { // The difference between add_event and add_user_event is that the latter has a special meaning // for browser security. A user event is a deliberate action by the user (like a mouse or key // press) and is the only time things like a fullscreen request may be successfully completed.) - fn add_user_event( + pub fn add_user_event( &self, event_name: &'static str, mut handler: F, @@ -415,43 +382,6 @@ impl Common { }) } - // This function is used exclusively for mouse events (not pointer events). - // Due to the need for mouse capturing, the mouse event handlers are added - // to the window instead of the canvas element, which requires special - // handling to control event propagation. - fn add_window_mouse_event( - &self, - event_name: &'static str, - mut handler: F, - ) -> EventListenerHandle - where - F: 'static + FnMut(MouseEvent), - { - let wants_fullscreen = self.wants_fullscreen.clone(); - let canvas = self.raw.clone(); - let window = web_sys::window().expect("Failed to obtain window"); - - let closure = Closure::wrap(Box::new(move |event: MouseEvent| { - handler(event); - - if *wants_fullscreen.borrow() { - canvas - .request_fullscreen() - .expect("Failed to enter fullscreen"); - *wants_fullscreen.borrow_mut() = false; - } - }) as Box); - - let listener = EventListenerHandle::with_options( - &window, - event_name, - closure, - AddEventListenerOptions::new().capture(true), - ); - - listener - } - pub fn request_fullscreen(&self) { #[wasm_bindgen] extern "C" { @@ -483,21 +413,3 @@ impl Common { super::is_fullscreen(&self.raw) } } - -/// 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 -/// doesn't support pointer events now. -fn has_pointer_event() -> bool { - if let Some(window) = web_sys::window() { - window.get("PointerEvent").is_some() - } else { - false - } -} diff --git a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs deleted file mode 100644 index 73213963f1..0000000000 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ /dev/null @@ -1,234 +0,0 @@ -use super::event; -use super::EventListenerHandle; -use crate::dpi::PhysicalPosition; -use crate::event::MouseButton; -use crate::keyboard::ModifiersState; - -use std::cell::RefCell; -use std::rc::Rc; - -use web_sys::{EventTarget, MouseEvent}; - -type MouseLeaveHandler = Rc>>>; - -#[allow(dead_code)] -pub(super) struct MouseHandler { - on_mouse_leave: Option>, - on_mouse_enter: Option>, - on_mouse_move: Option>, - on_mouse_press: Option>, - on_mouse_release: Option>, - on_mouse_leave_handler: MouseLeaveHandler, - mouse_capture_state: Rc>, -} - -#[derive(PartialEq, Eq)] -pub(super) enum MouseCaptureState { - NotCaptured, - Captured, - OtherElement, -} - -impl MouseHandler { - pub fn new() -> Self { - Self { - on_mouse_leave: None, - on_mouse_enter: None, - on_mouse_move: None, - on_mouse_press: None, - on_mouse_release: None, - on_mouse_leave_handler: Rc::new(RefCell::new(None)), - 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, ModifiersState), - { - *self.on_mouse_leave_handler.borrow_mut() = Some(Box::new(handler)); - let on_mouse_leave_handler = self.on_mouse_leave_handler.clone(); - let mouse_capture_state = self.mouse_capture_state.clone(); - self.on_mouse_leave = Some(canvas_common.add_event( - "mouseout", - move |event: MouseEvent| { - // If the mouse is being captured, it is always considered - // to be "within" the the canvas, until the capture has been - // released, therefore we don't send cursor leave events. - if *mouse_capture_state.borrow() != MouseCaptureState::Captured { - if let Some(handler) = on_mouse_leave_handler.borrow_mut().as_mut() { - let modifiers = event::mouse_modifiers(&event); - handler(0, modifiers); - } - } - }, - )); - } - - pub fn on_cursor_enter(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static + FnMut(i32, ModifiersState), - { - let mouse_capture_state = self.mouse_capture_state.clone(); - self.on_mouse_enter = Some(canvas_common.add_event( - "mouseover", - move |event: MouseEvent| { - // We don't send cursor leave events when the mouse is being - // captured, therefore we do the same with cursor enter events. - if *mouse_capture_state.borrow() != MouseCaptureState::Captured { - let modifiers = event::mouse_modifiers(&event); - handler(0, modifiers); - } - }, - )); - } - - pub fn on_mouse_release(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), - { - let on_mouse_leave_handler = self.on_mouse_leave_handler.clone(); - let mouse_capture_state = self.mouse_capture_state.clone(); - let canvas = canvas_common.raw.clone(); - self.on_mouse_release = Some(canvas_common.add_window_mouse_event( - "mouseup", - move |event: MouseEvent| { - let canvas = canvas.clone(); - let mut mouse_capture_state = mouse_capture_state.borrow_mut(); - match &*mouse_capture_state { - // Shouldn't happen but we'll just ignore it. - MouseCaptureState::NotCaptured => return, - MouseCaptureState::OtherElement => { - if event.buttons() == 0 { - // No buttons are pressed anymore so reset - // the capturing state. - *mouse_capture_state = MouseCaptureState::NotCaptured; - } - return; - } - MouseCaptureState::Captured => {} - } - event.stop_propagation(); - handler( - 0, - event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event).expect("no mouse button released"), - event::mouse_modifiers(&event), - ); - if event - .target() - .map_or(false, |target| target != EventTarget::from(canvas)) - { - // Since we do not send cursor leave events while the - // cursor is being captured, we instead send it after - // the capture has been released. - if let Some(handler) = on_mouse_leave_handler.borrow_mut().as_mut() { - let modifiers = event::mouse_modifiers(&event); - handler(0, modifiers); - } - } - if event.buttons() == 0 { - // No buttons are pressed anymore so reset - // the capturing state. - *mouse_capture_state = MouseCaptureState::NotCaptured; - } - }, - )); - } - - pub fn on_mouse_press(&mut self, canvas_common: &super::Common, mut handler: F) - where - F: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), - { - let mouse_capture_state = self.mouse_capture_state.clone(); - let canvas = canvas_common.raw.clone(); - self.on_mouse_press = Some(canvas_common.add_window_mouse_event( - "mousedown", - move |event: MouseEvent| { - let canvas = canvas.clone(); - let mut mouse_capture_state = mouse_capture_state.borrow_mut(); - match &*mouse_capture_state { - MouseCaptureState::NotCaptured - if event - .target() - .map_or(false, |target| target != EventTarget::from(canvas)) => - { - // The target isn't our canvas which means the - // mouse is pressed outside of it. - *mouse_capture_state = MouseCaptureState::OtherElement; - return; - } - MouseCaptureState::OtherElement => return, - _ => {} - } - *mouse_capture_state = MouseCaptureState::Captured; - event.stop_propagation(); - handler( - 0, - event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event).expect("no mouse button pressed"), - event::mouse_modifiers(&event), - ); - }, - )); - } - - 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 - .target() - .map_or(false, |target| target == EventTarget::from(canvas.clone())); - match &*mouse_capture_state { - // Don't handle hover events outside of canvas. - MouseCaptureState::NotCaptured | MouseCaptureState::OtherElement - if !is_over_canvas => {} - // If hovering over the canvas, just send the cursor move event. - MouseCaptureState::NotCaptured - | MouseCaptureState::OtherElement - | MouseCaptureState::Captured => { - if *mouse_capture_state == MouseCaptureState::Captured { - event.stop_propagation(); - } - let mouse_pos = if is_over_canvas { - event::mouse_position(&event) - } else { - // Since the mouse is not on the canvas, we cannot - // use `offsetX`/`offsetY`. - event::mouse_position_by_client(&event, &canvas) - }; - let mouse_delta = event::mouse_delta(&event); - mouse_handler( - 0, - mouse_pos.to_physical(super::super::scale_factor()), - mouse_delta.to_physical(super::super::scale_factor()), - ); - } - } - }, - )); - } - - pub fn remove_listeners(&mut self) { - self.on_mouse_leave = None; - self.on_mouse_enter = None; - self.on_mouse_move = None; - self.on_mouse_press = None; - self.on_mouse_release = None; - *self.on_mouse_leave_handler.borrow_mut() = None; - } -} diff --git a/src/platform_impl/web/web_sys/event_handle.rs b/src/platform_impl/web/web_sys/event_handle.rs index 5a0a3531f1..9f9be48ad3 100644 --- a/src/platform_impl/web/web_sys/event_handle.rs +++ b/src/platform_impl/web/web_sys/event_handle.rs @@ -1,7 +1,7 @@ use wasm_bindgen::{prelude::Closure, JsCast}; -use web_sys::{AddEventListenerOptions, EventListenerOptions, EventTarget}; +use web_sys::{EventListenerOptions, EventTarget}; -pub(super) struct EventListenerHandle { +pub struct EventListenerHandle { target: EventTarget, event_type: &'static str, listener: Closure, @@ -24,31 +24,6 @@ impl EventListenerHandle { options: EventListenerOptions::new(), } } - - pub fn with_options( - target: &U, - event_type: &'static str, - listener: Closure, - options: &AddEventListenerOptions, - ) -> Self - where - U: Clone + Into, - { - let target = target.clone().into(); - target - .add_event_listener_with_callback_and_add_event_listener_options( - event_type, - listener.as_ref().unchecked_ref(), - options, - ) - .expect("Failed to add event listener"); - EventListenerHandle { - target, - event_type, - listener, - options: options.clone().unchecked_into(), - } - } } impl Drop for EventListenerHandle { diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index 454204c79b..6a1f556ec0 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -2,6 +2,7 @@ mod canvas; mod event; mod event_handle; mod media_query_handle; +mod pointer; mod scaling; mod timeout; diff --git a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs b/src/platform_impl/web/web_sys/pointer.rs similarity index 84% rename from src/platform_impl/web/web_sys/canvas/pointer_handler.rs rename to src/platform_impl/web/web_sys/pointer.rs index 105f645b29..ff1aadb780 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -1,5 +1,6 @@ +use super::canvas::Common; use super::event; -use super::EventListenerHandle; +use super::event_handle::EventListenerHandle; use crate::dpi::PhysicalPosition; use crate::event::{Force, MouseButton}; use crate::keyboard::ModifiersState; @@ -31,7 +32,7 @@ impl PointerHandler { } } - pub fn on_cursor_leave(&mut self, canvas_common: &super::Common, mut handler: F) + pub fn on_cursor_leave(&mut self, canvas_common: &Common, mut handler: F) where F: 'static + FnMut(i32, ModifiersState), { @@ -50,7 +51,7 @@ impl PointerHandler { )); } - pub fn on_cursor_enter(&mut self, canvas_common: &super::Common, mut handler: F) + pub fn on_cursor_enter(&mut self, canvas_common: &Common, mut handler: F) where F: 'static + FnMut(i32, ModifiersState), { @@ -71,7 +72,7 @@ impl PointerHandler { pub fn on_mouse_release( &mut self, - canvas_common: &super::Common, + canvas_common: &Common, mut mouse_handler: M, mut touch_handler: T, ) where @@ -81,29 +82,26 @@ impl PointerHandler { let canvas = canvas_common.raw.clone(); self.on_pointer_release = Some(canvas_common.add_user_event( "pointerup", - move |event: PointerEvent| { - match event.pointer_type().as_str() { - "touch" => touch_handler( - event.pointer_id(), - event::touch_position(&event, &canvas) - .to_physical(super::super::scale_factor()), - Force::Normalized(event.pressure() as f64), - ), - "mouse" => mouse_handler( - event.pointer_id(), - event::mouse_position(&event).to_physical(super::super::scale_factor()), - event::mouse_button(&event).expect("no mouse button released"), - event::mouse_modifiers(&event), - ), - _ => (), - } + move |event: PointerEvent| match event.pointer_type().as_str() { + "touch" => touch_handler( + event.pointer_id(), + event::touch_position(&event, &canvas).to_physical(super::scale_factor()), + Force::Normalized(event.pressure() as f64), + ), + "mouse" => mouse_handler( + event.pointer_id(), + event::mouse_position(&event).to_physical(super::scale_factor()), + event::mouse_button(&event).expect("no mouse button released"), + event::mouse_modifiers(&event), + ), + _ => (), }, )); } pub fn on_mouse_press( &mut self, - canvas_common: &super::Common, + canvas_common: &Common, mut mouse_handler: M, mut touch_handler: T, ) where @@ -119,14 +117,14 @@ impl PointerHandler { touch_handler( event.pointer_id(), event::touch_position(&event, &canvas) - .to_physical(super::super::scale_factor()), + .to_physical(super::scale_factor()), Force::Normalized(event.pressure() as f64), ); } "mouse" => { mouse_handler( event.pointer_id(), - event::mouse_position(&event).to_physical(super::super::scale_factor()), + event::mouse_position(&event).to_physical(super::scale_factor()), event::mouse_button(&event).expect("no mouse button pressed"), event::mouse_modifiers(&event), ); @@ -144,7 +142,7 @@ impl PointerHandler { pub fn on_cursor_move( &mut self, - canvas_common: &super::Common, + canvas_common: &Common, mut modifier_handler: MOD, mut mouse_handler: M, mut touch_handler: T, @@ -196,7 +194,7 @@ impl PointerHandler { button_handler( id, - event::mouse_position(&event).to_physical(super::super::scale_factor()), + event::mouse_position(&event).to_physical(super::scale_factor()), event::mouse_buttons(&event), button, ); @@ -227,13 +225,13 @@ impl PointerHandler { 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()), + event::mouse_position(&event).to_physical(super::scale_factor()), + event::mouse_delta(&event).to_physical(super::scale_factor()), ), "touch" => touch_handler( id, event::touch_position(&event, &canvas) - .to_physical(super::super::scale_factor()), + .to_physical(super::scale_factor()), Force::Normalized(event.pressure() as f64), ), _ => unreachable!("didn't return early before"), @@ -243,7 +241,7 @@ impl PointerHandler { )); } - pub fn on_touch_cancel(&mut self, canvas_common: &super::Common, mut handler: F) + pub fn on_touch_cancel(&mut self, canvas_common: &Common, mut handler: F) where F: 'static + FnMut(i32, PhysicalPosition, Force), { @@ -254,8 +252,7 @@ impl PointerHandler { if event.pointer_type() == "touch" { handler( event.pointer_id(), - event::touch_position(&event, &canvas) - .to_physical(super::super::scale_factor()), + event::touch_position(&event, &canvas).to_physical(super::scale_factor()), Force::Normalized(event.pressure() as f64), ); } From d74afea8c62f849df0dfcd2b3de9e6693c6ba167 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 01:50:30 +0200 Subject: [PATCH 05/10] Prevent text selection --- CHANGELOG.md | 1 + .../web/event_loop/window_target.rs | 35 ++++++------------- src/platform_impl/web/web_sys/canvas.rs | 16 ++++++--- src/platform_impl/web/web_sys/pointer.rs | 15 ++++++++ 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5052289b57..cf7e7d56b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, send mouse position on button release as well. - On Web, fix touch input not gaining or loosing focus. - **Breaking:** On Web, dropped support for Safari versions below 13. +- On Web, prevent clicks on the canvas to select text. # 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 da35477c0b..ef2e64782d 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -349,14 +349,8 @@ impl EventLoopWindowTarget { { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - let has_focus = has_focus.clone(); move |pointer_id, position, button, active_modifiers| { - let focus_changed = (!has_focus.replace(true)).then_some(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Focused(true), - }); - let modifiers_changed = (modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -368,7 +362,7 @@ impl EventLoopWindowTarget { // A mouse down 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(focus_changed.into_iter().chain(modifiers_changed).chain([ + runner.send_events(modifiers_changed.into_iter().chain([ Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::CursorMoved { @@ -389,28 +383,21 @@ impl EventLoopWindowTarget { }, { let runner = self.runner.clone(); - let has_focus = has_focus.clone(); move |device_id, location, force| { - let focus_changed = (!has_focus.replace(true)).then_some(Event::WindowEvent { + runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::Focused(true), - }); - - runner.send_events(focus_changed.into_iter().chain(iter::once( - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), - phase: TouchPhase::Started, - force: Some(force), - location, - }), - }, - ))); + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: RootDeviceId(DeviceId(device_id)), + phase: TouchPhase::Started, + force: Some(force), + location, + }), + }) } }, + prevent_default, ); canvas.on_mouse_release( diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 0e8d4b9746..8ba5c24e00 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -235,13 +235,21 @@ impl Canvas { .on_mouse_release(&self.common, mouse_handler, touch_handler) } - pub fn on_mouse_press(&mut self, mouse_handler: M, touch_handler: T) - where + pub fn on_mouse_press( + &mut self, + mouse_handler: M, + touch_handler: T, + prevent_default: bool, + ) where M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), { - self.pointer_handler - .on_mouse_press(&self.common, mouse_handler, touch_handler) + self.pointer_handler.on_mouse_press( + &self.common, + mouse_handler, + touch_handler, + prevent_default, + ) } pub fn on_cursor_move( diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index ff1aadb780..bb0c786fc1 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -104,6 +104,7 @@ impl PointerHandler { canvas_common: &Common, mut mouse_handler: M, mut touch_handler: T, + prevent_default: bool, ) where M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), T: 'static + FnMut(i32, PhysicalPosition, Force), @@ -112,6 +113,13 @@ impl PointerHandler { self.on_pointer_press = Some(canvas_common.add_user_event( "pointerdown", move |event: PointerEvent| { + if prevent_default { + // prevent text selection + event.prevent_default(); + // but still focus element + let _ = canvas.focus(); + } + match event.pointer_type().as_str() { "touch" => { touch_handler( @@ -192,6 +200,13 @@ impl PointerHandler { "expect pointer type of a chorded button event to be a mouse" ); + if prevent_default { + // prevent text selection + event.prevent_default(); + // but still focus element + let _ = canvas.focus(); + } + button_handler( id, event::mouse_position(&event).to_physical(super::scale_factor()), From 210b3458ff9f2b53dd1a0c9849db3e9d0a41105d Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 02:16:55 +0200 Subject: [PATCH 06/10] Split modifier handling in all pointer events --- .../web/event_loop/window_target.rs | 197 ++++++++++-------- src/platform_impl/web/web_sys/canvas.rs | 42 ++-- src/platform_impl/web/web_sys/pointer.rs | 74 ++++--- 3 files changed, 190 insertions(+), 123 deletions(-) diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index ef2e64782d..f87cf93493 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -198,51 +198,65 @@ impl EventLoopWindowTarget { prevent_default, ); - let runner = self.runner.clone(); - let modifiers = self.modifiers.clone(); - let has_focus_clone = has_focus.clone(); - canvas.on_cursor_leave(move |pointer_id, active_modifiers| { - let modifiers_changed = (has_focus_clone.get() && modifiers.get() != active_modifiers) - .then(|| { - modifiers.set(active_modifiers); - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(active_modifiers.into()), - } - }); + canvas.on_cursor_leave( + { + let runner = self.runner.clone(); + let has_focus = has_focus.clone(); + let modifiers = self.modifiers.clone(); - runner.send_events(modifiers_changed.into_iter().chain(iter::once( - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorLeft { - device_id: RootDeviceId(DeviceId(pointer_id)), - }, - }, - ))); - }); + move |active_modifiers| { + if has_focus.get() && modifiers.get() != active_modifiers { + modifiers.set(active_modifiers); + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers.into()), + }); + } + } + }, + { + let runner = self.runner.clone(); - let runner = self.runner.clone(); - let modifiers = self.modifiers.clone(); - let has_focus_clone = has_focus.clone(); - canvas.on_cursor_enter(move |pointer_id, active_modifiers| { - let modifiers_changed = (has_focus_clone.get() && modifiers.get() != active_modifiers) - .then(|| { - modifiers.set(active_modifiers); - Event::WindowEvent { + move |pointer_id| { + runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(active_modifiers.into()), + event: WindowEvent::CursorLeft { + device_id: RootDeviceId(DeviceId(pointer_id)), + }, + }); + } + }, + ); + + canvas.on_cursor_enter( + { + 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); + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers.into()), + }); } - }); + } + }, + { + let runner = self.runner.clone(); - runner.send_events(modifiers_changed.into_iter().chain(iter::once( - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorEntered { - device_id: RootDeviceId(DeviceId(pointer_id)), - }, - }, - ))); - }); + move |pointer_id| { + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorEntered { + device_id: RootDeviceId(DeviceId(pointer_id)), + }, + }); + } + }, + ); canvas.on_cursor_move( { @@ -350,35 +364,43 @@ impl EventLoopWindowTarget { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - move |pointer_id, position, button, active_modifiers| { - let modifiers_changed = (modifiers.get() != active_modifiers).then(|| { + move |active_modifiers| { + if 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(); + move |pointer_id, position, button| { // A mouse down 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(modifiers_changed.into_iter().chain([ - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorMoved { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, + runner.send_events( + [ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, + }, }, - }, - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Pressed, - button, + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Pressed, + button, + }, }, - }, - ])); + ] + .into_iter(), + ); } }, { @@ -403,39 +425,46 @@ impl EventLoopWindowTarget { canvas.on_mouse_release( { let runner = self.runner.clone(); - let modifiers = self.modifiers.clone(); let has_focus = has_focus.clone(); + let modifiers = self.modifiers.clone(); - move |pointer_id, position, button, active_modifiers| { - let modifiers_changed = - (has_focus.get() && modifiers.get() != active_modifiers).then(|| { - modifiers.set(active_modifiers); - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::ModifiersChanged(active_modifiers.into()), - } + move |active_modifiers| { + if has_focus.get() && modifiers.get() != active_modifiers { + modifiers.set(active_modifiers); + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers.into()), }); + } + } + }, + { + let runner = self.runner.clone(); + move |pointer_id, position, button| { // A mouse up 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(modifiers_changed.into_iter().chain([ - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::CursorMoved { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, + runner.send_events( + [ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, + }, }, - }, - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Released, - button, + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Released, + button, + }, }, - }, - ])); + ] + .into_iter(), + ); } }, { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 8ba5c24e00..a7faeea45d 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -212,40 +212,56 @@ impl Canvas { )); } - pub fn on_cursor_leave(&mut self, handler: F) + pub fn on_cursor_leave(&mut self, modifier_handler: MOD, mouse_handler: M) where - F: 'static + FnMut(i32, ModifiersState), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32), { - self.pointer_handler.on_cursor_leave(&self.common, handler) + self.pointer_handler + .on_cursor_leave(&self.common, modifier_handler, mouse_handler) } - pub fn on_cursor_enter(&mut self, handler: F) + pub fn on_cursor_enter(&mut self, modifier_handler: MOD, mouse_handler: M) where - F: 'static + FnMut(i32, ModifiersState), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32), { - self.pointer_handler.on_cursor_enter(&self.common, handler) + self.pointer_handler + .on_cursor_enter(&self.common, modifier_handler, mouse_handler) } - pub fn on_mouse_release(&mut self, mouse_handler: M, touch_handler: T) - where - M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), + pub fn on_mouse_release( + &mut self, + modifier_handler: MOD, + mouse_handler: M, + touch_handler: T, + ) where + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton), T: 'static + FnMut(i32, PhysicalPosition, Force), { - self.pointer_handler - .on_mouse_release(&self.common, mouse_handler, touch_handler) + self.pointer_handler.on_mouse_release( + &self.common, + modifier_handler, + mouse_handler, + touch_handler, + ) } - pub fn on_mouse_press( + pub fn on_mouse_press( &mut self, + modifier_handler: MOD, mouse_handler: M, touch_handler: T, prevent_default: bool, ) where - M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton), T: 'static + FnMut(i32, PhysicalPosition, Force), { self.pointer_handler.on_mouse_press( &self.common, + modifier_handler, mouse_handler, touch_handler, prevent_default, diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index bb0c786fc1..c21fa4c351 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -32,13 +32,20 @@ impl PointerHandler { } } - pub fn on_cursor_leave(&mut self, canvas_common: &Common, mut handler: F) - where - F: 'static + FnMut(i32, ModifiersState), + pub fn on_cursor_leave( + &mut self, + canvas_common: &Common, + mut modifier_handler: MOD, + mut mouse_handler: M, + ) where + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32), { self.on_cursor_leave = Some(canvas_common.add_event( "pointerout", move |event: PointerEvent| { + modifier_handler(event::mouse_modifiers(&event)); + // touch events are handled separately // handling them here would produce duplicate mouse events, inconsistent with // other platforms. @@ -46,18 +53,25 @@ impl PointerHandler { return; } - handler(event.pointer_id(), event::mouse_modifiers(&event)); + mouse_handler(event.pointer_id()); }, )); } - pub fn on_cursor_enter(&mut self, canvas_common: &Common, mut handler: F) - where - F: 'static + FnMut(i32, ModifiersState), + pub fn on_cursor_enter( + &mut self, + canvas_common: &Common, + mut modifier_handler: MOD, + mut mouse_handler: M, + ) where + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32), { self.on_cursor_enter = Some(canvas_common.add_event( "pointerover", move |event: PointerEvent| { + modifier_handler(event::mouse_modifiers(&event)); + // touch events are handled separately // handling them here would produce duplicate mouse events, inconsistent with // other platforms. @@ -65,48 +79,55 @@ impl PointerHandler { return; } - handler(event.pointer_id(), event::mouse_modifiers(&event)); + mouse_handler(event.pointer_id()); }, )); } - pub fn on_mouse_release( + pub fn on_mouse_release( &mut self, canvas_common: &Common, + mut modifier_handler: MOD, mut mouse_handler: M, mut touch_handler: T, ) where - M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton), T: '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| match event.pointer_type().as_str() { - "touch" => touch_handler( - event.pointer_id(), - event::touch_position(&event, &canvas).to_physical(super::scale_factor()), - Force::Normalized(event.pressure() as f64), - ), - "mouse" => mouse_handler( - event.pointer_id(), - event::mouse_position(&event).to_physical(super::scale_factor()), - event::mouse_button(&event).expect("no mouse button released"), - event::mouse_modifiers(&event), - ), - _ => (), + move |event: PointerEvent| { + modifier_handler(event::mouse_modifiers(&event)); + + match event.pointer_type().as_str() { + "touch" => touch_handler( + event.pointer_id(), + event::touch_position(&event, &canvas).to_physical(super::scale_factor()), + Force::Normalized(event.pressure() as f64), + ), + "mouse" => mouse_handler( + event.pointer_id(), + event::mouse_position(&event).to_physical(super::scale_factor()), + event::mouse_button(&event).expect("no mouse button released"), + ), + _ => (), + } }, )); } - pub fn on_mouse_press( + pub fn on_mouse_press( &mut self, canvas_common: &Common, + mut modifier_handler: MOD, mut mouse_handler: M, mut touch_handler: T, prevent_default: bool, ) where - M: 'static + FnMut(i32, PhysicalPosition, MouseButton, ModifiersState), + MOD: 'static + FnMut(ModifiersState), + M: 'static + FnMut(i32, PhysicalPosition, MouseButton), T: 'static + FnMut(i32, PhysicalPosition, Force), { let canvas = canvas_common.raw.clone(); @@ -120,6 +141,8 @@ impl PointerHandler { let _ = canvas.focus(); } + modifier_handler(event::mouse_modifiers(&event)); + match event.pointer_type().as_str() { "touch" => { touch_handler( @@ -134,7 +157,6 @@ impl PointerHandler { event.pointer_id(), event::mouse_position(&event).to_physical(super::scale_factor()), 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 From 67ca86fa106da30283ab82a3668de06a26265431 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 12:18:38 +0200 Subject: [PATCH 07/10] Take `IntoIterator` in `send_events()` --- src/platform_impl/web/event_loop/runner.rs | 2 +- .../web/event_loop/window_target.rs | 116 ++++++++---------- 2 files changed, 53 insertions(+), 65 deletions(-) diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index c28a28e576..7134088f7a 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -192,7 +192,7 @@ impl Shared { // Add a series of events to the event loop runner // // It will determine if the event should be immediately sent to the user or buffered for later - pub fn send_events(&self, events: impl Iterator>) { + pub fn send_events(&self, events: impl IntoIterator>) { // If the event loop is closed, it should discard any new events if self.is_closed() { return; diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index f87cf93493..310c0a86a2 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -278,24 +278,21 @@ impl EventLoopWindowTarget { let runner = self.runner.clone(); 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 { + runner.send_events([ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { device_id: RootDeviceId(DeviceId(pointer_id)), - event: DeviceEvent::MouseMotion { - delta: (delta.x, delta.y), - }, + position, }, - ] - .into_iter(), - ); + }, + Event::DeviceEvent { + device_id: RootDeviceId(DeviceId(pointer_id)), + event: DeviceEvent::MouseMotion { + delta: (delta.x, delta.y), + }, + }, + ]); } }, { @@ -341,19 +338,16 @@ impl EventLoopWindowTarget { // 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 { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, - }, + runner.send_events([ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, }, - button_event, - ] - .into_iter(), - ); + }, + button_event, + ]); } }, prevent_default, @@ -381,26 +375,23 @@ impl EventLoopWindowTarget { // A mouse down 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 { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, - }, + runner.send_events([ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, }, - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Pressed, - button, - }, + }, + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Pressed, + button, }, - ] - .into_iter(), - ); + }, + ]); } }, { @@ -445,26 +436,23 @@ impl EventLoopWindowTarget { // A mouse up 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 { - device_id: RootDeviceId(DeviceId(pointer_id)), - position, - }, + runner.send_events([ + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::CursorMoved { + device_id: RootDeviceId(DeviceId(pointer_id)), + position, }, - Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseInput { - device_id: RootDeviceId(DeviceId(pointer_id)), - state: ElementState::Released, - button, - }, + }, + Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseInput { + device_id: RootDeviceId(DeviceId(pointer_id)), + state: ElementState::Released, + button, }, - ] - .into_iter(), - ); + }, + ]); } }, { From 84fb331df6b570738b5bc6a8948bd7a0eae7644f Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 12:46:03 +0200 Subject: [PATCH 08/10] Remove unnecessary `preventDefault()` calls --- src/platform_impl/web/event_loop/window_target.rs | 1 - src/platform_impl/web/web_sys/canvas.rs | 8 -------- src/platform_impl/web/web_sys/pointer.rs | 8 +------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 310c0a86a2..d1ba53e869 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -91,7 +91,6 @@ impl EventLoopWindowTarget { canvas.set_attribute("data-raw-handle", &id.0.to_string()); canvas.on_touch_start(prevent_default); - canvas.on_touch_end(prevent_default); let runner = self.runner.clone(); let has_focus_clone = has_focus.clone(); diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index a7faeea45d..8d3d5a07ee 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -138,14 +138,6 @@ impl Canvas { })); } - pub fn on_touch_end(&mut self, prevent_default: bool) { - self.on_touch_end = Some(self.common.add_event("touchend", move |event: Event| { - if prevent_default { - event.prevent_default(); - } - })); - } - pub fn on_blur(&mut self, mut handler: F) where F: 'static + FnMut(), diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index c21fa4c351..c24355b62e 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -203,13 +203,7 @@ impl PointerHandler { let pointer_type = event.pointer_type(); match pointer_type.as_str() { - "touch" => { - if prevent_default { - // prevent scroll on mobile web - event.prevent_default(); - } - } - "mouse" => (), + "touch" | "mouse" => (), _ => return, } From ece6962e9d9ec4e76f3fe07461978aac6971d6f3 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sun, 4 Jun 2023 13:15:43 +0200 Subject: [PATCH 09/10] Add `pointerrawupdate` support --- CHANGELOG.md | 1 + src/platform_impl/web/web_sys/pointer.rs | 30 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf7e7d56b8..1cb6b07ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, fix touch input not gaining or loosing focus. - **Breaking:** On Web, dropped support for Safari versions below 13. - On Web, prevent clicks on the canvas to select text. +- On Web, use high-frequency pointer input events when supported by the browser. # 0.28.6 diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index c24355b62e..aab89d29ca 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -6,9 +6,10 @@ use crate::event::{Force, MouseButton}; use crate::keyboard::ModifiersState; use event::ButtonsState; +use once_cell::unsync::OnceCell; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsCast, JsValue}; -use web_sys::PointerEvent; +use web_sys::{HtmlCanvasElement, PointerEvent}; #[allow(dead_code)] pub(super) struct PointerHandler { @@ -186,7 +187,11 @@ impl PointerHandler { { let canvas = canvas_common.raw.clone(); self.on_cursor_move = Some(canvas_common.add_event( - "pointermove", + if has_pointer_raw_support(&canvas) { + "pointerrawupdate" + } else { + "pointermove" + }, move |event: PointerEvent| { // coalesced events are not available on Safari #[wasm_bindgen] @@ -300,3 +305,24 @@ impl PointerHandler { self.on_touch_cancel = None; } } + +fn has_pointer_raw_support(canvas: &HtmlCanvasElement) -> bool { + thread_local! { + static POINTER_RAW_SUPPORT: OnceCell = OnceCell::new(); + } + + POINTER_RAW_SUPPORT.with(|support| { + *support.get_or_init(|| { + #[wasm_bindgen] + extern "C" { + type HtmlCanvasElementExt; + + #[wasm_bindgen(method, getter, js_name = onpointerrawupdate)] + fn has_on_pointerrawupdate(this: &HtmlCanvasElementExt) -> JsValue; + } + + let canvas: &HtmlCanvasElementExt = canvas.unchecked_ref(); + !canvas.has_on_pointerrawupdate().is_undefined() + }) + }) +} From d9625883a69a2f568b999e1f5541afe909c4bea3 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Mon, 5 Jun 2023 01:47:01 +0200 Subject: [PATCH 10/10] Document unpreventable events --- src/platform/web.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/web.rs b/src/platform/web.rs index f78a01fb02..2ab468d889 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -26,6 +26,9 @@ pub trait WindowBuilderExtWebSys { /// /// For example, mouse wheel events are only handled by the canvas by default. This avoids /// the default behavior of scrolling the page. + /// + /// Some events are impossible to prevent. E.g. Firefox allows to access the native browser + /// context menu with Shift+Rightclick. fn with_prevent_default(self, prevent_default: bool) -> Self; /// Whether the canvas should be focusable using the tab key. This is necessary to capture