Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- On macOS, fixed potential panic when getting refresh rate.

# 0.28.3
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ impl<T> EventLoopWindowTarget<T> {
});
});

canvas.on_contextmenu(prevent_default);

let runner = self.runner.clone();
canvas.on_keyboard_press(
move |scancode, virtual_keycode, modifiers| {
Expand Down
96 changes: 55 additions & 41 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -27,13 +30,15 @@ pub struct Canvas {
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
on_blur: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
on_contextmenu: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_keyboard_release: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
on_keyboard_press: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
on_received_character: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
on_mouse_wheel: Option<EventListenerHandle<dyn FnMut(WheelEvent)>>,
on_fullscreen_change: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_dark_mode: Option<MediaQueryListHandle>,
mouse_state: MouseState,
mouse_handler: MouseHandler,
pointer_handler: Option<PointerHandler>,
}

struct Common {
Expand Down Expand Up @@ -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 {
Expand All @@ -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),
})
}

Expand Down Expand Up @@ -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<F>(&mut self, mut handler: F, prevent_default: bool)
where
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
Expand Down Expand Up @@ -248,32 +268,33 @@ 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<F>(&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<M, T>(&mut self, mouse_handler: M, touch_handler: T)
where
M: 'static + FnMut(i32, MouseButton, ModifiersState),
T: 'static + FnMut(i32, PhysicalPosition<f64>, 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);
}
}

Expand All @@ -282,11 +303,10 @@ impl Canvas {
M: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
T: 'static + FnMut(i32, PhysicalPosition<f64>, 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);
}
}

Expand All @@ -299,20 +319,19 @@ impl Canvas {
M: 'static + FnMut(i32, PhysicalPosition<f64>, PhysicalPosition<f64>, ModifiersState),
T: 'static + FnMut(i32, PhysicalPosition<f64>, 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);
}
}

pub fn on_touch_cancel<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, 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);
}
}

Expand Down Expand Up @@ -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();
}
}
}
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/web/web_sys/canvas/mouse_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl MouseHandler {
mouse_capture_state: Rc::new(RefCell::new(MouseCaptureState::NotCaptured)),
}
}

pub fn on_cursor_leave<F>(&mut self, canvas_common: &super::Common, handler: F)
where
F: 'static + FnMut(i32),
Expand Down
Loading