On Web, implement Send and Sync where appropriate (#2834)
This commit is contained in:
parent
eb2d3894ef
commit
8f7f3efc0d
21 changed files with 609 additions and 196 deletions
|
|
@ -36,19 +36,20 @@ pub struct Canvas {
|
|||
}
|
||||
|
||||
pub struct Common {
|
||||
pub window: web_sys::Window,
|
||||
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
|
||||
pub raw: HtmlCanvasElement,
|
||||
wants_fullscreen: Rc<RefCell<bool>>,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
pub fn create(attr: PlatformSpecificWindowBuilderAttributes) -> Result<Self, RootOE> {
|
||||
pub fn create(
|
||||
window: web_sys::Window,
|
||||
attr: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Self, RootOE> {
|
||||
let canvas = match attr.canvas {
|
||||
Some(canvas) => canvas,
|
||||
None => {
|
||||
let window = web_sys::window()
|
||||
.ok_or_else(|| os_error!(OsError("Failed to obtain window".to_owned())))?;
|
||||
|
||||
let document = window
|
||||
.document()
|
||||
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
|
||||
|
|
@ -73,6 +74,7 @@ impl Canvas {
|
|||
|
||||
Ok(Canvas {
|
||||
common: Common {
|
||||
window,
|
||||
raw: canvas,
|
||||
wants_fullscreen: Rc::new(RefCell::new(false)),
|
||||
},
|
||||
|
|
@ -93,9 +95,9 @@ impl Canvas {
|
|||
if lock {
|
||||
self.raw().request_pointer_lock();
|
||||
} else {
|
||||
let window = web_sys::window()
|
||||
.ok_or_else(|| os_error!(OsError("Failed to obtain window".to_owned())))?;
|
||||
let document = window
|
||||
let document = self
|
||||
.common
|
||||
.window
|
||||
.document()
|
||||
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
|
||||
document.exit_pointer_lock();
|
||||
|
|
@ -294,12 +296,13 @@ impl Canvas {
|
|||
where
|
||||
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
|
||||
{
|
||||
let window = self.common.window.clone();
|
||||
self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| {
|
||||
if prevent_default {
|
||||
event.prevent_default();
|
||||
}
|
||||
|
||||
if let Some(delta) = event::mouse_scroll_delta(&event) {
|
||||
if let Some(delta) = event::mouse_scroll_delta(&window, &event) {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, delta, modifiers);
|
||||
}
|
||||
|
|
@ -325,7 +328,8 @@ impl Canvas {
|
|||
Box::new(move |event: MediaQueryListEvent| handler(event.matches()))
|
||||
as Box<dyn FnMut(_)>,
|
||||
);
|
||||
self.on_dark_mode = MediaQueryListHandle::new("(prefers-color-scheme: dark)", closure);
|
||||
self.on_dark_mode =
|
||||
MediaQueryListHandle::new(&self.common.window, "(prefers-color-scheme: dark)", closure);
|
||||
}
|
||||
|
||||
pub fn request_fullscreen(&self) {
|
||||
|
|
@ -426,6 +430,6 @@ impl Common {
|
|||
}
|
||||
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
super::is_fullscreen(&self.raw)
|
||||
super::is_fullscreen(&self.window, &self.raw)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,14 +79,17 @@ pub fn mouse_position_by_client(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mouse_scroll_delta(event: &WheelEvent) -> Option<MouseScrollDelta> {
|
||||
pub fn mouse_scroll_delta(
|
||||
window: &web_sys::Window,
|
||||
event: &WheelEvent,
|
||||
) -> Option<MouseScrollDelta> {
|
||||
let x = -event.delta_x();
|
||||
let y = -event.delta_y();
|
||||
|
||||
match event.delta_mode() {
|
||||
WheelEvent::DOM_DELTA_LINE => Some(MouseScrollDelta::LineDelta(x as f32, y as f32)),
|
||||
WheelEvent::DOM_DELTA_PIXEL => {
|
||||
let delta = LogicalPosition::new(x, y).to_physical(super::scale_factor());
|
||||
let delta = LogicalPosition::new(x, y).to_physical(super::scale_factor(window));
|
||||
Some(MouseScrollDelta::PixelDelta(delta))
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ pub(super) struct MediaQueryListHandle {
|
|||
|
||||
impl MediaQueryListHandle {
|
||||
pub fn new(
|
||||
window: &web_sys::Window,
|
||||
media_query: &str,
|
||||
listener: Closure<dyn FnMut(MediaQueryListEvent)>,
|
||||
) -> Option<Self> {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
let mql = window
|
||||
.match_media(media_query)
|
||||
.ok()
|
||||
|
|
|
|||
|
|
@ -15,14 +15,13 @@ use crate::dpi::{LogicalSize, Size};
|
|||
use crate::platform::web::WindowExtWebSys;
|
||||
use crate::window::Window;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use web_sys::{window, BeforeUnloadEvent, Element, HtmlCanvasElement};
|
||||
use web_sys::{BeforeUnloadEvent, Element, HtmlCanvasElement};
|
||||
|
||||
pub fn throw(msg: &str) {
|
||||
wasm_bindgen::throw_str(msg);
|
||||
}
|
||||
|
||||
pub fn exit_fullscreen() {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
pub fn exit_fullscreen(window: &web_sys::Window) {
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
document.exit_fullscreen();
|
||||
|
|
@ -32,38 +31,33 @@ pub struct UnloadEventHandle {
|
|||
_listener: event_handle::EventListenerHandle<dyn FnMut(BeforeUnloadEvent)>,
|
||||
}
|
||||
|
||||
pub fn on_unload(mut handler: impl FnMut() + 'static) -> UnloadEventHandle {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
pub fn on_unload(
|
||||
window: &web_sys::Window,
|
||||
mut handler: impl FnMut() + 'static,
|
||||
) -> UnloadEventHandle {
|
||||
let closure = Closure::wrap(
|
||||
Box::new(move |_: BeforeUnloadEvent| handler()) as Box<dyn FnMut(BeforeUnloadEvent)>
|
||||
);
|
||||
|
||||
let listener = event_handle::EventListenerHandle::new(&window, "beforeunload", closure);
|
||||
let listener = event_handle::EventListenerHandle::new(window, "beforeunload", closure);
|
||||
UnloadEventHandle {
|
||||
_listener: listener,
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtWebSys for Window {
|
||||
fn canvas(&self) -> HtmlCanvasElement {
|
||||
self.window.canvas().raw().clone()
|
||||
fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||
self.window.canvas()
|
||||
}
|
||||
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
window
|
||||
.match_media("(prefers-color-scheme: dark)")
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|media| media.matches())
|
||||
.unwrap_or(false)
|
||||
self.window
|
||||
.inner
|
||||
.queue(|inner| is_dark_mode(&inner.window).unwrap_or(false))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_size() -> LogicalSize<f64> {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
pub fn window_size(window: &web_sys::Window) -> LogicalSize<f64> {
|
||||
let width = window
|
||||
.inner_width()
|
||||
.expect("Failed to get width")
|
||||
|
|
@ -78,13 +72,12 @@ pub fn window_size() -> LogicalSize<f64> {
|
|||
LogicalSize { width, height }
|
||||
}
|
||||
|
||||
pub fn scale_factor() -> f64 {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
pub fn scale_factor(window: &web_sys::Window) -> f64 {
|
||||
window.device_pixel_ratio()
|
||||
}
|
||||
|
||||
pub fn set_canvas_size(raw: &HtmlCanvasElement, size: Size) {
|
||||
let scale_factor = scale_factor();
|
||||
pub fn set_canvas_size(window: &web_sys::Window, raw: &HtmlCanvasElement, size: Size) {
|
||||
let scale_factor = scale_factor(window);
|
||||
let logical_size = size.to_logical::<f64>(scale_factor);
|
||||
|
||||
set_canvas_style_property(raw, "width", &format!("{}px", logical_size.width));
|
||||
|
|
@ -98,8 +91,7 @@ pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value:
|
|||
.unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}"))
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(canvas: &HtmlCanvasElement) -> bool {
|
||||
let window = window().expect("Failed to obtain window");
|
||||
pub fn is_fullscreen(window: &web_sys::Window, canvas: &HtmlCanvasElement) -> bool {
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
match document.fullscreen_element() {
|
||||
|
|
@ -111,4 +103,12 @@ pub fn is_fullscreen(canvas: &HtmlCanvasElement) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
|
||||
window
|
||||
.match_media("(prefers-color-scheme: dark)")
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|media| media.matches())
|
||||
}
|
||||
|
||||
pub type RawCanvasType = HtmlCanvasElement;
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ impl PointerHandler {
|
|||
M: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
let canvas = canvas_common.raw.clone();
|
||||
self.on_pointer_release = Some(canvas_common.add_user_event(
|
||||
"pointerup",
|
||||
|
|
@ -105,12 +106,13 @@ impl PointerHandler {
|
|||
match event.pointer_type().as_str() {
|
||||
"touch" => touch_handler(
|
||||
event.pointer_id(),
|
||||
event::touch_position(&event, &canvas).to_physical(super::scale_factor()),
|
||||
event::touch_position(&event, &canvas)
|
||||
.to_physical(super::scale_factor(&window)),
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
),
|
||||
"mouse" => mouse_handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
event::mouse_button(&event).expect("no mouse button released"),
|
||||
),
|
||||
_ => (),
|
||||
|
|
@ -131,6 +133,7 @@ impl PointerHandler {
|
|||
M: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton),
|
||||
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
let canvas = canvas_common.raw.clone();
|
||||
self.on_pointer_press = Some(canvas_common.add_user_event(
|
||||
"pointerdown",
|
||||
|
|
@ -149,14 +152,14 @@ impl PointerHandler {
|
|||
touch_handler(
|
||||
event.pointer_id(),
|
||||
event::touch_position(&event, &canvas)
|
||||
.to_physical(super::scale_factor()),
|
||||
.to_physical(super::scale_factor(&window)),
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
}
|
||||
"mouse" => {
|
||||
mouse_handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
event::mouse_button(&event).expect("no mouse button pressed"),
|
||||
);
|
||||
|
||||
|
|
@ -185,6 +188,7 @@ impl PointerHandler {
|
|||
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
||||
B: 'static + FnMut(i32, PhysicalPosition<f64>, ButtonsState, MouseButton),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
let canvas = canvas_common.raw.clone();
|
||||
self.on_cursor_move = Some(canvas_common.add_event(
|
||||
if has_pointer_raw_support(&canvas) {
|
||||
|
|
@ -230,7 +234,7 @@ impl PointerHandler {
|
|||
|
||||
button_handler(
|
||||
id,
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
event::mouse_buttons(&event),
|
||||
button,
|
||||
);
|
||||
|
|
@ -261,13 +265,13 @@ impl PointerHandler {
|
|||
match pointer_type.as_str() {
|
||||
"mouse" => mouse_handler(
|
||||
id,
|
||||
event::mouse_position(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_delta(&event).to_physical(super::scale_factor()),
|
||||
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
|
||||
event::mouse_delta(&event).to_physical(super::scale_factor(&window)),
|
||||
),
|
||||
"touch" => touch_handler(
|
||||
id,
|
||||
event::touch_position(&event, &canvas)
|
||||
.to_physical(super::scale_factor()),
|
||||
.to_physical(super::scale_factor(&window)),
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
),
|
||||
_ => unreachable!("didn't return early before"),
|
||||
|
|
@ -281,6 +285,7 @@ impl PointerHandler {
|
|||
where
|
||||
F: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
||||
{
|
||||
let window = canvas_common.window.clone();
|
||||
let canvas = canvas_common.raw.clone();
|
||||
self.on_touch_cancel = Some(canvas_common.add_event(
|
||||
"pointercancel",
|
||||
|
|
@ -288,7 +293,8 @@ impl PointerHandler {
|
|||
if event.pointer_type() == "touch" {
|
||||
handler(
|
||||
event.pointer_id(),
|
||||
event::touch_position(&event, &canvas).to_physical(super::scale_factor()),
|
||||
event::touch_position(&event, &canvas)
|
||||
.to_physical(super::scale_factor(&window)),
|
||||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,29 +8,31 @@ use web_sys::MediaQueryListEvent;
|
|||
pub struct ScaleChangeDetector(Rc<RefCell<ScaleChangeDetectorInternal>>);
|
||||
|
||||
impl ScaleChangeDetector {
|
||||
pub(crate) fn new<F>(handler: F) -> Self
|
||||
pub(crate) fn new<F>(window: web_sys::Window, handler: F) -> Self
|
||||
where
|
||||
F: 'static + FnMut(ScaleChangeArgs),
|
||||
{
|
||||
Self(ScaleChangeDetectorInternal::new(handler))
|
||||
Self(ScaleChangeDetectorInternal::new(window, handler))
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper type to help manage the `MediaQueryList` used for detecting
|
||||
/// changes of the `devicePixelRatio`.
|
||||
struct ScaleChangeDetectorInternal {
|
||||
window: web_sys::Window,
|
||||
callback: Box<dyn FnMut(ScaleChangeArgs)>,
|
||||
mql: Option<MediaQueryListHandle>,
|
||||
last_scale: f64,
|
||||
}
|
||||
|
||||
impl ScaleChangeDetectorInternal {
|
||||
fn new<F>(handler: F) -> Rc<RefCell<Self>>
|
||||
fn new<F>(window: web_sys::Window, handler: F) -> Rc<RefCell<Self>>
|
||||
where
|
||||
F: 'static + FnMut(ScaleChangeArgs),
|
||||
{
|
||||
let current_scale = super::scale_factor();
|
||||
let current_scale = super::scale_factor(&window);
|
||||
let new_self = Rc::new(RefCell::new(Self {
|
||||
window: window.clone(),
|
||||
callback: Box::new(handler),
|
||||
mql: None,
|
||||
last_scale: current_scale,
|
||||
|
|
@ -43,7 +45,7 @@ impl ScaleChangeDetectorInternal {
|
|||
}
|
||||
}) as Box<dyn FnMut(_)>);
|
||||
|
||||
let mql = Self::create_mql(closure);
|
||||
let mql = Self::create_mql(&window, closure);
|
||||
{
|
||||
let mut borrowed_self = new_self.borrow_mut();
|
||||
borrowed_self.mql = mql;
|
||||
|
|
@ -52,9 +54,10 @@ impl ScaleChangeDetectorInternal {
|
|||
}
|
||||
|
||||
fn create_mql(
|
||||
window: &web_sys::Window,
|
||||
closure: Closure<dyn FnMut(MediaQueryListEvent)>,
|
||||
) -> Option<MediaQueryListHandle> {
|
||||
let current_scale = super::scale_factor();
|
||||
let current_scale = super::scale_factor(window);
|
||||
// This media query initially matches the current `devicePixelRatio`.
|
||||
// We add 0.0001 to the lower and upper bounds such that it won't fail
|
||||
// due to floating point precision limitations.
|
||||
|
|
@ -63,7 +66,7 @@ impl ScaleChangeDetectorInternal {
|
|||
(-webkit-min-device-pixel-ratio: {min_scale:.4}) and (-webkit-max-device-pixel-ratio: {max_scale:.4})",
|
||||
min_scale = current_scale - 0.0001, max_scale= current_scale + 0.0001,
|
||||
);
|
||||
let mql = MediaQueryListHandle::new(&media_query, closure);
|
||||
let mql = MediaQueryListHandle::new(window, &media_query, closure);
|
||||
if let Some(mql) = &mql {
|
||||
assert!(mql.mql().matches());
|
||||
}
|
||||
|
|
@ -76,12 +79,12 @@ impl ScaleChangeDetectorInternal {
|
|||
.take()
|
||||
.expect("DevicePixelRatioChangeDetector::mql should not be None");
|
||||
let closure = mql.remove();
|
||||
let new_scale = super::scale_factor();
|
||||
let new_scale = super::scale_factor(&self.window);
|
||||
(self.callback)(ScaleChangeArgs {
|
||||
old_scale: self.last_scale,
|
||||
new_scale,
|
||||
});
|
||||
let new_mql = Self::create_mql(closure);
|
||||
let new_mql = Self::create_mql(&self.window, closure);
|
||||
self.mql = new_mql;
|
||||
self.last_scale = new_scale;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,16 @@ use wasm_bindgen::JsCast;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Timeout {
|
||||
window: web_sys::Window,
|
||||
handle: i32,
|
||||
_closure: Closure<dyn FnMut()>,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
pub fn new<F>(f: F, duration: Duration) -> Timeout
|
||||
pub fn new<F>(window: web_sys::Window, f: F, duration: Duration) -> Timeout
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
let closure = Closure::wrap(Box::new(f) as Box<dyn FnMut()>);
|
||||
|
||||
let handle = window
|
||||
|
|
@ -27,6 +26,7 @@ impl Timeout {
|
|||
.expect("Failed to set timeout");
|
||||
|
||||
Timeout {
|
||||
window,
|
||||
handle,
|
||||
_closure: closure,
|
||||
}
|
||||
|
|
@ -35,14 +35,13 @@ impl Timeout {
|
|||
|
||||
impl Drop for Timeout {
|
||||
fn drop(&mut self) {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
window.clear_timeout_with_handle(self.handle);
|
||||
self.window.clear_timeout_with_handle(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationFrameRequest {
|
||||
window: web_sys::Window,
|
||||
handle: i32,
|
||||
// track callback state, because `cancelAnimationFrame` is slow
|
||||
fired: Rc<Cell<bool>>,
|
||||
|
|
@ -50,12 +49,10 @@ pub struct AnimationFrameRequest {
|
|||
}
|
||||
|
||||
impl AnimationFrameRequest {
|
||||
pub fn new<F>(mut f: F) -> AnimationFrameRequest
|
||||
pub fn new<F>(window: web_sys::Window, mut f: F) -> AnimationFrameRequest
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
|
||||
let fired = Rc::new(Cell::new(false));
|
||||
let c_fired = fired.clone();
|
||||
let closure = Closure::wrap(Box::new(move || {
|
||||
|
|
@ -68,6 +65,7 @@ impl AnimationFrameRequest {
|
|||
.expect("Failed to request animation frame");
|
||||
|
||||
AnimationFrameRequest {
|
||||
window,
|
||||
handle,
|
||||
fired,
|
||||
_closure: closure,
|
||||
|
|
@ -78,8 +76,7 @@ impl AnimationFrameRequest {
|
|||
impl Drop for AnimationFrameRequest {
|
||||
fn drop(&mut self) {
|
||||
if !(*self.fired).get() {
|
||||
let window = web_sys::window().expect("Failed to obtain window");
|
||||
window
|
||||
self.window
|
||||
.cancel_animation_frame(self.handle)
|
||||
.expect("Failed to cancel animation frame");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue