Implement ThemeChanged for web target. (#1462)

* Implement ThemeChanged for web target.

* Add TODO upstream to stdweb.

Co-authored-by: Ryan G <ryanisaacg@users.noreply.github.com>
This commit is contained in:
daxpedda 2020-02-17 20:25:27 +01:00 committed by GitHub
parent e88e8bc194
commit d1073dcecb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 92 additions and 2 deletions

View file

@ -5,6 +5,7 @@
- On Wayland, fix color from `close_button_icon_color` not applying. - On Wayland, fix color from `close_button_icon_color` not applying.
- Ignore locale if unsupported by X11 backend - Ignore locale if unsupported by X11 backend
- On Wayland, Add HiDPI cursor support - On Wayland, Add HiDPI cursor support
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
- Fix `Event::to_static` returning `None` for user events. - Fix `Event::to_static` returning `None` for user events.
# 0.21.0 (2020-02-04) # 0.21.0 (2020-02-04)

View file

@ -101,6 +101,8 @@ features = [
'HtmlCanvasElement', 'HtmlCanvasElement',
'HtmlElement', 'HtmlElement',
'KeyboardEvent', 'KeyboardEvent',
'MediaQueryList',
'MediaQueryListEvent',
'MouseEvent', 'MouseEvent',
'Node', 'Node',
'PointerEvent', 'PointerEvent',

View file

@ -149,6 +149,9 @@ If your PR makes notable changes to Winit's features, please update this section
* Getting the device idiom * Getting the device idiom
* Getting the preferred video mode * Getting the preferred video mode
### Web
* Get if systems preferred color scheme is "dark"
## Usability ## Usability
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial) * `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)

View file

@ -14,6 +14,9 @@ use stdweb::web::html_element::CanvasElement;
#[cfg(feature = "stdweb")] #[cfg(feature = "stdweb")]
pub trait WindowExtStdweb { pub trait WindowExtStdweb {
fn canvas(&self) -> CanvasElement; fn canvas(&self) -> CanvasElement;
/// Whether the browser reports the preferred color scheme to be "dark".
fn is_dark_mode(&self) -> bool;
} }
#[cfg(feature = "web-sys")] #[cfg(feature = "web-sys")]
@ -22,6 +25,9 @@ use web_sys::HtmlCanvasElement;
#[cfg(feature = "web-sys")] #[cfg(feature = "web-sys")]
pub trait WindowExtWebSys { pub trait WindowExtWebSys {
fn canvas(&self) -> HtmlCanvasElement; fn canvas(&self) -> HtmlCanvasElement;
/// Whether the browser reports the preferred color scheme to be "dark".
fn is_dark_mode(&self) -> bool;
} }
#[cfg(feature = "stdweb")] #[cfg(feature = "stdweb")]

View file

@ -2,7 +2,7 @@ use super::{backend, device, proxy::Proxy, runner, window};
use crate::dpi::{PhysicalSize, Size}; use crate::dpi::{PhysicalSize, Size};
use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent}; use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent};
use crate::event_loop::ControlFlow; use crate::event_loop::ControlFlow;
use crate::window::WindowId; use crate::window::{Theme, WindowId};
use std::clone::Clone; use std::clone::Clone;
pub struct WindowTarget<T: 'static> { pub struct WindowTarget<T: 'static> {
@ -199,5 +199,18 @@ impl<T> WindowTarget<T> {
}); });
runner.request_redraw(WindowId(id)); runner.request_redraw(WindowId(id));
}); });
let runner = self.runner.clone();
canvas.on_dark_mode(move |is_dark_mode| {
let theme = if is_dark_mode {
Theme::Dark
} else {
Theme::Light
};
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::ThemeChanged(theme),
});
});
} }
} }

View file

@ -6,6 +6,7 @@ use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use stdweb::js;
use stdweb::traits::IPointerEvent; use stdweb::traits::IPointerEvent;
use stdweb::unstable::TryInto; use stdweb::unstable::TryInto;
use stdweb::web::event::{ use stdweb::web::event::{
@ -240,6 +241,22 @@ impl Canvas {
self.on_fullscreen_change = Some(self.add_event(move |_: FullscreenChangeEvent| handler())); self.on_fullscreen_change = Some(self.add_event(move |_: FullscreenChangeEvent| handler()));
} }
pub fn on_dark_mode<F>(&mut self, handler: F)
where
F: 'static + FnMut(bool),
{
// TODO: upstream to stdweb
js! {
var handler = @{handler};
if (window.matchMedia) {
window.matchMedia("(prefers-color-scheme: dark)").addListener(function(e) {
handler(event.matches)
});
}
}
}
fn add_event<E, F>(&self, mut handler: F) -> EventListenerHandle fn add_event<E, F>(&self, mut handler: F) -> EventListenerHandle
where where
E: ConcreteEvent, E: ConcreteEvent,

View file

@ -10,6 +10,7 @@ use crate::platform::web::WindowExtStdweb;
use crate::window::Window; use crate::window::Window;
use stdweb::js; use stdweb::js;
use stdweb::unstable::TryInto;
use stdweb::web::event::BeforeUnloadEvent; use stdweb::web::event::BeforeUnloadEvent;
use stdweb::web::window; use stdweb::web::window;
use stdweb::web::IEventTarget; use stdweb::web::IEventTarget;
@ -31,6 +32,15 @@ impl WindowExtStdweb for Window {
fn canvas(&self) -> CanvasElement { fn canvas(&self) -> CanvasElement {
self.window.canvas().raw().clone() self.window.canvas().raw().clone()
} }
fn is_dark_mode(&self) -> bool {
// TODO: upstream to stdweb
let is_dark_mode = js! {
return (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches)
};
is_dark_mode.try_into().expect("should return a bool")
}
} }
pub fn window_size() -> LogicalSize<f64> { pub fn window_size() -> LogicalSize<f64> {

View file

@ -8,7 +8,10 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use wasm_bindgen::{closure::Closure, JsCast}; use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, PointerEvent, WheelEvent}; use web_sys::{
Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, PointerEvent,
WheelEvent,
};
pub struct Canvas { pub struct Canvas {
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained. /// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
@ -26,6 +29,7 @@ pub struct Canvas {
on_mouse_wheel: Option<Closure<dyn FnMut(WheelEvent)>>, on_mouse_wheel: Option<Closure<dyn FnMut(WheelEvent)>>,
on_fullscreen_change: Option<Closure<dyn FnMut(Event)>>, on_fullscreen_change: Option<Closure<dyn FnMut(Event)>>,
wants_fullscreen: Rc<RefCell<bool>>, wants_fullscreen: Rc<RefCell<bool>>,
on_dark_mode: Option<Closure<dyn FnMut(MediaQueryListEvent)>>,
} }
impl Drop for Canvas { impl Drop for Canvas {
@ -77,6 +81,7 @@ impl Canvas {
on_mouse_wheel: None, on_mouse_wheel: None,
on_fullscreen_change: None, on_fullscreen_change: None,
wants_fullscreen: Rc::new(RefCell::new(false)), wants_fullscreen: Rc::new(RefCell::new(false)),
on_dark_mode: None,
}) })
} }
@ -251,6 +256,28 @@ impl Canvas {
Some(self.add_event("fullscreenchange", move |_: Event| handler())); Some(self.add_event("fullscreenchange", move |_: Event| handler()));
} }
pub fn on_dark_mode<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(bool),
{
let window = web_sys::window().expect("Failed to obtain window");
self.on_dark_mode = window
.match_media("(prefers-color-scheme: dark)")
.ok()
.flatten()
.and_then(|media| {
let closure = Closure::wrap(Box::new(move |event: MediaQueryListEvent| {
handler(event.matches())
}) as Box<dyn FnMut(_)>);
media
.add_listener_with_opt_callback(Some(&closure.as_ref().unchecked_ref()))
.map(|_| closure)
.ok()
});
}
fn add_event<E, F>(&self, event_name: &str, mut handler: F) -> Closure<dyn FnMut(E)> fn add_event<E, F>(&self, event_name: &str, mut handler: F) -> Closure<dyn FnMut(E)>
where where
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi, E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,

View file

@ -38,6 +38,17 @@ impl WindowExtWebSys for Window {
fn canvas(&self) -> HtmlCanvasElement { fn canvas(&self) -> HtmlCanvasElement {
self.window.canvas().raw().clone() self.window.canvas().raw().clone()
} }
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)
}
} }
pub fn window_size() -> LogicalSize<f64> { pub fn window_size() -> LogicalSize<f64> {