Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge winit 0.22.2 #1

Open
wants to merge 59 commits into
base: wasm
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
28023d9
upgrades x11-dl to 2.18.5 to fix #376 (#1517)
Mar 26, 2020
0bc58f6
Fix warnings (#1530)
murarth Apr 10, 2020
a8e777a
Fix a possible double-borrow during event handling (#1512)
ryanisaacg Apr 11, 2020
1f24a09
Implement `requestAnimationFrame` for web (#1519)
chemicstry Apr 11, 2020
d560972
Bump version to 0.22.1 (#1537)
ryanisaacg Apr 17, 2020
4c4d091
control_flow example: fix wait_cancelled logic again (#1511)
filnet Apr 19, 2020
6dae994
Mention raw-window-handle in library docs (#1528)
Ralith Apr 19, 2020
78a62ec
Added more docs.rs targets (#1521)
simlay Apr 19, 2020
aabe42d
Preserve with_maximized on windows (#1515)
yanchith Apr 19, 2020
849b8f5
Clarify when RedrawRequested is useful (#1529)
Ralith Apr 19, 2020
47ff8d6
Document that platforms will display garbage data in the window by de…
Osspial Apr 20, 2020
54bc41f
Implement `Drop` for `Proxy` on macOS platform (#1526)
hecrj Apr 20, 2020
114fe9d
wayland: rework scale factor handling (#1538)
Matthias-Fauconneau Apr 22, 2020
26775fa
Report mouse motion before click (#1490)
chrisduerr Apr 26, 2020
9975ced
update macos libs
j4qfrost Apr 27, 2020
36ab890
modify dependency
j4qfrost May 2, 2020
f3b4992
changelog
j4qfrost May 2, 2020
b4c6cdf
Fix several crashes on Windows by heavily simplifying the event loop …
Osspial May 4, 2020
007b195
iOS: convert touch positions to physical (#1551)
francesca64 May 4, 2020
b882810
add android NDK event loop (#1556)
JasperDeSutter May 6, 2020
8f230f4
update core-video-sys version
j4qfrost May 6, 2020
3c38afd
Update macOS dependencies (#1554)
j4qfrost May 8, 2020
aeb4866
Merge remote-tracking branch 'upstream/master'
j4qfrost May 8, 2020
501341d
move changelog line
j4qfrost May 8, 2020
c7a33f9
Fixed a couple of typos in repo description (#1568)
May 15, 2020
bc19c04
Fixed changelog line for core-* dependencies (#1561)
j4qfrost May 15, 2020
878c179
Implement Clone for 'static events (#1478)
semtexzv May 15, 2020
49bcec1
Release 0.22.2 (#1570)
Osspial May 16, 2020
6cfddfe
Prevent the default browser behavior of events (#1576)
ryanisaacg May 21, 2020
ff66bdd
On Wayland, fix deadlock when calling set_inner_size from event loop
kchibisov May 22, 2020
03335ce
macOS: add function to hide other applications
freamon May 24, 2020
a4121a2
platform_impl/linux/x11: fix deadlock in fn set_fullscreen_inner (#1579)
May 27, 2020
5a6cfc3
Macos fullscreen & dialog support with `run_return` (#1581)
VZout Jun 9, 2020
c1ea0dd
On Unix, add option to pick backends
ogoffart Jun 15, 2020
4b1b314
Test x11 and wayland features on CI
murarth Jun 15, 2020
bf62103
Android run return (#1604)
VZout Jun 17, 2020
2191e9e
macOS: Support click-dragging out of a window (#1607)
ozkriff Jun 20, 2020
b1e22aa
Make drag and drop optional (fixes OleInitialize failure #1255) (#1524)
chemicstry Jun 28, 2020
dd866a7
On Windows, fix bug where we'd try to emit `MainEventsCleared` events…
Osspial Jul 2, 2020
3d5d05e
Move available_monitors and primary_monitor to EventLoopWindowTarget …
AdminXVII Jul 4, 2020
6919c2f
Fix misspellings in comments (#1618)
kraai Jul 9, 2020
55dff53
Fix Window platform support documentation
chrisduerr Jul 26, 2020
40232d4
Use `PhysicalPosition` in `PixelDelta` event
chrisduerr Jul 26, 2020
7a49c88
Fix with_fullscreen signature
chrisduerr Aug 1, 2020
05fdcb5
Web: Use mouse events instead of pointer events if the latter isn't s…
hayashi-stl Aug 5, 2020
6810010
android: fix event loop polling (#1638)
msiglreith Aug 12, 2020
514ab04
[macos] add NSWindow.hasShadow support (#1637)
TakWolf Aug 13, 2020
412bd94
Renamed NSString to NSStringRust to support Debug View Heirarchy in X…
simlay Aug 14, 2020
9c72cc2
Fix HiDPI vs. set_cursor_icon for web (#1652)
michaelkirk Aug 17, 2020
89d4c06
Fix crash on NetBSD
chrisduerr Aug 20, 2020
6ba583d
Fix vertical scroll being inverted on web targets (#1665)
alvinhochun Aug 21, 2020
0f7c82d
Send CursorMove before mouse press event and note that touch is unimp…
alvinhochun Aug 22, 2020
bea6093
Use send_events instead of send_event in web backend (#1681)
alvinhochun Aug 26, 2020
02a34a1
Impl. mouse capturing on web target (#1672)
alvinhochun Aug 29, 2020
a2db4c0
Unify Minus/Subtract virtual keycodes
chrisduerr Aug 29, 2020
658a9a4
Handle scale factor change on web-sys backend (#1690)
alvinhochun Aug 30, 2020
4a1939c
Merge branch 'wasm' of github.com:amethyst/winit into wasm
j4qfrost Sep 4, 2020
a764327
Merge branch 'master' into wasm
j4qfrost Sep 4, 2020
c142719
housekeeping
j4qfrost Sep 4, 2020
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
Prev Previous commit
Next Next commit
Impl. mouse capturing on web target (rust-windowing#1672)
* Impl. mouse capturing for web-sys with PointerEvent

* Impl. mouse capturing for web-sys with MouseEvent by manual tracking

* Reorganize web-sys backend mouse and pointer handling code

* Impl. mouse capturing for stdweb with PointerEvent

* Add mouse capturing for web target to changelog
alvinhochun authored Aug 29, 2020

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
commit 02a34a167ab281d7cca9908f67928b659d428b39
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
- On Android, fix `ControlFlow::Poll` not polling the Android event queue.
- On macOS, add `NSWindow.hasShadow` support.
- On Web, fix vertical mouse wheel scrolling being inverted.
- On Web, implement mouse capturing for click-dragging out of the canvas.
- **Breaking:** On Web, `set_cursor_position` and `set_cursor_grab` will now always return an error.
- **Breaking:** `PixelDelta` scroll events now return a `PhysicalPosition`.
- On NetBSD, fixed crash due to incorrect detection of the main thread.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -97,6 +97,7 @@ version = "0.3.22"
optional = true
features = [
'console',
"AddEventListenerOptions",
'CssStyleDeclaration',
'BeforeUnloadEvent',
'Document',
4 changes: 4 additions & 0 deletions src/platform_impl/web/stdweb/canvas.rs
Original file line number Diff line number Diff line change
@@ -202,13 +202,17 @@ impl Canvas {
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
{
let canvas = self.raw.clone();
self.on_mouse_press = Some(self.add_user_event(move |event: PointerDownEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
canvas
.set_pointer_capture(event.pointer_id())
.expect("Failed to set pointer capture");
}));
}

250 changes: 122 additions & 128 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
@@ -9,36 +9,33 @@ use std::rc::Rc;

use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{
Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, MouseEvent,
PointerEvent, WheelEvent,
AddEventListenerOptions, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent,
MediaQueryListEvent, MouseEvent, WheelEvent,
};

mod mouse_handler;
mod pointer_handler;

pub struct Canvas {
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
raw: HtmlCanvasElement,
common: Common,
on_focus: Option<Closure<dyn FnMut(FocusEvent)>>,
on_blur: Option<Closure<dyn FnMut(FocusEvent)>>,
on_keyboard_release: Option<Closure<dyn FnMut(KeyboardEvent)>>,
on_keyboard_press: Option<Closure<dyn FnMut(KeyboardEvent)>>,
on_received_character: Option<Closure<dyn FnMut(KeyboardEvent)>>,
on_cursor_leave: Option<Closure<dyn FnMut(PointerEvent)>>,
on_cursor_enter: Option<Closure<dyn FnMut(PointerEvent)>>,
on_cursor_move: Option<Closure<dyn FnMut(PointerEvent)>>,
on_pointer_press: Option<Closure<dyn FnMut(PointerEvent)>>,
on_pointer_release: Option<Closure<dyn FnMut(PointerEvent)>>,
// Fallback events when pointer event support is missing
on_mouse_leave: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_enter: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_move: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_press: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_release: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_wheel: Option<Closure<dyn FnMut(WheelEvent)>>,
on_fullscreen_change: Option<Closure<dyn FnMut(Event)>>,
wants_fullscreen: Rc<RefCell<bool>>,
on_dark_mode: Option<Closure<dyn FnMut(MediaQueryListEvent)>>,
mouse_state: MouseState,
}

struct Common {
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
raw: HtmlCanvasElement,
wants_fullscreen: Rc<RefCell<bool>>,
}

impl Drop for Canvas {
impl Drop for Common {
fn drop(&mut self) {
self.raw.remove();
}
@@ -72,38 +69,38 @@ impl Canvas {
.set_attribute("tabindex", "0")
.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 {
raw: canvas,
common: Common {
raw: canvas,
wants_fullscreen: Rc::new(RefCell::new(false)),
},
on_blur: None,
on_focus: None,
on_keyboard_release: None,
on_keyboard_press: None,
on_received_character: None,
on_cursor_leave: None,
on_cursor_enter: None,
on_cursor_move: None,
on_pointer_release: None,
on_pointer_press: None,
on_mouse_leave: None,
on_mouse_enter: None,
on_mouse_move: None,
on_mouse_press: None,
on_mouse_release: None,
on_mouse_wheel: None,
on_fullscreen_change: None,
wants_fullscreen: Rc::new(RefCell::new(false)),
on_dark_mode: None,
mouse_state,
})
}

pub fn set_attribute(&self, attribute: &str, value: &str) {
self.raw
self.common
.raw
.set_attribute(attribute, value)
.expect(&format!("Set attribute: {}", attribute));
}

pub fn position(&self) -> LogicalPosition<f64> {
let bounds = self.raw.get_bounding_client_rect();
let bounds = self.common.raw.get_bounding_client_rect();

LogicalPosition {
x: bounds.x(),
@@ -113,20 +110,20 @@ impl Canvas {

pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize {
width: self.raw.width(),
height: self.raw.height(),
width: self.common.raw.width(),
height: self.common.raw.height(),
}
}

pub fn raw(&self) -> &HtmlCanvasElement {
&self.raw
&self.common.raw
}

pub fn on_blur<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(),
{
self.on_blur = Some(self.add_event("blur", move |_: FocusEvent| {
self.on_blur = Some(self.common.add_event("blur", move |_: FocusEvent| {
handler();
}));
}
@@ -135,7 +132,7 @@ impl Canvas {
where
F: 'static + FnMut(),
{
self.on_focus = Some(self.add_event("focus", move |_: FocusEvent| {
self.on_focus = Some(self.common.add_event("focus", move |_: FocusEvent| {
handler();
}));
}
@@ -144,30 +141,34 @@ impl Canvas {
where
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
{
self.on_keyboard_release =
Some(self.add_user_event("keyup", move |event: KeyboardEvent| {
self.on_keyboard_release = Some(self.common.add_user_event(
"keyup",
move |event: KeyboardEvent| {
event.prevent_default();
handler(
event::scan_code(&event),
event::virtual_key_code(&event),
event::keyboard_modifiers(&event),
);
}));
},
));
}

pub fn on_keyboard_press<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
{
self.on_keyboard_press =
Some(self.add_user_event("keydown", move |event: KeyboardEvent| {
self.on_keyboard_press = Some(self.common.add_user_event(
"keydown",
move |event: KeyboardEvent| {
event.prevent_default();
handler(
event::scan_code(&event),
event::virtual_key_code(&event),
event::keyboard_modifiers(&event),
);
}));
},
));
}

pub fn on_received_character<F>(&mut self, mut handler: F)
@@ -179,131 +180,69 @@ impl Canvas {
// The `keypress` event is deprecated, but there does not seem to be a
// viable/compatible alternative as of now. `beforeinput` is still widely
// unsupported.
self.on_received_character = Some(self.add_user_event(
self.on_received_character = Some(self.common.add_user_event(
"keypress",
move |event: KeyboardEvent| {
handler(event::codepoint(&event));
},
));
}

pub fn on_cursor_leave<F>(&mut self, mut handler: F)
pub fn on_cursor_leave<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32),
{
if has_pointer_event() {
self.on_cursor_leave =
Some(self.add_event("pointerout", move |event: PointerEvent| {
handler(event.pointer_id());
}));
} else {
self.on_mouse_leave = Some(self.add_event("mouseout", move |_: MouseEvent| {
handler(0);
}));
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, mut handler: F)
pub fn on_cursor_enter<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32),
{
if has_pointer_event() {
self.on_cursor_enter =
Some(self.add_event("pointerover", move |event: PointerEvent| {
handler(event.pointer_id());
}));
} else {
self.on_mouse_enter = Some(self.add_event("mouseover", move |_: MouseEvent| {
handler(0);
}));
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<F>(&mut self, mut handler: F)
pub fn on_mouse_release<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32, MouseButton, ModifiersState),
{
if has_pointer_event() {
self.on_pointer_release = Some(self.add_user_event(
"pointerup",
move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
},
));
} else {
self.on_mouse_release =
Some(self.add_user_event("mouseup", move |event: MouseEvent| {
handler(
0,
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
}));
match &mut self.mouse_state {
MouseState::HasPointerEvent(h) => h.on_mouse_release(&self.common, handler),
MouseState::NoPointerEvent(h) => h.on_mouse_release(&self.common, handler),
}
}

pub fn on_mouse_press<F>(&mut self, mut handler: F)
pub fn on_mouse_press<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
{
if has_pointer_event() {
self.on_pointer_press = Some(self.add_user_event(
"pointerdown",
move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
},
));
} else {
self.on_mouse_press =
Some(self.add_user_event("mousedown", move |event: MouseEvent| {
handler(
0,
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
}));
match &mut self.mouse_state {
MouseState::HasPointerEvent(h) => h.on_mouse_press(&self.common, handler),
MouseState::NoPointerEvent(h) => h.on_mouse_press(&self.common, handler),
}
}

pub fn on_cursor_move<F>(&mut self, mut handler: F)
pub fn on_cursor_move<F>(&mut self, handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, ModifiersState),
{
if has_pointer_event() {
self.on_cursor_move =
Some(self.add_event("pointermove", move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_modifiers(&event),
);
}));
} else {
self.on_mouse_move = Some(self.add_event("mousemove", move |event: MouseEvent| {
handler(
0,
event::mouse_position(&event).to_physical(super::scale_factor()),
event::mouse_modifiers(&event),
);
}));
match &mut self.mouse_state {
MouseState::HasPointerEvent(h) => h.on_cursor_move(&self.common, handler),
MouseState::NoPointerEvent(h) => h.on_cursor_move(&self.common, handler),
}
}

pub fn on_mouse_wheel<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
{
self.on_mouse_wheel = Some(self.add_event("wheel", move |event: WheelEvent| {
self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| {
event.prevent_default();
if let Some(delta) = event::mouse_scroll_delta(&event) {
handler(0, delta, event::mouse_modifiers(&event));
@@ -315,8 +254,10 @@ impl Canvas {
where
F: 'static + FnMut(),
{
self.on_fullscreen_change =
Some(self.add_event("fullscreenchange", move |_: Event| handler()));
self.on_fullscreen_change = Some(
self.common
.add_event("fullscreenchange", move |_: Event| handler()),
);
}

pub fn on_dark_mode<F>(&mut self, mut handler: F)
@@ -341,6 +282,16 @@ impl Canvas {
});
}

pub fn request_fullscreen(&self) {
self.common.request_fullscreen()
}

pub fn is_fullscreen(&self) -> bool {
self.common.is_fullscreen()
}
}

impl Common {
fn add_event<E, F>(&self, event_name: &str, mut handler: F) -> Closure<dyn FnMut(E)>
where
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
@@ -386,6 +337,44 @@ impl Canvas {
})
}

// 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<F>(
&self,
event_name: &str,
mut handler: F,
) -> Closure<dyn FnMut(MouseEvent)>
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<dyn FnMut(_)>);

window
.add_event_listener_with_callback_and_add_event_listener_options(
event_name,
&closure.as_ref().unchecked_ref(),
AddEventListenerOptions::new().capture(true),
)
.expect("Failed to add event listener with callback and options");

closure
}

pub fn request_fullscreen(&self) {
*self.wants_fullscreen.borrow_mut() = true;
}
@@ -395,6 +384,11 @@ impl Canvas {
}
}

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
203 changes: 203 additions & 0 deletions src/platform_impl/web/web_sys/canvas/mouse_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use super::event;
use crate::dpi::PhysicalPosition;
use crate::event::{ModifiersState, MouseButton};

use std::cell::RefCell;
use std::rc::Rc;

use wasm_bindgen::closure::Closure;
use web_sys::{EventTarget, MouseEvent};

pub(super) struct MouseHandler {
on_mouse_leave: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_enter: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_move: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_press: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_release: Option<Closure<dyn FnMut(MouseEvent)>>,
on_mouse_leave_handler: Rc<RefCell<Option<Box<dyn FnMut(i32)>>>>,
mouse_capture_state: Rc<RefCell<MouseCaptureState>>,
}

#[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<F>(&mut self, canvas_common: &super::Common, handler: F)
where
F: 'static + FnMut(i32),
{
*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 |_: 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() {
handler(0);
}
}
}));
}

pub fn on_cursor_enter<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32),
{
let mouse_capture_state = self.mouse_capture_state.clone();
self.on_mouse_enter = Some(canvas_common.add_event("mouseover", move |_: 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 {
handler(0);
}
}));
}

pub fn on_mouse_release<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32, 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_button(&event),
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() {
handler(0);
}
}
if event.buttons() == 0 {
// No buttons are pressed anymore so reset
// the capturing state.
*mouse_capture_state = MouseCaptureState::NotCaptured;
}
},
));
}

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

pub fn on_cursor_move<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, ModifiersState),
{
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| {
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 if !is_over_canvas => return,
MouseCaptureState::OtherElement if !is_over_canvas => return,
// 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)
};
handler(
0,
mouse_pos.to_physical(super::super::scale_factor()),
event::mouse_modifiers(&event),
);
}
}
},
));
}
}
103 changes: 103 additions & 0 deletions src/platform_impl/web/web_sys/canvas/pointer_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use super::event;
use crate::dpi::PhysicalPosition;
use crate::event::{ModifiersState, MouseButton};

use wasm_bindgen::closure::Closure;
use web_sys::PointerEvent;

pub(super) struct PointerHandler {
on_cursor_leave: Option<Closure<dyn FnMut(PointerEvent)>>,
on_cursor_enter: Option<Closure<dyn FnMut(PointerEvent)>>,
on_cursor_move: Option<Closure<dyn FnMut(PointerEvent)>>,
on_pointer_press: Option<Closure<dyn FnMut(PointerEvent)>>,
on_pointer_release: Option<Closure<dyn FnMut(PointerEvent)>>,
}

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,
}
}

pub fn on_cursor_leave<F>(&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| {
handler(event.pointer_id());
},
));
}

pub fn on_cursor_enter<F>(&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| {
handler(event.pointer_id());
},
));
}

pub fn on_mouse_release<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32, MouseButton, ModifiersState),
{
self.on_pointer_release = Some(canvas_common.add_user_event(
"pointerup",
move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
},
));
}

pub fn on_mouse_press<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
{
let canvas = canvas_common.raw.clone();
self.on_pointer_press = Some(canvas_common.add_user_event(
"pointerdown",
move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event).to_physical(super::super::scale_factor()),
event::mouse_button(&event),
event::mouse_modifiers(&event),
);
canvas
.set_pointer_capture(event.pointer_id())
.expect("Failed to set pointer capture");
},
));
}

pub fn on_cursor_move<F>(&mut self, canvas_common: &super::Common, mut handler: F)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, ModifiersState),
{
self.on_cursor_move = Some(canvas_common.add_event(
"pointermove",
move |event: PointerEvent| {
handler(
event.pointer_id(),
event::mouse_position(&event).to_physical(super::super::scale_factor()),
event::mouse_modifiers(&event),
);
},
));
}
}
13 changes: 12 additions & 1 deletion src/platform_impl/web/web_sys/event.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ use crate::dpi::LogicalPosition;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};

use std::convert::TryInto;
use web_sys::{KeyboardEvent, MouseEvent, WheelEvent};
use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, WheelEvent};

pub fn mouse_button(event: &MouseEvent) -> MouseButton {
match event.button() {
@@ -29,6 +29,17 @@ pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
}
}

pub fn mouse_position_by_client(
event: &MouseEvent,
canvas: &HtmlCanvasElement,
) -> LogicalPosition<f64> {
let bounding_client_rect = canvas.get_bounding_client_rect();
LogicalPosition {
x: event.client_x() as f64 - bounding_client_rect.x(),
y: event.client_y() as f64 - bounding_client_rect.y(),
}
}

pub fn mouse_scroll_delta(event: &WheelEvent) -> Option<MouseScrollDelta> {
let x = event.delta_x();
let y = -event.delta_y();