mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-26 03:36:32 +11:00
web: add with_prevent_default
, with_focusable
(#2365)
* web: add `with_prevent_default`, `with_focusable` `with_prevent_default` controls whether `event.preventDefault` is called `with_focusable` controls whether `tabindex` is added Fixes #1768 * Remove extra space from CHANGELOG
This commit is contained in:
parent
472d7b9376
commit
990e34a129
5 changed files with 137 additions and 68 deletions
|
@ -70,6 +70,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
- Added `From<u64>` for `WindowId` and `From<WindowId>` for `u64`.
|
- Added `From<u64>` for `WindowId` and `From<WindowId>` for `u64`.
|
||||||
- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate.
|
- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate.
|
||||||
- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision.
|
- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision.
|
||||||
|
- On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated.
|
||||||
|
|
||||||
# 0.26.1 (2022-01-05)
|
# 0.26.1 (2022-01-05)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,17 @@ pub trait WindowExtWebSys {
|
||||||
|
|
||||||
pub trait WindowBuilderExtWebSys {
|
pub trait WindowBuilderExtWebSys {
|
||||||
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
||||||
|
|
||||||
|
/// Whether `event.preventDefault` should be automatically called to prevent event propagation
|
||||||
|
/// when appropriate.
|
||||||
|
///
|
||||||
|
/// For example, mouse wheel events are only handled by the canvas by default. This avoids
|
||||||
|
/// the default behavior of scrolling the page.
|
||||||
|
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
||||||
|
|
||||||
|
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
|
||||||
|
/// canvas keyboard events.
|
||||||
|
fn with_focusable(self, focusable: bool) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||||
|
@ -30,6 +41,18 @@ impl WindowBuilderExtWebSys for WindowBuilder {
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
|
||||||
|
self.platform_specific.prevent_default = prevent_default;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_focusable(mut self, focusable: bool) -> Self {
|
||||||
|
self.platform_specific.focusable = focusable;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional methods on `EventLoop` that are specific to the web.
|
/// Additional methods on `EventLoop` that are specific to the web.
|
||||||
|
|
|
@ -50,7 +50,12 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
WindowId(self.runner.generate_id())
|
WindowId(self.runner.generate_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(&self, canvas: &Rc<RefCell<backend::Canvas>>, id: WindowId) {
|
pub fn register(
|
||||||
|
&self,
|
||||||
|
canvas: &Rc<RefCell<backend::Canvas>>,
|
||||||
|
id: WindowId,
|
||||||
|
prevent_default: bool,
|
||||||
|
) {
|
||||||
self.runner.add_canvas(RootWindowId(id), canvas);
|
self.runner.add_canvas(RootWindowId(id), canvas);
|
||||||
let mut canvas = canvas.borrow_mut();
|
let mut canvas = canvas.borrow_mut();
|
||||||
canvas.set_attribute("data-raw-handle", &id.0.to_string());
|
canvas.set_attribute("data-raw-handle", &id.0.to_string());
|
||||||
|
@ -72,48 +77,57 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_keyboard_press(move |scancode, virtual_keycode, modifiers| {
|
canvas.on_keyboard_press(
|
||||||
#[allow(deprecated)]
|
move |scancode, virtual_keycode, modifiers| {
|
||||||
runner.send_event(Event::WindowEvent {
|
#[allow(deprecated)]
|
||||||
window_id: RootWindowId(id),
|
runner.send_event(Event::WindowEvent {
|
||||||
event: WindowEvent::KeyboardInput {
|
window_id: RootWindowId(id),
|
||||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
event: WindowEvent::KeyboardInput {
|
||||||
input: KeyboardInput {
|
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||||
scancode,
|
input: KeyboardInput {
|
||||||
state: ElementState::Pressed,
|
scancode,
|
||||||
virtual_keycode,
|
state: ElementState::Pressed,
|
||||||
modifiers,
|
virtual_keycode,
|
||||||
|
modifiers,
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
is_synthetic: false,
|
});
|
||||||
},
|
},
|
||||||
});
|
prevent_default,
|
||||||
});
|
);
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_keyboard_release(move |scancode, virtual_keycode, modifiers| {
|
canvas.on_keyboard_release(
|
||||||
#[allow(deprecated)]
|
move |scancode, virtual_keycode, modifiers| {
|
||||||
runner.send_event(Event::WindowEvent {
|
#[allow(deprecated)]
|
||||||
window_id: RootWindowId(id),
|
runner.send_event(Event::WindowEvent {
|
||||||
event: WindowEvent::KeyboardInput {
|
window_id: RootWindowId(id),
|
||||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
event: WindowEvent::KeyboardInput {
|
||||||
input: KeyboardInput {
|
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||||
scancode,
|
input: KeyboardInput {
|
||||||
state: ElementState::Released,
|
scancode,
|
||||||
virtual_keycode,
|
state: ElementState::Released,
|
||||||
modifiers,
|
virtual_keycode,
|
||||||
|
modifiers,
|
||||||
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
is_synthetic: false,
|
});
|
||||||
},
|
},
|
||||||
});
|
prevent_default,
|
||||||
});
|
);
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_received_character(move |char_code| {
|
canvas.on_received_character(
|
||||||
runner.send_event(Event::WindowEvent {
|
move |char_code| {
|
||||||
window_id: RootWindowId(id),
|
runner.send_event(Event::WindowEvent {
|
||||||
event: WindowEvent::ReceivedCharacter(char_code),
|
window_id: RootWindowId(id),
|
||||||
});
|
event: WindowEvent::ReceivedCharacter(char_code),
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
prevent_default,
|
||||||
|
);
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_cursor_leave(move |pointer_id| {
|
canvas.on_cursor_leave(move |pointer_id| {
|
||||||
|
@ -197,17 +211,20 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_mouse_wheel(move |pointer_id, delta, modifiers| {
|
canvas.on_mouse_wheel(
|
||||||
runner.send_event(Event::WindowEvent {
|
move |pointer_id, delta, modifiers| {
|
||||||
window_id: RootWindowId(id),
|
runner.send_event(Event::WindowEvent {
|
||||||
event: WindowEvent::MouseWheel {
|
window_id: RootWindowId(id),
|
||||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
event: WindowEvent::MouseWheel {
|
||||||
delta,
|
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||||
phase: TouchPhase::Moved,
|
delta,
|
||||||
modifiers,
|
phase: TouchPhase::Moved,
|
||||||
},
|
modifiers,
|
||||||
});
|
},
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
prevent_default,
|
||||||
|
);
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
let raw = canvas.raw().clone();
|
let raw = canvas.raw().clone();
|
||||||
|
|
|
@ -62,9 +62,11 @@ impl Canvas {
|
||||||
// sequential keyboard navigation, but its order is defined by the
|
// sequential keyboard navigation, but its order is defined by the
|
||||||
// document's source order.
|
// document's source order.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
|
||||||
canvas
|
if attr.focusable {
|
||||||
.set_attribute("tabindex", "0")
|
canvas
|
||||||
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
|
.set_attribute("tabindex", "0")
|
||||||
|
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
|
||||||
|
}
|
||||||
|
|
||||||
let mouse_state = if has_pointer_event() {
|
let mouse_state = if has_pointer_event() {
|
||||||
MouseState::HasPointerEvent(pointer_handler::PointerHandler::new())
|
MouseState::HasPointerEvent(pointer_handler::PointerHandler::new())
|
||||||
|
@ -148,14 +150,17 @@ impl Canvas {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_keyboard_release<F>(&mut self, mut handler: F)
|
pub fn on_keyboard_release<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||||
{
|
{
|
||||||
self.on_keyboard_release = Some(self.common.add_user_event(
|
self.on_keyboard_release = Some(self.common.add_user_event(
|
||||||
"keyup",
|
"keyup",
|
||||||
move |event: KeyboardEvent| {
|
move |event: KeyboardEvent| {
|
||||||
event.prevent_default();
|
if prevent_default {
|
||||||
|
event.prevent_default();
|
||||||
|
}
|
||||||
|
|
||||||
handler(
|
handler(
|
||||||
event::scan_code(&event),
|
event::scan_code(&event),
|
||||||
event::virtual_key_code(&event),
|
event::virtual_key_code(&event),
|
||||||
|
@ -165,7 +170,7 @@ impl Canvas {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_keyboard_press<F>(&mut self, mut handler: F)
|
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||||
{
|
{
|
||||||
|
@ -173,16 +178,19 @@ impl Canvas {
|
||||||
"keydown",
|
"keydown",
|
||||||
move |event: KeyboardEvent| {
|
move |event: KeyboardEvent| {
|
||||||
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
||||||
// supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
// suppression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
||||||
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
||||||
// input though.
|
// input though.
|
||||||
let event_key = &event.key();
|
if prevent_default {
|
||||||
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
let event_key = &event.key();
|
||||||
let is_shortcut_modifiers =
|
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
||||||
(event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr");
|
let is_shortcut_modifiers =
|
||||||
if !is_key_string || is_shortcut_modifiers {
|
(event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr");
|
||||||
event.prevent_default();
|
if !is_key_string || is_shortcut_modifiers {
|
||||||
|
event.prevent_default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler(
|
handler(
|
||||||
event::scan_code(&event),
|
event::scan_code(&event),
|
||||||
event::virtual_key_code(&event),
|
event::virtual_key_code(&event),
|
||||||
|
@ -192,7 +200,7 @@ impl Canvas {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_received_character<F>(&mut self, mut handler: F)
|
pub fn on_received_character<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(char),
|
F: 'static + FnMut(char),
|
||||||
{
|
{
|
||||||
|
@ -204,8 +212,11 @@ impl Canvas {
|
||||||
self.on_received_character = Some(self.common.add_user_event(
|
self.on_received_character = Some(self.common.add_user_event(
|
||||||
"keypress",
|
"keypress",
|
||||||
move |event: KeyboardEvent| {
|
move |event: KeyboardEvent| {
|
||||||
// Supress further handling to stop keys like the space key from scrolling the page.
|
// Suppress further handling to stop keys like the space key from scrolling the page.
|
||||||
event.prevent_default();
|
if prevent_default {
|
||||||
|
event.prevent_default();
|
||||||
|
}
|
||||||
|
|
||||||
handler(event::codepoint(&event));
|
handler(event::codepoint(&event));
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -261,12 +272,15 @@ impl Canvas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_mouse_wheel<F>(&mut self, mut handler: F)
|
pub fn on_mouse_wheel<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
|
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
|
||||||
{
|
{
|
||||||
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| {
|
||||||
event.prevent_default();
|
if prevent_default {
|
||||||
|
event.prevent_default();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(delta) = event::mouse_scroll_delta(&event) {
|
if let Some(delta) = event::mouse_scroll_delta(&event) {
|
||||||
handler(0, delta, event::mouse_modifiers(&event));
|
handler(0, delta, event::mouse_modifiers(&event));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,14 @@ impl Window {
|
||||||
|
|
||||||
let id = target.generate_id();
|
let id = target.generate_id();
|
||||||
|
|
||||||
|
let prevent_default = platform_attr.prevent_default;
|
||||||
|
|
||||||
let canvas = backend::Canvas::create(platform_attr)?;
|
let canvas = backend::Canvas::create(platform_attr)?;
|
||||||
let canvas = Rc::new(RefCell::new(canvas));
|
let canvas = Rc::new(RefCell::new(canvas));
|
||||||
|
|
||||||
let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));
|
let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));
|
||||||
|
|
||||||
target.register(&canvas, id);
|
target.register(&canvas, id, prevent_default);
|
||||||
|
|
||||||
let runner = target.runner.clone();
|
let runner = target.runner.clone();
|
||||||
let resize_notify_fn = Box::new(move |new_size| {
|
let resize_notify_fn = Box::new(move |new_size| {
|
||||||
|
@ -392,7 +394,19 @@ impl From<u64> for WindowId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub(crate) canvas: Option<backend::RawCanvasType>,
|
pub(crate) canvas: Option<backend::RawCanvasType>,
|
||||||
|
pub(crate) prevent_default: bool,
|
||||||
|
pub(crate) focusable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
canvas: None,
|
||||||
|
prevent_default: true,
|
||||||
|
focusable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue