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