Skip to content

Commit

Permalink
Web touch event (#2188)
Browse files Browse the repository at this point in the history
* feat: add pointer events to web

* feat: remove PointerType for touch events

* Remove duplicate

* Changelog and features

* Remove PointerType

* feat: renamed events, added touch type guard

* Rename

* Flip the y axis

* Fix physical position and add force

* Update comment

* Update features

* Use normalized force

* Remove unnecessary todos

* Update comment

* Refactor add touch_handler

* Rephrase by Liamolucko

* Update CHANGELOG.md

* Fix duplicate mouse and touch events

* Removed workaround for scale factor

* Flip the y axis

* Fix

* Fmt

* Replace `match` with a single pattern with `if let`

* Update documentation

* Have one callback per event

* Remove a comment

* Fix

* Remove y-axis flip

* Update src/event.rs

Co-authored-by: Mads Marquart <[email protected]>

* Fix platform specific comment

* Fix extra argument to `touch_position` function

Co-authored-by: Dany Sluijk <[email protected]>
Co-authored-by: Johan Klokkhammer Helsing <[email protected]>
Co-authored-by: oscrim <[email protected]>
Co-authored-by: Mads Marquart <[email protected]>
  • Loading branch information
5 people authored Dec 23, 2022
1 parent 402cbd5 commit f43ce2a
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`.
- **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`.
- On Windows, retain `WS_MAXIMIZE` window style when un-minimizing a maximized window.
- On Windows, fix left mouse button release event not being sent after `Window::drag_window`.
Expand Down
4 changes: 2 additions & 2 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ Legend:
|Cursor confining |✔️ ||✔️ |✔️ |**N/A**|**N/A**||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Cursor hittest |✔️ |✔️ ||✔️ |**N/A**|**N/A**||
|Touch events |✔️ ||✔️ |✔️ |✔️ |✔️ | |
|Touch pressure |✔️ |||||✔️ | |
|Touch events |✔️ ||✔️ |✔️ |✔️ |✔️ |️✔️ |
|Touch pressure |✔️ |||||✔️ |️✔️ |
|Multitouch |✔️ ||✔️ |✔️ |✔️ |✔️ ||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ ||✔️ |
|Drag & Drop |[#720] |[#720] |[#720] |[#306] |**N/A**|**N/A**||
Expand Down
8 changes: 8 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,10 @@ pub enum WindowEvent<'a> {
},

/// Touch event has been received
///
/// ## Platform-specific
///
/// - **macOS:** Unsupported.
Touch(Touch),

/// The window's scale factor has changed.
Expand Down Expand Up @@ -924,6 +928,10 @@ pub enum TouchPhase {
/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this
/// touch, such as when the window loses focus, or on iOS if the user moves the
/// device against their face.
///
/// ## Platform-specific
///
/// - **macOS:** Unsupported.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Touch {
pub device_id: DeviceId,
Expand Down
127 changes: 92 additions & 35 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{
};
use crate::dpi::{PhysicalSize, Size};
use crate::event::{
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyboardInput, TouchPhase,
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyboardInput, Touch, TouchPhase,
WindowEvent,
};
use crate::window::{Theme, WindowId as RootWindowId};
Expand Down Expand Up @@ -154,6 +154,7 @@ impl<T> EventLoopWindowTarget<T> {
});

let runner = self.runner.clone();
let runner_touch = self.runner.clone();
canvas.on_cursor_move(
move |pointer_id, position, delta, modifiers| {
runner.send_event(Event::WindowEvent {
Expand All @@ -171,51 +172,93 @@ impl<T> EventLoopWindowTarget<T> {
},
});
},
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,
}),
});
},
prevent_default,
);

let runner = self.runner.clone();
canvas.on_mouse_press(move |pointer_id, position, button, modifiers| {
// 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(
std::iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
})
.chain(std::iter::once(Event::WindowEvent {
let runner_touch = self.runner.clone();
canvas.on_mouse_press(
move |pointer_id, position, button, modifiers| {
// 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(
std::iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
})
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)),
position,
modifiers,
},
}))
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Pressed,
button,
modifiers,
},
})),
);
},
move |device_id, location, force| {
runner_touch.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::CursorMoved {
device_id: RootDeviceId(DeviceId(pointer_id)),
position,
modifiers,
},
}))
.chain(std::iter::once(Event::WindowEvent {
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 runner_touch = self.runner.clone();
canvas.on_mouse_release(
move |pointer_id, button, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Pressed,
state: ElementState::Released,
button,
modifiers,
},
})),
);
});

let runner = self.runner.clone();
canvas.on_mouse_release(move |pointer_id, button, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseInput {
device_id: RootDeviceId(DeviceId(pointer_id)),
state: ElementState::Released,
button,
modifiers,
},
});
});
});
},
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 = self.runner.clone();
canvas.on_mouse_wheel(
Expand Down Expand Up @@ -263,6 +306,20 @@ impl<T> EventLoopWindowTarget<T> {
runner.request_redraw(RootWindowId(id));
});

let runner = self.runner.clone();
canvas.on_touch_cancel(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::Cancelled,
force: Some(force),
location,
}),
});
});

let runner = self.runner.clone();
canvas.on_dark_mode(move |is_dark_mode| {
let theme = if is_dark_mode {
Expand Down
51 changes: 37 additions & 14 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use super::event_handle::EventListenerHandle;
use super::media_query_handle::MediaQueryListHandle;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::OsError as RootOE;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
use crate::event::{
Force, ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode,
};
use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};

use std::cell::RefCell;
Expand Down Expand Up @@ -262,35 +264,55 @@ impl Canvas {
}
}

pub fn on_mouse_release<F>(&mut self, handler: F)
pub fn on_mouse_release<M, T>(&mut self, mouse_handler: M, touch_handler: T)
where
F: 'static + FnMut(i32, MouseButton, ModifiersState),
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, handler),
MouseState::NoPointerEvent(h) => h.on_mouse_release(&self.common, handler),
MouseState::HasPointerEvent(h) => {
h.on_mouse_release(&self.common, mouse_handler, touch_handler)
}
MouseState::NoPointerEvent(h) => h.on_mouse_release(&self.common, mouse_handler),
}
}

pub fn on_mouse_press<F>(&mut self, handler: F)
pub fn on_mouse_press<M, T>(&mut self, mouse_handler: M, touch_handler: T)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton, ModifiersState),
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, handler),
MouseState::NoPointerEvent(h) => h.on_mouse_press(&self.common, handler),
MouseState::HasPointerEvent(h) => {
h.on_mouse_press(&self.common, mouse_handler, touch_handler)
}
MouseState::NoPointerEvent(h) => h.on_mouse_press(&self.common, mouse_handler),
}
}

pub fn on_cursor_move<F>(&mut self, handler: F, prevent_default: bool)
where
F: 'static + FnMut(i32, PhysicalPosition<f64>, PhysicalPosition<f64>, ModifiersState),
pub fn on_cursor_move<M, T>(
&mut self,
mouse_handler: M,
touch_handler: T,
prevent_default: bool,
) where
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, handler, prevent_default)
h.on_cursor_move(&self.common, mouse_handler, touch_handler, prevent_default)
}
MouseState::NoPointerEvent(h) => h.on_cursor_move(&self.common, handler),
MouseState::NoPointerEvent(h) => h.on_cursor_move(&self.common, mouse_handler),
}
}

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

Expand Down Expand Up @@ -451,6 +473,7 @@ impl Common {
}
}

/// Pointer events are supported or not.
enum MouseState {
HasPointerEvent(pointer_handler::PointerHandler),
NoPointerEvent(mouse_handler::MouseHandler),
Expand Down
Loading

0 comments on commit f43ce2a

Please sign in to comment.