mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
Make media queries more robust
This commit is contained in:
parent
c88a4ab221
commit
12fb37d827
|
@ -73,6 +73,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
|||
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
|
||||
- On Web, use the correct canvas size when calculating the new size during scale factor change,
|
||||
instead of using the output bitmap size.
|
||||
- On Web, scale factor and dark mode detection are now more robust.
|
||||
|
||||
# 0.28.6
|
||||
|
||||
|
|
|
@ -145,7 +145,6 @@ features = [
|
|||
'HtmlElement',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MediaQueryListEvent',
|
||||
'Node',
|
||||
'PointerEvent',
|
||||
'Window',
|
||||
|
|
|
@ -17,9 +17,7 @@ use smol_str::SmolStr;
|
|||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{
|
||||
Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, WheelEvent,
|
||||
};
|
||||
use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Canvas {
|
||||
|
@ -343,13 +341,11 @@ impl Canvas {
|
|||
where
|
||||
F: 'static + FnMut(bool),
|
||||
{
|
||||
let closure =
|
||||
Closure::wrap(
|
||||
Box::new(move |event: MediaQueryListEvent| handler(event.matches()))
|
||||
as Box<dyn FnMut(_)>,
|
||||
);
|
||||
self.on_dark_mode =
|
||||
MediaQueryListHandle::new(&self.common.window, "(prefers-color-scheme: dark)", closure);
|
||||
self.on_dark_mode = Some(MediaQueryListHandle::new(
|
||||
&self.common.window,
|
||||
"(prefers-color-scheme: dark)",
|
||||
move |mql| handler(mql.matches()),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn request_fullscreen(&self) {
|
||||
|
|
|
@ -1,54 +1,46 @@
|
|||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use web_sys::{MediaQueryList, MediaQueryListEvent};
|
||||
use web_sys::MediaQueryList;
|
||||
|
||||
pub(super) struct MediaQueryListHandle {
|
||||
mql: MediaQueryList,
|
||||
listener: Option<Closure<dyn FnMut(MediaQueryListEvent)>>,
|
||||
closure: Closure<dyn FnMut()>,
|
||||
}
|
||||
|
||||
impl MediaQueryListHandle {
|
||||
pub fn new(
|
||||
window: &web_sys::Window,
|
||||
media_query: &str,
|
||||
listener: Closure<dyn FnMut(MediaQueryListEvent)>,
|
||||
) -> Option<Self> {
|
||||
pub fn new<F>(window: &web_sys::Window, media_query: &str, mut listener: F) -> Self
|
||||
where
|
||||
F: 'static + FnMut(&MediaQueryList),
|
||||
{
|
||||
let mql = window
|
||||
.match_media(media_query)
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|mql| {
|
||||
mql.add_listener_with_opt_callback(Some(listener.as_ref().unchecked_ref()))
|
||||
.map(|_| mql)
|
||||
.ok()
|
||||
});
|
||||
mql.map(|mql| Self {
|
||||
mql,
|
||||
listener: Some(listener),
|
||||
})
|
||||
.expect("Failed to parse media query")
|
||||
.expect("Found empty media query");
|
||||
|
||||
let closure = Closure::new({
|
||||
let mql = mql.clone();
|
||||
move || listener(&mql)
|
||||
});
|
||||
// TODO: Replace obsolete `addListener()` with `addEventListener()` and use
|
||||
// `MediaQueryListEvent` instead of cloning the `MediaQueryList`.
|
||||
// Requires Safari v14.
|
||||
mql.add_listener_with_opt_callback(Some(closure.as_ref().unchecked_ref()))
|
||||
.expect("Invalid listener");
|
||||
|
||||
Self { mql, closure }
|
||||
}
|
||||
|
||||
pub fn mql(&self) -> &MediaQueryList {
|
||||
&self.mql
|
||||
}
|
||||
|
||||
/// Removes the listener and returns the original listener closure, which
|
||||
/// can be reused.
|
||||
pub fn remove(mut self) -> Closure<dyn FnMut(MediaQueryListEvent)> {
|
||||
let listener = self.listener.take().unwrap_or_else(|| unreachable!());
|
||||
remove_listener(&self.mql, &listener);
|
||||
listener
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MediaQueryListHandle {
|
||||
fn drop(&mut self) {
|
||||
if let Some(listener) = self.listener.take() {
|
||||
remove_listener(&self.mql, &listener);
|
||||
}
|
||||
remove_listener(&self.mql, &self.closure);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_listener(mql: &MediaQueryList, listener: &Closure<dyn FnMut(MediaQueryListEvent)>) {
|
||||
fn remove_listener(mql: &MediaQueryList, listener: &Closure<dyn FnMut()>) {
|
||||
mql.remove_listener_with_opt_callback(Some(listener.as_ref().unchecked_ref()))
|
||||
.unwrap_or_else(|e| {
|
||||
web_sys::console::error_2(&"Error removing media query listener".into(), &e)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use web_sys::MediaQueryList;
|
||||
|
||||
use super::super::ScaleChangeArgs;
|
||||
use super::media_query_handle::MediaQueryListHandle;
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use web_sys::MediaQueryListEvent;
|
||||
|
||||
pub struct ScaleChangeDetector(Rc<RefCell<ScaleChangeDetectorInternal>>);
|
||||
|
||||
|
@ -39,53 +39,57 @@ impl ScaleChangeDetectorInternal {
|
|||
}));
|
||||
|
||||
let weak_self = Rc::downgrade(&new_self);
|
||||
let closure = Closure::wrap(Box::new(move |event: MediaQueryListEvent| {
|
||||
let mql = Self::create_mql(&window, move |mql| {
|
||||
if let Some(rc_self) = weak_self.upgrade() {
|
||||
rc_self.borrow_mut().handler(event);
|
||||
Self::handler(rc_self, mql);
|
||||
}
|
||||
}) as Box<dyn FnMut(_)>);
|
||||
|
||||
let mql = Self::create_mql(&window, closure);
|
||||
});
|
||||
{
|
||||
let mut borrowed_self = new_self.borrow_mut();
|
||||
borrowed_self.mql = mql;
|
||||
borrowed_self.mql = Some(mql);
|
||||
}
|
||||
new_self
|
||||
}
|
||||
|
||||
fn create_mql(
|
||||
window: &web_sys::Window,
|
||||
closure: Closure<dyn FnMut(MediaQueryListEvent)>,
|
||||
) -> Option<MediaQueryListHandle> {
|
||||
fn create_mql<F>(window: &web_sys::Window, closure: F) -> MediaQueryListHandle
|
||||
where
|
||||
F: 'static + FnMut(&MediaQueryList),
|
||||
{
|
||||
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.
|
||||
// TODO: Remove `-webkit-device-pixel-ratio`. Requires Safari v16.
|
||||
let media_query = format!(
|
||||
"(min-resolution: {min_scale:.4}dppx) and (max-resolution: {max_scale:.4}dppx),
|
||||
(-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,
|
||||
"(resolution: {current_scale}dppx),
|
||||
(-webkit-device-pixel-ratio: {current_scale})",
|
||||
);
|
||||
let mql = MediaQueryListHandle::new(window, &media_query, closure);
|
||||
if let Some(mql) = &mql {
|
||||
assert!(mql.mql().matches());
|
||||
}
|
||||
assert!(
|
||||
mql.mql().matches(),
|
||||
"created media query doesn't match, {current_scale} != {}",
|
||||
super::scale_factor(window,)
|
||||
);
|
||||
mql
|
||||
}
|
||||
|
||||
fn handler(&mut self, _event: MediaQueryListEvent) {
|
||||
let mql = self
|
||||
.mql
|
||||
.take()
|
||||
.expect("DevicePixelRatioChangeDetector::mql should not be None");
|
||||
let closure = mql.remove();
|
||||
let new_scale = super::scale_factor(&self.window);
|
||||
(self.callback)(ScaleChangeArgs {
|
||||
old_scale: self.last_scale,
|
||||
fn handler(this: Rc<RefCell<Self>>, mql: &MediaQueryList) {
|
||||
let weak_self = Rc::downgrade(&this);
|
||||
let mut this = this.borrow_mut();
|
||||
let old_scale = this.last_scale;
|
||||
let new_scale = super::scale_factor(&this.window);
|
||||
(this.callback)(ScaleChangeArgs {
|
||||
old_scale,
|
||||
new_scale,
|
||||
});
|
||||
let new_mql = Self::create_mql(&self.window, closure);
|
||||
self.mql = new_mql;
|
||||
self.last_scale = new_scale;
|
||||
|
||||
// If this matches, then the scale factor is back to it's
|
||||
// old value again, so we won't need to update the query.
|
||||
if !mql.matches() {
|
||||
let new_mql = Self::create_mql(&this.window, move |mql| {
|
||||
if let Some(rc_self) = weak_self.upgrade() {
|
||||
Self::handler(rc_self, mql);
|
||||
}
|
||||
});
|
||||
this.mql = Some(new_mql);
|
||||
this.last_scale = new_scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue