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

Add docs about how to get more fine-grained control of event.preventDefault() calls on web #3451

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
130 changes: 130 additions & 0 deletions src/platform/web.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets link `event.preventDefault()` to https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault as well.

Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,138 @@ pub trait WindowExtWeb {
/// 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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Specifically, this will call `event.preventDefault()` for the following event types:
/// # Implementation Notes
///
/// Specifically, this will call `event.preventDefault()` for the following event types:

/// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove` (for chorded button
/// interactions only), `keyup`, and `keydown`.
Comment on lines +84 to +85
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove` (for chorded button
/// interactions only), `keyup`, and `keydown`.
/// `touchstart`, `wheel`, `contextmenu`, `pointerdown`, `pointermove` (for
/// [chorded button events only]), `keyup`, and `keydown`.

And link to https://www.w3.org/TR/2019/REC-pointerevents2-20190404/#chorded-button-interactions.

///
/// For fine-grained control over which events call `event.preventDefault()`, call
/// `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.
///
/// # Example
/// This example calls `event.preventDefault()` for all relevant events except for ctrl-p.
Comment on lines +94 to +95
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// # Example
/// This example calls `event.preventDefault()` for all relevant events except for ctrl-p.
/// # Example
///
/// This example calls `event.preventDefault()` for all relevant events except for Ctrl-P.
///

/// ```
/// # 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<Window>,
/// # }
/// #
/// # impl ApplicationHandler for App {
/// # fn resumed(&mut self, event_loop: &ActiveEventLoop) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be moved to can_create_surfaces().
The actual issue of course is that documentation tests are not tested on Web, so I went ahead and fixed that in #3799.

Feel free to rebase this PR on #3799 so you can test it yourself as well.

/// # 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());
Comment on lines +118 to +119
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is essential to the example, so it should be visible.
I don't think there is anything wrong with showing the whole struct App and impl ApplicationHandler for App.

/// #
/// # 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();
/// # }
Comment on lines +130 to +132
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not a recommended setup, especially for Web.
I think we can get away with only handling WindowEvent::CloseRequested.

/// # _ => (),
/// # }
/// # }
/// # }
/// #
/// 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 events_types_to_fully_prevent_default.into_iter() {
/// let prevent_default_listener =
/// Closure::<dyn FnMut(_)>::new(move |event: web_sys::Event| {
/// event.prevent_default();
/// });
///
/// let _ = canvas.add_event_listener_with_callback(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't ignore the error, just unwrap() here.

/// 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::<dyn FnMut(_)>::new(move |event: web_sys::PointerEvent| {
/// 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 pointer_events_to_focus_and_prevent_default_on_chord.into_iter() {
/// let stored_canvas = canvas.clone();
/// let prevent_default_listener =
/// Closure::<dyn FnMut(_)>::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::<dyn FnMut(_)>::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(), "p");
/// 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 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);
}

Expand Down
Loading