diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b46118bc..9116ef21fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- On Web, added support for `WindowEvent::Touch`. - **Breaking:** On Web, remove the `stdweb` backend. - Added `Window::focus_window`to bring the window to the front and set input focus. diff --git a/Cargo.toml b/Cargo.toml index ce4a25792b..1558f7f2a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,7 +115,8 @@ features = [ 'Node', 'PointerEvent', 'Window', - 'WheelEvent' + 'WheelEvent', + 'PointerEvent', ] [target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] diff --git a/FEATURES.md b/FEATURES.md index 96052a7671..fc0d9c7a0a 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -199,7 +199,7 @@ Legend: |Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**| |Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|❓ | |Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ | -|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ | +|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ | |Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❌ | |Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ | |Keyboard events |✔️ |✔️ |✔️ |✔️ |❓ |❌ |✔️ | diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 367556a7a1..d43f346300 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -1,7 +1,7 @@ use super::{super::monitor, backend, device, proxy::Proxy, runner, window}; use crate::dpi::{PhysicalSize, Size}; use crate::event::{ - DeviceEvent, DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent, + DeviceEvent, DeviceId, ElementState, Event, KeyboardInput, Touch, TouchPhase, WindowEvent, }; use crate::event_loop::ControlFlow; use crate::monitor::MonitorHandle as RootMH; @@ -231,6 +231,62 @@ impl WindowTarget { runner.request_redraw(WindowId(id)); }); + let runner = self.runner.clone(); + canvas.on_pointer_move(move |device_id, location| { + runner.send_event(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: DeviceId(device::Id(device_id)), + phase: TouchPhase::Moved, + force: None, // Todo + location, + }), + }); + }); + + let runner = self.runner.clone(); + canvas.on_pointer_down(move |device_id, location| { + runner.send_event(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: DeviceId(device::Id(device_id)), + phase: TouchPhase::Started, + force: None, // Todo + location, + }), + }); + }); + + let runner = self.runner.clone(); + canvas.on_pointer_up(move |device_id, location| { + runner.send_event(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: DeviceId(device::Id(device_id)), + phase: TouchPhase::Ended, + force: None, // Todo + location, + }), + }); + }); + + let runner = self.runner.clone(); + canvas.on_pointer_cancel(move |device_id, location| { + runner.send_event(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::Touch(Touch { + id: device_id as u64, + device_id: DeviceId(device::Id(device_id)), + phase: TouchPhase::Cancelled, + force: None, // Todo + location, + }), + }); + }); + let runner = self.runner.clone(); canvas.on_dark_mode(move |is_dark_mode| { let theme = if is_dark_mode { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 9725e8b6b8..1c01d9d15e 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -247,6 +247,46 @@ impl Canvas { } } + pub fn on_pointer_move(&mut self, handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + match &mut self.mouse_state { + MouseState::HasPointerEvent(h) => h.on_pointer_move(&self.common, handler), + _ => {} + } + } + + pub fn on_pointer_down(&mut self, handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + match &mut self.mouse_state { + MouseState::HasPointerEvent(h) => h.on_pointer_down(&self.common, handler), + _ => {} + } + } + + pub fn on_pointer_up(&mut self, handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + match &mut self.mouse_state { + MouseState::HasPointerEvent(h) => h.on_pointer_up(&self.common, handler), + _ => {} + } + } + + pub fn on_pointer_cancel(&mut self, handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + match &mut self.mouse_state { + MouseState::HasPointerEvent(h) => h.on_pointer_cancel(&self.common, handler), + _ => {} + } + } + pub fn on_mouse_wheel(&mut self, mut handler: F) where F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState), 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 5f6f321016..ad893e1d2e 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -12,6 +12,10 @@ pub(super) struct PointerHandler { on_cursor_move: Option>, on_pointer_press: Option>, on_pointer_release: Option>, + on_touch_move: Option>, + on_touch_down: Option>, + on_touch_up: Option>, + on_touch_cancel: Option>, } impl PointerHandler { @@ -22,6 +26,10 @@ impl PointerHandler { on_cursor_move: None, on_pointer_press: None, on_pointer_release: None, + on_touch_move: None, + on_touch_down: None, + on_touch_up: None, + on_touch_cancel: None, } } @@ -103,11 +111,103 @@ impl PointerHandler { )); } + pub fn on_pointer_move(&mut self, canvas_common: &super::Common, mut handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + self.on_touch_move = Some(canvas_common.add_event( + "pointermove", + move |event: PointerEvent| { + if event.pointer_type() != "touch" { + return; + } + + handler( + event.pointer_id(), + PhysicalPosition { + x: event.offset_x() as f64, + y: event.offset_y() as f64, + }, + ); + }, + )); + } + + pub fn on_pointer_down(&mut self, canvas_common: &super::Common, mut handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + self.on_touch_down = Some(canvas_common.add_event( + "pointerdown", + move |event: PointerEvent| { + if event.pointer_type() != "touch" { + return; + } + + handler( + event.pointer_id(), + PhysicalPosition { + x: event.offset_x() as f64, + y: event.offset_y() as f64, + }, + ); + }, + )); + } + + pub fn on_pointer_up(&mut self, canvas_common: &super::Common, mut handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + self.on_touch_up = Some(canvas_common.add_event( + "pointerup", + move |event: PointerEvent| { + if event.pointer_type() != "touch" { + return; + } + + handler( + event.pointer_id(), + PhysicalPosition { + x: event.offset_x() as f64, + y: event.offset_y() as f64, + }, + ); + }, + )); + } + + pub fn on_pointer_cancel(&mut self, canvas_common: &super::Common, mut handler: F) + where + F: 'static + FnMut(i32, PhysicalPosition), + { + self.on_touch_cancel = Some(canvas_common.add_event( + "pointercancel", + move |event: PointerEvent| { + if event.pointer_type() != "touch" { + return; + } + + handler( + event.pointer_id(), + PhysicalPosition { + x: event.offset_x() as f64, + y: event.offset_y() as f64, + }, + ); + }, + )); + } + pub fn remove_listeners(&mut self) { self.on_cursor_leave = None; self.on_cursor_enter = None; self.on_cursor_move = None; self.on_pointer_press = None; self.on_pointer_release = None; + self.on_touch_move = None; + self.on_touch_down = None; + self.on_touch_up = None; + self.on_touch_cancel = None; } }