On Web, add Window::(set_)prevent_default() (#3307)

This commit is contained in:
daxpedda 2023-12-25 09:37:35 +01:00 committed by GitHub
parent 28a811bbba
commit 843d7904d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 60 deletions

View file

@ -23,6 +23,7 @@ Unreleased` header.
- **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`. - **Breaking:** On Web, return `RawWindowHandle::WebCanvas` instead of `RawWindowHandle::Web`.
- **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available. - **Breaking:** On Web, macOS and iOS, return `HandleError::Unavailable` when a window handle is not available.
- **Breaking:** Bump MSRV from `1.65` to `1.70`. - **Breaking:** Bump MSRV from `1.65` to `1.70`.
- On Web, add the ability to toggle calling `Event.preventDefault()` on `Window`.
# 0.29.6 # 0.29.6

View file

@ -42,6 +42,21 @@ pub trait WindowExtWebSys {
/// Only returns the canvas if called from inside the window context (the /// Only returns the canvas if called from inside the window context (the
/// main thread). /// main thread).
fn canvas(&self) -> Option<HtmlCanvasElement>; fn canvas(&self) -> Option<HtmlCanvasElement>;
/// Returns [`true`] if calling `event.preventDefault()` is enabled.
///
/// See [`Window::set_prevent_default()`] for more details.
fn prevent_default(&self) -> bool;
/// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects.
///
/// For example, by default using the mouse wheel would cause the page to scroll, enabling this
/// would prevent that.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
fn set_prevent_default(&self, prevent_default: bool);
} }
impl WindowExtWebSys for Window { impl WindowExtWebSys for Window {
@ -49,6 +64,14 @@ impl WindowExtWebSys for Window {
fn canvas(&self) -> Option<HtmlCanvasElement> { fn canvas(&self) -> Option<HtmlCanvasElement> {
self.window.canvas() self.window.canvas()
} }
fn prevent_default(&self) -> bool {
self.window.prevent_default()
}
fn set_prevent_default(&self, prevent_default: bool) {
self.window.set_prevent_default(prevent_default)
}
} }
pub trait WindowBuilderExtWebSys { pub trait WindowBuilderExtWebSys {
@ -60,14 +83,10 @@ pub trait WindowBuilderExtWebSys {
/// [`None`] by default. /// [`None`] by default.
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self; fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
/// Whether `event.preventDefault()` should be called on events on the /// Sets whether `event.preventDefault()` should be called on events on the
/// canvas that have side effects. /// canvas that have side effects.
/// ///
/// For example, by default using the mouse wheel would cause the page to scroll, enabling this /// See [`Window::set_prevent_default()`] for more details.
/// would prevent that.
///
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
/// context menu with Shift+Rightclick.
/// ///
/// Enabled by default. /// Enabled by default.
fn with_prevent_default(self, prevent_default: bool) -> Self; fn with_prevent_default(self, prevent_default: bool) -> Self;

View file

@ -75,18 +75,13 @@ impl<T> EventLoopWindowTarget<T> {
WindowId(self.runner.generate_id()) WindowId(self.runner.generate_id())
} }
pub fn register( pub fn register(&self, canvas: &Rc<RefCell<backend::Canvas>>, id: WindowId) {
&self,
canvas: &Rc<RefCell<backend::Canvas>>,
id: WindowId,
prevent_default: bool,
) {
let canvas_clone = canvas.clone(); let canvas_clone = canvas.clone();
let mut canvas = canvas.borrow_mut(); let mut canvas = canvas.borrow_mut();
#[cfg(any(feature = "rwh_04", feature = "rwh_05"))] #[cfg(any(feature = "rwh_04", feature = "rwh_05"))]
canvas.set_attribute("data-raw-handle", &id.0.to_string()); canvas.set_attribute("data-raw-handle", &id.0.to_string());
canvas.on_touch_start(prevent_default); canvas.on_touch_start();
let runner = self.runner.clone(); let runner = self.runner.clone();
let has_focus = canvas.has_focus.clone(); let has_focus = canvas.has_focus.clone();
@ -157,7 +152,6 @@ impl<T> EventLoopWindowTarget<T> {
.chain(modifiers_changed), .chain(modifiers_changed),
); );
}, },
prevent_default,
); );
let runner = self.runner.clone(); let runner = self.runner.clone();
@ -194,7 +188,6 @@ impl<T> EventLoopWindowTarget<T> {
.chain(modifiers_changed), .chain(modifiers_changed),
) )
}, },
prevent_default,
); );
let has_focus = canvas.has_focus.clone(); let has_focus = canvas.has_focus.clone();
@ -374,7 +367,6 @@ impl<T> EventLoopWindowTarget<T> {
])); ]));
} }
}, },
prevent_default,
); );
canvas.on_mouse_press( canvas.on_mouse_press(
@ -456,7 +448,6 @@ impl<T> EventLoopWindowTarget<T> {
))) )))
} }
}, },
prevent_default,
); );
canvas.on_mouse_release( canvas.on_mouse_release(
@ -547,30 +538,27 @@ impl<T> EventLoopWindowTarget<T> {
let runner = self.runner.clone(); let runner = self.runner.clone();
let modifiers = self.modifiers.clone(); let modifiers = self.modifiers.clone();
canvas.on_mouse_wheel( canvas.on_mouse_wheel(move |pointer_id, delta, active_modifiers| {
move |pointer_id, delta, active_modifiers| { let modifiers_changed =
let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers) (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
.then(|| { modifiers.set(active_modifiers);
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
Event::WindowEvent { Event::WindowEvent {
window_id: RootWindowId(id), window_id: RootWindowId(id),
event: WindowEvent::MouseWheel { event: WindowEvent::ModifiersChanged(active_modifiers.into()),
device_id: RootDeviceId(DeviceId(pointer_id)), }
delta, });
phase: TouchPhase::Moved,
}, runner.send_events(modifiers_changed.into_iter().chain(iter::once(
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::MouseWheel {
device_id: RootDeviceId(DeviceId(pointer_id)),
delta,
phase: TouchPhase::Moved,
}, },
))); },
}, )));
prevent_default, });
);
let runner = self.runner.clone(); let runner = self.runner.clone();
canvas.on_touch_cancel(move |device_id, location, force| { canvas.on_touch_cancel(move |device_id, location, force| {
@ -649,7 +637,7 @@ impl<T> EventLoopWindowTarget<T> {
let runner = self.runner.clone(); let runner = self.runner.clone();
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id))); canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
canvas.on_context_menu(prevent_default); canvas.on_context_menu();
} }
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> { pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {

View file

@ -30,6 +30,7 @@ pub struct Canvas {
common: Common, common: Common,
id: WindowId, id: WindowId,
pub has_focus: Rc<Cell<bool>>, pub has_focus: Rc<Cell<bool>>,
pub prevent_default: Rc<Cell<bool>>,
pub is_intersecting: Option<bool>, pub is_intersecting: Option<bool>,
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>, on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>, on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
@ -141,6 +142,7 @@ impl Canvas {
common, common,
id, id,
has_focus: Rc::new(Cell::new(false)), has_focus: Rc::new(Cell::new(false)),
prevent_default: Rc::new(Cell::new(platform_attr.prevent_default)),
is_intersecting: None, is_intersecting: None,
on_touch_start: None, on_touch_start: None,
on_blur: None, on_blur: None,
@ -231,9 +233,10 @@ impl Canvas {
&self.common.style &self.common.style
} }
pub fn on_touch_start(&mut self, prevent_default: bool) { pub fn on_touch_start(&mut self) {
let prevent_default = Rc::clone(&self.prevent_default);
self.on_touch_start = Some(self.common.add_event("touchstart", move |event: Event| { self.on_touch_start = Some(self.common.add_event("touchstart", move |event: Event| {
if prevent_default { if prevent_default.get() {
event.prevent_default(); event.prevent_default();
} }
})); }));
@ -257,13 +260,14 @@ impl Canvas {
})); }));
} }
pub fn on_keyboard_release<F>(&mut self, mut handler: F, prevent_default: bool) pub fn on_keyboard_release<F>(&mut self, mut handler: F)
where where
F: 'static + FnMut(PhysicalKey, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState), F: 'static + FnMut(PhysicalKey, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
{ {
let prevent_default = Rc::clone(&self.prevent_default);
self.on_keyboard_release = self.on_keyboard_release =
Some(self.common.add_event("keyup", move |event: KeyboardEvent| { Some(self.common.add_event("keyup", move |event: KeyboardEvent| {
if prevent_default { if prevent_default.get() {
event.prevent_default(); event.prevent_default();
} }
let key = event::key(&event); let key = event::key(&event);
@ -279,14 +283,15 @@ impl Canvas {
})); }));
} }
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool) pub fn on_keyboard_press<F>(&mut self, mut handler: F)
where where
F: 'static + FnMut(PhysicalKey, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState), F: 'static + FnMut(PhysicalKey, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
{ {
let prevent_default = Rc::clone(&self.prevent_default);
self.on_keyboard_press = Some(self.common.add_event( self.on_keyboard_press = Some(self.common.add_event(
"keydown", "keydown",
move |event: KeyboardEvent| { move |event: KeyboardEvent| {
if prevent_default { if prevent_default.get() {
event.prevent_default(); event.prevent_default();
} }
let key = event::key(&event); let key = event::key(&event);
@ -340,7 +345,6 @@ impl Canvas {
modifier_handler: MOD, modifier_handler: MOD,
mouse_handler: M, mouse_handler: M,
touch_handler: T, touch_handler: T,
prevent_default: bool,
) where ) where
MOD: 'static + FnMut(ModifiersState), MOD: 'static + FnMut(ModifiersState),
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton), M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
@ -351,7 +355,7 @@ impl Canvas {
modifier_handler, modifier_handler,
mouse_handler, mouse_handler,
touch_handler, touch_handler,
prevent_default, Rc::clone(&self.prevent_default),
) )
} }
@ -361,7 +365,6 @@ impl Canvas {
mouse_handler: M, mouse_handler: M,
touch_handler: T, touch_handler: T,
button_handler: B, button_handler: B,
prevent_default: bool,
) where ) where
MOD: 'static + FnMut(ModifiersState), MOD: 'static + FnMut(ModifiersState),
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>), M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
@ -375,7 +378,7 @@ impl Canvas {
mouse_handler, mouse_handler,
touch_handler, touch_handler,
button_handler, button_handler,
prevent_default, Rc::clone(&self.prevent_default),
) )
} }
@ -386,13 +389,14 @@ impl Canvas {
self.pointer_handler.on_touch_cancel(&self.common, handler) self.pointer_handler.on_touch_cancel(&self.common, handler)
} }
pub fn on_mouse_wheel<F>(&mut self, mut handler: F, prevent_default: bool) pub fn on_mouse_wheel<F>(&mut self, mut handler: F)
where where
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState), F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
{ {
let window = self.common.window.clone(); let window = self.common.window.clone();
let prevent_default = Rc::clone(&self.prevent_default);
self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| { self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| {
if prevent_default { if prevent_default.get() {
event.prevent_default(); event.prevent_default();
} }
@ -443,11 +447,12 @@ impl Canvas {
self.animation_frame_handler.on_animation_frame(f) self.animation_frame_handler.on_animation_frame(f)
} }
pub(crate) fn on_context_menu(&mut self, prevent_default: bool) { pub(crate) fn on_context_menu(&mut self) {
let prevent_default = Rc::clone(&self.prevent_default);
self.on_context_menu = Some(self.common.add_event( self.on_context_menu = Some(self.common.add_event(
"contextmenu", "contextmenu",
move |event: PointerEvent| { move |event: PointerEvent| {
if prevent_default { if prevent_default.get() {
event.prevent_default(); event.prevent_default();
} }
}, },

View file

@ -1,3 +1,6 @@
use std::cell::Cell;
use std::rc::Rc;
use super::canvas::Common; use super::canvas::Common;
use super::event; use super::event;
use super::event_handle::EventListenerHandle; use super::event_handle::EventListenerHandle;
@ -110,7 +113,7 @@ impl PointerHandler {
mut modifier_handler: MOD, mut modifier_handler: MOD,
mut mouse_handler: M, mut mouse_handler: M,
mut touch_handler: T, mut touch_handler: T,
prevent_default: bool, prevent_default: Rc<Cell<bool>>,
) where ) where
MOD: 'static + FnMut(ModifiersState), MOD: 'static + FnMut(ModifiersState),
M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton), M: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, MouseButton),
@ -121,7 +124,7 @@ impl PointerHandler {
self.on_pointer_press = Some(canvas_common.add_event( self.on_pointer_press = Some(canvas_common.add_event(
"pointerdown", "pointerdown",
move |event: PointerEvent| { move |event: PointerEvent| {
if prevent_default { if prevent_default.get() {
// prevent text selection // prevent text selection
event.prevent_default(); event.prevent_default();
// but still focus element // but still focus element
@ -165,7 +168,7 @@ impl PointerHandler {
mut mouse_handler: M, mut mouse_handler: M,
mut touch_handler: T, mut touch_handler: T,
mut button_handler: B, mut button_handler: B,
prevent_default: bool, prevent_default: Rc<Cell<bool>>,
) where ) where
MOD: 'static + FnMut(ModifiersState), MOD: 'static + FnMut(ModifiersState),
M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>), M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
@ -197,7 +200,7 @@ impl PointerHandler {
"expect pointer type of a chorded button event to be a mouse" "expect pointer type of a chorded button event to be a mouse"
); );
if prevent_default { if prevent_default.get() {
// prevent text selection // prevent text selection
event.prevent_default(); event.prevent_default();
// but still focus element // but still focus element

View file

@ -36,8 +36,6 @@ impl Window {
) -> Result<Self, RootOE> { ) -> Result<Self, RootOE> {
let id = target.generate_id(); let id = target.generate_id();
let prevent_default = platform_attr.prevent_default;
let window = target.runner.window(); let window = target.runner.window();
let document = target.runner.document(); let document = target.runner.document();
let canvas = let canvas =
@ -45,7 +43,7 @@ impl Window {
let canvas = Rc::new(RefCell::new(canvas)); let canvas = Rc::new(RefCell::new(canvas));
let cursor = CursorState::new(canvas.borrow().style().clone()); let cursor = CursorState::new(canvas.borrow().style().clone());
target.register(&canvas, id, prevent_default); target.register(&canvas, id);
let runner = target.runner.clone(); let runner = target.runner.clone();
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id))); let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
@ -84,6 +82,16 @@ impl Window {
.map(|inner| inner.canvas.borrow().raw().clone()) .map(|inner| inner.canvas.borrow().raw().clone())
} }
pub(crate) fn prevent_default(&self) -> bool {
self.inner
.queue(|inner| inner.canvas.borrow().prevent_default.get())
}
pub(crate) fn set_prevent_default(&self, prevent_default: bool) {
self.inner
.dispatch(move |inner| inner.canvas.borrow().prevent_default.set(prevent_default))
}
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> { pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {