From 85ea54ba588ba78cf32b7031ab316eaf272c8ec7 Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Fri, 2 Feb 2024 12:20:02 -0800 Subject: [PATCH 1/6] Add docs about how to get more fine-grained control of event.preventDefault() calls on web --- src/platform/web.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/platform/web.rs b/src/platform/web.rs index 9c0b5a2973..95a4f4c3ab 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -65,8 +65,76 @@ pub trait WindowExtWebSys { /// For example, by default using the mouse wheel would cause the page to scroll, enabling this /// would prevent that. /// + /// Specifically, this will call `event.preventDefault()` for the following event types: + /// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove`, `keyup`, and `keydown`. + /// + /// For fine-grained control over which events call `event.preventDefault()`, call + /// `Window::set_prevent_default(false)` and add an event listener on the canvas for each of + /// the above event types that selectively calls `event.preventDefault()`. + /// /// Some events are impossible to prevent. E.g. Firefox allows to access the native browser /// context menu with Shift+Rightclick. + /// + /// # Example + /// This example calls `event.preventDefault()` for all relevant events except for ctrl-c, + /// ctrl-x, and ctrl-v. + /// ``` + /// # fn add_prevent_default_listeners(canvas: HtmlCanvasElement) { + /// # use wasm_bindgen::closure::Closure; + /// # use wasm_bindgen::JsCast; + /// let events_types_to_fully_prevent_default = + /// ["touchstart", "wheel", "contextmenu", "pointermove"]; + /// let key_events_to_partially_prevent_default = ["keyup", "keydown"]; + /// let pointer_events_to_focus_and_prevent_default = ["pointerdown"]; + /// + /// for event_type in events_types_to_fully_prevent_default.into_iter() { + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::Event| { + /// event.prevent_default(); + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } + /// + /// for event_type in pointer_events_to_focus_and_prevent_default.into_iter() { + /// let stored_canvas = canvas.clone(); + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::Event| { + /// event.prevent_default(); + /// let _ = stored_canvas.focus(); + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } + /// + /// for event_type in key_events_to_partially_prevent_default.into_iter() { + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::KeyboardEvent| { + /// let only_ctrl_key = + /// event.ctrl_key() && !event.meta_key() && !event.shift_key() && !event.alt_key(); + /// let allow_default = + /// only_ctrl_key && matches!(event.key().as_ref(), "c" | "x" | "v"); + /// if !allow_default { + /// event.prevent_default(); + /// } + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } + /// # } + /// ``` fn set_prevent_default(&self, prevent_default: bool); } From c13cd4c6c8bcd18ce0ac5f8ca6267adcadbfbab3 Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Fri, 5 Apr 2024 15:49:26 -0700 Subject: [PATCH 2/6] Update src/platform/web.rs Co-authored-by: daxpedda --- src/platform/web.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/web.rs b/src/platform/web.rs index 95a4f4c3ab..0096f7f1d5 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -69,8 +69,8 @@ pub trait WindowExtWebSys { /// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove`, `keyup`, and `keydown`. /// /// For fine-grained control over which events call `event.preventDefault()`, call - /// `Window::set_prevent_default(false)` and add an event listener on the canvas for each of - /// the above event types that selectively calls `event.preventDefault()`. + /// `Window::set_prevent_default(false)` and add event listeners on the canvas that + /// selectively call `event.preventDefault()`. /// /// Some events are impossible to prevent. E.g. Firefox allows to access the native browser /// context menu with Shift+Rightclick. From ab8f4a7f0036a98c0b01bab4d46c368a44334c26 Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Fri, 5 Apr 2024 15:50:17 -0700 Subject: [PATCH 3/6] Update src/platform/web.rs Co-authored-by: daxpedda --- src/platform/web.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/web.rs b/src/platform/web.rs index 0096f7f1d5..31f0c46b5f 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -97,7 +97,7 @@ pub trait WindowExtWebSys { /// event_type, /// prevent_default_listener.as_ref().unchecked_ref(), /// ); - /// prevent_default_listener.forget(); + /// prevent_default_listener.into_js_value(); /// } /// /// for event_type in pointer_events_to_focus_and_prevent_default.into_iter() { From 10468d469b6a29a9043bfbfd049578829e01f30a Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Thu, 18 Jul 2024 12:34:21 -0400 Subject: [PATCH 4/6] Update comment to address chorded button interactions and switch example to printing --- src/platform/web.rs | 123 +++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 52 deletions(-) diff --git a/src/platform/web.rs b/src/platform/web.rs index 31f0c46b5f..744e69b883 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -66,7 +66,8 @@ pub trait WindowExtWebSys { /// would prevent that. /// /// Specifically, this will call `event.preventDefault()` for the following event types: - /// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove`, `keyup`, and `keydown`. + /// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove` (for chorded button + /// interactions only), `keyup`, and `keydown`. /// /// For fine-grained control over which events call `event.preventDefault()`, call /// `Window::set_prevent_default(false)` and add event listeners on the canvas that @@ -76,64 +77,82 @@ pub trait WindowExtWebSys { /// context menu with Shift+Rightclick. /// /// # Example - /// This example calls `event.preventDefault()` for all relevant events except for ctrl-c, - /// ctrl-x, and ctrl-v. + /// This example calls `event.preventDefault()` for all relevant events except for ctrl-p. /// ``` - /// # fn add_prevent_default_listeners(canvas: HtmlCanvasElement) { /// # use wasm_bindgen::closure::Closure; /// # use wasm_bindgen::JsCast; - /// let events_types_to_fully_prevent_default = - /// ["touchstart", "wheel", "contextmenu", "pointermove"]; - /// let key_events_to_partially_prevent_default = ["keyup", "keydown"]; - /// let pointer_events_to_focus_and_prevent_default = ["pointerdown"]; - /// - /// for event_type in events_types_to_fully_prevent_default.into_iter() { - /// let prevent_default_listener = - /// Closure::::new(move |event: web_sys::Event| { - /// event.prevent_default(); - /// }); - /// - /// let _ = canvas.add_event_listener_with_callback( - /// event_type, - /// prevent_default_listener.as_ref().unchecked_ref(), - /// ); - /// prevent_default_listener.into_js_value(); - /// } + /// # use web_sys::HtmlCanvasElement; + /// fn add_prevent_default_listeners(canvas: HtmlCanvasElement) { + /// let events_types_to_fully_prevent_default = ["touchstart", "wheel", "contextmenu"]; + /// let pointer_events_to_focus_and_prevent_default = ["pointerdown"]; + /// let pointer_events_to_focus_and_prevent_default_on_chord = ["pointermove"]; + /// let key_events_to_partially_prevent_default = ["keyup", "keydown"]; /// - /// for event_type in pointer_events_to_focus_and_prevent_default.into_iter() { - /// let stored_canvas = canvas.clone(); - /// let prevent_default_listener = - /// Closure::::new(move |event: web_sys::Event| { - /// event.prevent_default(); - /// let _ = stored_canvas.focus(); - /// }); - /// - /// let _ = canvas.add_event_listener_with_callback( - /// event_type, - /// prevent_default_listener.as_ref().unchecked_ref(), - /// ); - /// prevent_default_listener.forget(); - /// } + /// for event_type in events_types_to_fully_prevent_default.into_iter() { + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::Event| { + /// event.prevent_default(); + /// }); /// - /// for event_type in key_events_to_partially_prevent_default.into_iter() { - /// let prevent_default_listener = - /// Closure::::new(move |event: web_sys::KeyboardEvent| { - /// let only_ctrl_key = - /// event.ctrl_key() && !event.meta_key() && !event.shift_key() && !event.alt_key(); - /// let allow_default = - /// only_ctrl_key && matches!(event.key().as_ref(), "c" | "x" | "v"); - /// if !allow_default { + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.into_js_value(); + /// } + /// + /// for event_type in pointer_events_to_focus_and_prevent_default.into_iter() { + /// let stored_canvas = canvas.clone(); + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::PointerEvent| { /// event.prevent_default(); - /// } - /// }); - /// - /// let _ = canvas.add_event_listener_with_callback( - /// event_type, - /// prevent_default_listener.as_ref().unchecked_ref(), - /// ); - /// prevent_default_listener.forget(); + /// let _ = stored_canvas.focus(); + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } + /// + /// for event_type in pointer_events_to_focus_and_prevent_default_on_chord.into_iter() { + /// let stored_canvas = canvas.clone(); + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::PointerEvent| { + /// if event.button() != -1 { + /// // chorded button interaction + /// event.prevent_default(); + /// let _ = stored_canvas.focus(); + /// } + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } + /// + /// for event_type in key_events_to_partially_prevent_default.into_iter() { + /// let prevent_default_listener = + /// Closure::::new(move |event: web_sys::KeyboardEvent| { + /// let only_ctrl_key = + /// event.ctrl_key() && !event.meta_key() && !event.shift_key() && !event.alt_key(); + /// let allow_default = + /// only_ctrl_key && matches!(event.key().as_ref(), "c" | "x" | "v"); + /// if !allow_default { + /// event.prevent_default(); + /// } + /// }); + /// + /// let _ = canvas.add_event_listener_with_callback( + /// event_type, + /// prevent_default_listener.as_ref().unchecked_ref(), + /// ); + /// prevent_default_listener.forget(); + /// } /// } - /// # } /// ``` fn set_prevent_default(&self, prevent_default: bool); } From 1d2a63f84aa5e26d85f8d51a2ba0332f0ae6458a Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Thu, 18 Jul 2024 12:42:53 -0400 Subject: [PATCH 5/6] Make example into full example app --- src/platform/web.rs | 47 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/platform/web.rs b/src/platform/web.rs index 744e69b883..daeb70b97b 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -82,6 +82,44 @@ pub trait WindowExtWebSys { /// # use wasm_bindgen::closure::Closure; /// # use wasm_bindgen::JsCast; /// # use web_sys::HtmlCanvasElement; + /// # use winit::application::ApplicationHandler; + /// # use winit::event::WindowEvent; + /// # use winit::event_loop::{ActiveEventLoop, EventLoop}; + /// # use winit::platform::web::WindowAttributesExtWebSys; + /// # use winit::platform::web::WindowExtWebSys; + /// # use winit::window::{Window, WindowId}; + /// # + /// # struct App { + /// # window: Option, + /// # } + /// # + /// # impl ApplicationHandler for App { + /// # fn resumed(&mut self, event_loop: &ActiveEventLoop) { + /// # if self.window.is_none() { + /// # let window = event_loop + /// # .create_window(Window::default_attributes().with_append(true)) + /// # .unwrap(); + /// # + /// # window.set_prevent_default(false); + /// # add_prevent_default_listeners(window.canvas().unwrap()); + /// # + /// # self.window = Some(window) + /// # } + /// # } + /// # + /// # fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + /// # match event { + /// # WindowEvent::CloseRequested => { + /// # event_loop.exit(); + /// # } + /// # WindowEvent::RedrawRequested => { + /// # self.window.as_ref().unwrap().request_redraw(); + /// # } + /// # _ => (), + /// # } + /// # } + /// # } + /// # /// fn add_prevent_default_listeners(canvas: HtmlCanvasElement) { /// let events_types_to_fully_prevent_default = ["touchstart", "wheel", "contextmenu"]; /// let pointer_events_to_focus_and_prevent_default = ["pointerdown"]; @@ -139,8 +177,7 @@ pub trait WindowExtWebSys { /// Closure::::new(move |event: web_sys::KeyboardEvent| { /// let only_ctrl_key = /// event.ctrl_key() && !event.meta_key() && !event.shift_key() && !event.alt_key(); - /// let allow_default = - /// only_ctrl_key && matches!(event.key().as_ref(), "c" | "x" | "v"); + /// let allow_default = only_ctrl_key && matches!(event.key().as_ref(), "p"); /// if !allow_default { /// event.prevent_default(); /// } @@ -153,6 +190,12 @@ pub trait WindowExtWebSys { /// prevent_default_listener.forget(); /// } /// } + /// # + /// # fn main() { + /// # let mut app = App { window: None }; + /// # let event_loop = EventLoop::new().unwrap(); + /// # let _ = event_loop.run_app(&mut app); + /// # } /// ``` fn set_prevent_default(&self, prevent_default: bool); } From 158f7eef7d7be0406526dd5d9b3a433d83b74ef2 Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Thu, 18 Jul 2024 14:12:02 -0400 Subject: [PATCH 6/6] minor fix --- src/platform/web.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/web.rs b/src/platform/web.rs index ae1beaea9b..0ce481f8ff 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -151,7 +151,7 @@ pub trait WindowExtWeb { /// event_type, /// prevent_default_listener.as_ref().unchecked_ref(), /// ); - /// prevent_default_listener.into_js_value(); + /// prevent_default_listener.forget(); /// } /// /// for event_type in pointer_events_to_focus_and_prevent_default.into_iter() {