mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
Web: Fullscreen Overhaul (#3063)
This commit is contained in:
parent
1dfca5a395
commit
0c8cf94a70
|
@ -15,6 +15,8 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
|
- **Breaking:** Move `Event::RedrawRequested` to `WindowEvent::RedrawRequested`.
|
||||||
- On macOS, fix crash in `window.set_minimized(false)`.
|
- On macOS, fix crash in `window.set_minimized(false)`.
|
||||||
- On Web, enable event propagation and let `DeviceEvent`s appear after `WindowEvent`s.
|
- On Web, enable event propagation and let `DeviceEvent`s appear after `WindowEvent`s.
|
||||||
|
- On Web, take all transient activations on the canvas and window into account to queue a fullscreen request.
|
||||||
|
- On Web, remove any fullscreen requests from the queue when an external fullscreen activation was detected.
|
||||||
|
|
||||||
# 0.29.1-beta
|
# 0.29.1-beta
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,10 @@ mod wasm {
|
||||||
|
|
||||||
use softbuffer::{Surface, SurfaceExtWeb};
|
use softbuffer::{Surface, SurfaceExtWeb};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use winit::{event::Event, window::Window};
|
use winit::{
|
||||||
|
event::{Event, WindowEvent},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
#[wasm_bindgen(start)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
@ -116,6 +119,10 @@ mod wasm {
|
||||||
// So we implement this basic logging system into the page to give developers an easy alternative.
|
// So we implement this basic logging system into the page to give developers an easy alternative.
|
||||||
// As a bonus its also kind of handy on desktop.
|
// As a bonus its also kind of handy on desktop.
|
||||||
let event = match event {
|
let event = match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => None,
|
||||||
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
||||||
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub struct Execution<T: 'static> {
|
||||||
on_key_press: OnEventHandle<KeyboardEvent>,
|
on_key_press: OnEventHandle<KeyboardEvent>,
|
||||||
on_key_release: OnEventHandle<KeyboardEvent>,
|
on_key_release: OnEventHandle<KeyboardEvent>,
|
||||||
on_visibility_change: OnEventHandle<web_sys::Event>,
|
on_visibility_change: OnEventHandle<web_sys::Event>,
|
||||||
|
on_touch_end: OnEventHandle<web_sys::Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RunnerEnum<T: 'static> {
|
enum RunnerEnum<T: 'static> {
|
||||||
|
@ -167,6 +168,7 @@ impl<T: 'static> Shared<T> {
|
||||||
on_key_press: RefCell::new(None),
|
on_key_press: RefCell::new(None),
|
||||||
on_key_release: RefCell::new(None),
|
on_key_release: RefCell::new(None),
|
||||||
on_visibility_change: RefCell::new(None),
|
on_visibility_change: RefCell::new(None),
|
||||||
|
on_touch_end: RefCell::new(None),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +326,8 @@ impl<T: 'static> Shared<T> {
|
||||||
self.window().clone(),
|
self.window().clone(),
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
Closure::new(move |event: PointerEvent| {
|
Closure::new(move |event: PointerEvent| {
|
||||||
|
runner.transient_activation();
|
||||||
|
|
||||||
if !runner.device_events() {
|
if !runner.device_events() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -347,6 +351,8 @@ impl<T: 'static> Shared<T> {
|
||||||
self.window().clone(),
|
self.window().clone(),
|
||||||
"pointerup",
|
"pointerup",
|
||||||
Closure::new(move |event: PointerEvent| {
|
Closure::new(move |event: PointerEvent| {
|
||||||
|
runner.transient_activation();
|
||||||
|
|
||||||
if !runner.device_events() {
|
if !runner.device_events() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -370,6 +376,8 @@ impl<T: 'static> Shared<T> {
|
||||||
self.window().clone(),
|
self.window().clone(),
|
||||||
"keydown",
|
"keydown",
|
||||||
Closure::new(move |event: KeyboardEvent| {
|
Closure::new(move |event: KeyboardEvent| {
|
||||||
|
runner.transient_activation();
|
||||||
|
|
||||||
if !runner.device_events() {
|
if !runner.device_events() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -428,6 +436,14 @@ impl<T: 'static> Shared<T> {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
|
let runner = self.clone();
|
||||||
|
*self.0.on_touch_end.borrow_mut() = Some(EventListenerHandle::new(
|
||||||
|
self.window().clone(),
|
||||||
|
"touchend",
|
||||||
|
Closure::new(move |_| {
|
||||||
|
runner.transient_activation();
|
||||||
|
}),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a strictly increasing ID
|
// Generate a strictly increasing ID
|
||||||
|
@ -736,7 +752,7 @@ impl<T: 'static> Shared<T> {
|
||||||
self.0.device_events.set(allowed)
|
self.0.device_events.set(allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn device_events(&self) -> bool {
|
fn device_events(&self) -> bool {
|
||||||
match self.0.device_events.get() {
|
match self.0.device_events.get() {
|
||||||
DeviceEvents::Always => true,
|
DeviceEvents::Always => true,
|
||||||
DeviceEvents::WhenFocused => self.0.all_canvases.borrow().iter().any(|(_, canvas)| {
|
DeviceEvents::WhenFocused => self.0.all_canvases.borrow().iter().any(|(_, canvas)| {
|
||||||
|
@ -750,6 +766,14 @@ impl<T: 'static> Shared<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transient_activation(&self) {
|
||||||
|
self.0.all_canvases.borrow().iter().for_each(|(_, canvas)| {
|
||||||
|
if let Some(canvas) = canvas.upgrade() {
|
||||||
|
canvas.borrow().transient_activation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn event_loop_recreation(&self, allow: bool) {
|
pub fn event_loop_recreation(&self, allow: bool) {
|
||||||
self.0.event_loop_recreation.set(allow)
|
self.0.event_loop_recreation.set(allow)
|
||||||
}
|
}
|
||||||
|
|
|
@ -660,6 +660,8 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
|
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
|
||||||
|
|
||||||
|
canvas.on_touch_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
|
||||||
|
|
|
@ -49,6 +49,14 @@ impl AnimationFrameHandler {
|
||||||
|
|
||||||
self.handle.set(Some(handle));
|
self.handle.set(Some(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cancel(&mut self) {
|
||||||
|
if let Some(handle) = self.handle.take() {
|
||||||
|
self.window
|
||||||
|
.cancel_animation_frame(handle)
|
||||||
|
.expect("Failed to cancel animation frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AnimationFrameHandler {
|
impl Drop for AnimationFrameHandler {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use js_sys::Promise;
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
use wasm_bindgen_futures::JsFuture;
|
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
CssStyleDeclaration, Document, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent,
|
CssStyleDeclaration, Document, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent,
|
||||||
};
|
};
|
||||||
|
@ -21,10 +19,11 @@ use crate::window::{WindowAttributes, WindowId as RootWindowId};
|
||||||
use super::super::WindowId;
|
use super::super::WindowId;
|
||||||
use super::animation_frame::AnimationFrameHandler;
|
use super::animation_frame::AnimationFrameHandler;
|
||||||
use super::event_handle::EventListenerHandle;
|
use super::event_handle::EventListenerHandle;
|
||||||
|
use super::fullscreen::FullscreenHandler;
|
||||||
use super::intersection_handle::IntersectionObserverHandle;
|
use super::intersection_handle::IntersectionObserverHandle;
|
||||||
use super::media_query_handle::MediaQueryListHandle;
|
use super::media_query_handle::MediaQueryListHandle;
|
||||||
use super::pointer::PointerHandler;
|
use super::pointer::PointerHandler;
|
||||||
use super::{event, fullscreen, ButtonsState, ResizeScaleHandle};
|
use super::{event, ButtonsState, ResizeScaleHandle};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Canvas {
|
pub struct Canvas {
|
||||||
|
@ -33,7 +32,6 @@ pub struct Canvas {
|
||||||
pub has_focus: Arc<AtomicBool>,
|
pub has_focus: Arc<AtomicBool>,
|
||||||
pub is_intersecting: Option<bool>,
|
pub is_intersecting: Option<bool>,
|
||||||
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
||||||
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
|
||||||
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
||||||
on_blur: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
on_blur: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
||||||
on_keyboard_release: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
on_keyboard_release: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
||||||
|
@ -44,6 +42,7 @@ pub struct Canvas {
|
||||||
on_resize_scale: Option<ResizeScaleHandle>,
|
on_resize_scale: Option<ResizeScaleHandle>,
|
||||||
on_intersect: Option<IntersectionObserverHandle>,
|
on_intersect: Option<IntersectionObserverHandle>,
|
||||||
animation_frame_handler: AnimationFrameHandler,
|
animation_frame_handler: AnimationFrameHandler,
|
||||||
|
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Common {
|
pub struct Common {
|
||||||
|
@ -54,7 +53,7 @@ pub struct Common {
|
||||||
style: CssStyleDeclaration,
|
style: CssStyleDeclaration,
|
||||||
old_size: Rc<Cell<PhysicalSize<u32>>>,
|
old_size: Rc<Cell<PhysicalSize<u32>>>,
|
||||||
current_size: Rc<Cell<PhysicalSize<u32>>>,
|
current_size: Rc<Cell<PhysicalSize<u32>>>,
|
||||||
wants_fullscreen: Rc<RefCell<bool>>,
|
fullscreen_handler: Rc<FullscreenHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
|
@ -101,12 +100,12 @@ impl Canvas {
|
||||||
|
|
||||||
let common = Common {
|
let common = Common {
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
document,
|
document: document.clone(),
|
||||||
raw: canvas,
|
raw: canvas.clone(),
|
||||||
style,
|
style,
|
||||||
old_size: Rc::default(),
|
old_size: Rc::default(),
|
||||||
current_size: Rc::default(),
|
current_size: Rc::default(),
|
||||||
wants_fullscreen: Rc::new(RefCell::new(false)),
|
fullscreen_handler: Rc::new(FullscreenHandler::new(document.clone(), canvas.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(size) = attr.inner_size {
|
if let Some(size) = attr.inner_size {
|
||||||
|
@ -130,7 +129,7 @@ impl Canvas {
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr.fullscreen.is_some() {
|
if attr.fullscreen.is_some() {
|
||||||
common.request_fullscreen();
|
common.fullscreen_handler.request_fullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr.active {
|
if attr.active {
|
||||||
|
@ -143,7 +142,6 @@ impl Canvas {
|
||||||
has_focus: Arc::new(AtomicBool::new(false)),
|
has_focus: Arc::new(AtomicBool::new(false)),
|
||||||
is_intersecting: None,
|
is_intersecting: None,
|
||||||
on_touch_start: None,
|
on_touch_start: None,
|
||||||
on_touch_end: None,
|
|
||||||
on_blur: None,
|
on_blur: None,
|
||||||
on_focus: None,
|
on_focus: None,
|
||||||
on_keyboard_release: None,
|
on_keyboard_release: None,
|
||||||
|
@ -154,6 +152,7 @@ impl Canvas {
|
||||||
on_resize_scale: None,
|
on_resize_scale: None,
|
||||||
on_intersect: None,
|
on_intersect: None,
|
||||||
animation_frame_handler: AnimationFrameHandler::new(window),
|
animation_frame_handler: AnimationFrameHandler::new(window),
|
||||||
|
on_touch_end: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,9 +261,8 @@ impl Canvas {
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
||||||
{
|
{
|
||||||
self.on_keyboard_release = Some(self.common.add_user_event(
|
self.on_keyboard_release =
|
||||||
"keyup",
|
Some(self.common.add_event("keyup", move |event: KeyboardEvent| {
|
||||||
move |event: KeyboardEvent| {
|
|
||||||
if prevent_default {
|
if prevent_default {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
@ -278,15 +276,14 @@ impl Canvas {
|
||||||
event.repeat(),
|
event.repeat(),
|
||||||
modifiers,
|
modifiers,
|
||||||
);
|
);
|
||||||
},
|
}));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool)
|
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
||||||
{
|
{
|
||||||
self.on_keyboard_press = Some(self.common.add_user_event(
|
self.on_keyboard_press = Some(self.common.add_transient_event(
|
||||||
"keydown",
|
"keydown",
|
||||||
move |event: KeyboardEvent| {
|
move |event: KeyboardEvent| {
|
||||||
if prevent_default {
|
if prevent_default {
|
||||||
|
@ -446,12 +443,20 @@ impl Canvas {
|
||||||
self.animation_frame_handler.on_animation_frame(f)
|
self.animation_frame_handler.on_animation_frame(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_touch_end(&mut self) {
|
||||||
|
self.on_touch_end = Some(self.common.add_transient_event("touchend", |_| {}));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request_fullscreen(&self) {
|
pub fn request_fullscreen(&self) {
|
||||||
self.common.request_fullscreen()
|
self.common.fullscreen_handler.request_fullscreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_fullscreen(&self) {
|
||||||
|
self.common.fullscreen_handler.exit_fullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fullscreen(&self) -> bool {
|
pub fn is_fullscreen(&self) -> bool {
|
||||||
self.common.is_fullscreen()
|
self.common.fullscreen_handler.is_fullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_animation_frame(&self) {
|
pub fn request_animation_frame(&self) {
|
||||||
|
@ -502,7 +507,12 @@ impl Canvas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn transient_activation(&self) {
|
||||||
|
self.common.fullscreen_handler.transient_activation()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_listeners(&mut self) {
|
pub fn remove_listeners(&mut self) {
|
||||||
|
self.on_touch_start = None;
|
||||||
self.on_focus = None;
|
self.on_focus = None;
|
||||||
self.on_blur = None;
|
self.on_blur = None;
|
||||||
self.on_keyboard_release = None;
|
self.on_keyboard_release = None;
|
||||||
|
@ -512,6 +522,9 @@ impl Canvas {
|
||||||
self.pointer_handler.remove_listeners();
|
self.pointer_handler.remove_listeners();
|
||||||
self.on_resize_scale = None;
|
self.on_resize_scale = None;
|
||||||
self.on_intersect = None;
|
self.on_intersect = None;
|
||||||
|
self.animation_frame_handler.cancel();
|
||||||
|
self.on_touch_end = None;
|
||||||
|
self.common.fullscreen_handler.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +544,7 @@ impl Common {
|
||||||
// The difference between add_event and add_user_event is that the latter has a special meaning
|
// The difference between add_event and add_user_event is that the latter has a special meaning
|
||||||
// for browser security. A user event is a deliberate action by the user (like a mouse or key
|
// for browser security. A user event is a deliberate action by the user (like a mouse or key
|
||||||
// press) and is the only time things like a fullscreen request may be successfully completed.)
|
// press) and is the only time things like a fullscreen request may be successfully completed.)
|
||||||
pub fn add_user_event<E, F>(
|
pub fn add_transient_event<E, F>(
|
||||||
&self,
|
&self,
|
||||||
event_name: &'static str,
|
event_name: &'static str,
|
||||||
mut handler: F,
|
mut handler: F,
|
||||||
|
@ -540,37 +553,14 @@ impl Common {
|
||||||
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
|
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
|
||||||
F: 'static + FnMut(E),
|
F: 'static + FnMut(E),
|
||||||
{
|
{
|
||||||
let wants_fullscreen = self.wants_fullscreen.clone();
|
let fullscreen_handler = Rc::downgrade(&self.fullscreen_handler);
|
||||||
let canvas = self.raw.clone();
|
|
||||||
|
|
||||||
self.add_event(event_name, move |event: E| {
|
self.add_event(event_name, move |event: E| {
|
||||||
handler(event);
|
handler(event);
|
||||||
|
|
||||||
if *wants_fullscreen.borrow() {
|
if let Some(fullscreen_handler) = Weak::upgrade(&fullscreen_handler) {
|
||||||
fullscreen::request_fullscreen(&canvas).expect("Failed to enter fullscreen");
|
fullscreen_handler.transient_activation()
|
||||||
*wants_fullscreen.borrow_mut() = false;
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_fullscreen(&self) {
|
|
||||||
// This should return a `Promise`, but Safari v<16.4 is not up-to-date with the spec.
|
|
||||||
match fullscreen::request_fullscreen(&self.raw) {
|
|
||||||
Ok(value) if !value.is_undefined() => {
|
|
||||||
let promise: Promise = value.unchecked_into();
|
|
||||||
let wants_fullscreen = self.wants_fullscreen.clone();
|
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
if JsFuture::from(promise).await.is_err() {
|
|
||||||
*wants_fullscreen.borrow_mut() = true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// We are on Safari v<16.4, let's try again on the next transient activation.
|
|
||||||
_ => *self.wants_fullscreen.borrow_mut() = true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fullscreen(&self) -> bool {
|
|
||||||
super::is_fullscreen(&self.document, &self.raw)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,145 @@
|
||||||
use once_cell::unsync::OnceCell;
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use js_sys::Promise;
|
||||||
|
use once_cell::unsync::{Lazy, OnceCell};
|
||||||
|
use wasm_bindgen::closure::Closure;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
use web_sys::{Document, Element, HtmlCanvasElement};
|
use web_sys::{Document, Element, HtmlCanvasElement};
|
||||||
|
|
||||||
|
use super::EventListenerHandle;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static FULLSCREEN_API_SUPPORT: OnceCell<bool> = OnceCell::new();
|
static FULLSCREEN_API_SUPPORT: OnceCell<bool> = OnceCell::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canvas_has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool {
|
pub struct FullscreenHandler {
|
||||||
|
document: Document,
|
||||||
|
canvas: HtmlCanvasElement,
|
||||||
|
fullscreen_requested: Rc<Cell<bool>>,
|
||||||
|
_fullscreen_change: EventListenerHandle<dyn FnMut()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullscreenHandler {
|
||||||
|
pub fn new(document: Document, canvas: HtmlCanvasElement) -> Self {
|
||||||
|
let fullscreen_requested = Rc::new(Cell::new(false));
|
||||||
|
let fullscreen_change = EventListenerHandle::new(
|
||||||
|
canvas.clone(),
|
||||||
|
if has_fullscreen_api_support(&canvas) {
|
||||||
|
"fullscreenchange"
|
||||||
|
} else {
|
||||||
|
"webkitfullscreenchange"
|
||||||
|
},
|
||||||
|
Closure::new({
|
||||||
|
let fullscreen_requested = fullscreen_requested.clone();
|
||||||
|
move || {
|
||||||
|
// It doesn't matter if the canvas entered or exitted fullscreen mode,
|
||||||
|
// we don't want to request it again later.
|
||||||
|
fullscreen_requested.set(false);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
document,
|
||||||
|
canvas,
|
||||||
|
fullscreen_requested,
|
||||||
|
_fullscreen_change: fullscreen_change,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_request_fullscreen(&self) {
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
type RequestFullscreen;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = requestFullscreen)]
|
||||||
|
fn request_fullscreen(this: &RequestFullscreen) -> Promise;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = webkitRequestFullscreen)]
|
||||||
|
fn webkit_request_fullscreen(this: &RequestFullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvas: &RequestFullscreen = self.canvas.unchecked_ref();
|
||||||
|
|
||||||
|
if has_fullscreen_api_support(&self.canvas) {
|
||||||
|
thread_local! {
|
||||||
|
static REJECT_HANDLER: Lazy<Closure<dyn FnMut(JsValue)>> = Lazy::new(|| Closure::new(|_| ()));
|
||||||
|
}
|
||||||
|
REJECT_HANDLER.with(|handler| {
|
||||||
|
let _ = canvas.request_fullscreen().catch(handler);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
canvas.webkit_request_fullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_fullscreen(&self) {
|
||||||
|
if !self.is_fullscreen() {
|
||||||
|
self.internal_request_fullscreen();
|
||||||
|
self.fullscreen_requested.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transient_activation(&self) {
|
||||||
|
if self.fullscreen_requested.get() {
|
||||||
|
self.internal_request_fullscreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fullscreen(&self) -> bool {
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
type FullscreenElement;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)]
|
||||||
|
fn webkit_fullscreen_element(this: &FullscreenElement) -> Option<Element>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let element = if has_fullscreen_api_support(&self.canvas) {
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
self.document.fullscreen_element()
|
||||||
|
} else {
|
||||||
|
let document: &FullscreenElement = self.document.unchecked_ref();
|
||||||
|
document.webkit_fullscreen_element()
|
||||||
|
};
|
||||||
|
|
||||||
|
match element {
|
||||||
|
Some(element) => {
|
||||||
|
let canvas: &Element = &self.canvas;
|
||||||
|
canvas == &element
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_fullscreen(&self) {
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
type ExitFullscreen;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = webkitExitFullscreen)]
|
||||||
|
fn webkit_exit_fullscreen(this: &ExitFullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_fullscreen_api_support(&self.canvas) {
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
self.document.exit_fullscreen()
|
||||||
|
} else {
|
||||||
|
let document: &ExitFullscreen = self.document.unchecked_ref();
|
||||||
|
document.webkit_exit_fullscreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fullscreen_requested.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel(&self) {
|
||||||
|
self.fullscreen_requested.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool {
|
||||||
FULLSCREEN_API_SUPPORT.with(|support| {
|
FULLSCREEN_API_SUPPORT.with(|support| {
|
||||||
*support.get_or_init(|| {
|
*support.get_or_init(|| {
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -23,77 +155,3 @@ fn canvas_has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn document_has_fullscreen_api_support(document: &Document) -> bool {
|
|
||||||
FULLSCREEN_API_SUPPORT.with(|support| {
|
|
||||||
*support.get_or_init(|| {
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
type DocumentFullScreenApiSupport;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, getter, js_name = exitFullscreen)]
|
|
||||||
fn has_exit_fullscreen(this: &DocumentFullScreenApiSupport) -> JsValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let support: &DocumentFullScreenApiSupport = document.unchecked_ref();
|
|
||||||
!support.has_exit_fullscreen().is_undefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_fullscreen(canvas: &HtmlCanvasElement) -> Result<JsValue, JsValue> {
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
type RequestFullscreen;
|
|
||||||
|
|
||||||
#[wasm_bindgen(catch, method, js_name = requestFullscreen)]
|
|
||||||
fn request_fullscreen(this: &RequestFullscreen) -> Result<JsValue, JsValue>;
|
|
||||||
|
|
||||||
#[wasm_bindgen(catch, method, js_name = webkitRequestFullscreen)]
|
|
||||||
fn webkit_request_fullscreen(this: &RequestFullscreen) -> Result<JsValue, JsValue>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let element: &RequestFullscreen = canvas.unchecked_ref();
|
|
||||||
|
|
||||||
if canvas_has_fullscreen_api_support(canvas) {
|
|
||||||
element.request_fullscreen()
|
|
||||||
} else {
|
|
||||||
element.webkit_request_fullscreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_fullscreen(document: &Document) {
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
type ExitFullscreen;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = webkitExitFullscreen)]
|
|
||||||
fn webkit_exit_fullscreen(this: &ExitFullscreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if document_has_fullscreen_api_support(document) {
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
|
||||||
document.exit_fullscreen()
|
|
||||||
} else {
|
|
||||||
let document: &ExitFullscreen = document.unchecked_ref();
|
|
||||||
document.webkit_exit_fullscreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fullscreen_element(document: &Document) -> Option<Element> {
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
type FullscreenElement;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)]
|
|
||||||
fn webkit_fullscreen_element(this: &FullscreenElement) -> Option<Element>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if document_has_fullscreen_api_support(document) {
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
|
||||||
document.fullscreen_element()
|
|
||||||
} else {
|
|
||||||
let document: &FullscreenElement = document.unchecked_ref();
|
|
||||||
document.webkit_fullscreen_element()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,17 +18,13 @@ pub use self::schedule::Schedule;
|
||||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||||
use wasm_bindgen::closure::Closure;
|
use wasm_bindgen::closure::Closure;
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
CssStyleDeclaration, Document, Element, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
|
CssStyleDeclaration, Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn throw(msg: &str) {
|
pub fn throw(msg: &str) {
|
||||||
wasm_bindgen::throw_str(msg);
|
wasm_bindgen::throw_str(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_fullscreen(document: &Document) {
|
|
||||||
fullscreen::exit_fullscreen(document);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PageTransitionEventHandle {
|
pub struct PageTransitionEventHandle {
|
||||||
_show_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
_show_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
||||||
_hide_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
_hide_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
||||||
|
@ -175,16 +171,6 @@ pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value:
|
||||||
.unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}"))
|
.unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fullscreen(document: &Document, canvas: &HtmlCanvasElement) -> bool {
|
|
||||||
match fullscreen::fullscreen_element(document) {
|
|
||||||
Some(elem) => {
|
|
||||||
let canvas: &Element = canvas;
|
|
||||||
canvas == &elem
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
|
pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
|
||||||
window
|
window
|
||||||
.match_media("(prefers-color-scheme: dark)")
|
.match_media("(prefers-color-scheme: dark)")
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl PointerHandler {
|
||||||
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
T: 'static + FnMut(ModifiersState, i32, PhysicalPosition<f64>, Force),
|
||||||
{
|
{
|
||||||
let window = canvas_common.window.clone();
|
let window = canvas_common.window.clone();
|
||||||
self.on_pointer_release = Some(canvas_common.add_user_event(
|
self.on_pointer_release = Some(canvas_common.add_transient_event(
|
||||||
"pointerup",
|
"pointerup",
|
||||||
move |event: PointerEvent| {
|
move |event: PointerEvent| {
|
||||||
let modifiers = event::mouse_modifiers(&event);
|
let modifiers = event::mouse_modifiers(&event);
|
||||||
|
@ -118,7 +118,7 @@ impl PointerHandler {
|
||||||
{
|
{
|
||||||
let window = canvas_common.window.clone();
|
let window = canvas_common.window.clone();
|
||||||
let canvas = canvas_common.raw.clone();
|
let canvas = canvas_common.raw.clone();
|
||||||
self.on_pointer_press = Some(canvas_common.add_user_event(
|
self.on_pointer_press = Some(canvas_common.add_transient_event(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
move |event: PointerEvent| {
|
move |event: PointerEvent| {
|
||||||
if prevent_default {
|
if prevent_default {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::window::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
|
||||||
use web_sys::{Document, HtmlCanvasElement};
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
use super::r#async::Dispatcher;
|
use super::r#async::Dispatcher;
|
||||||
use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};
|
use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};
|
||||||
|
@ -25,7 +25,6 @@ pub struct Window {
|
||||||
pub struct Inner {
|
pub struct Inner {
|
||||||
id: WindowId,
|
id: WindowId,
|
||||||
pub window: web_sys::Window,
|
pub window: web_sys::Window,
|
||||||
document: Document,
|
|
||||||
canvas: Rc<RefCell<backend::Canvas>>,
|
canvas: Rc<RefCell<backend::Canvas>>,
|
||||||
previous_pointer: RefCell<&'static str>,
|
previous_pointer: RefCell<&'static str>,
|
||||||
destroy_fn: Option<Box<dyn FnOnce()>>,
|
destroy_fn: Option<Box<dyn FnOnce()>>,
|
||||||
|
@ -57,7 +56,6 @@ impl Window {
|
||||||
let inner = Inner {
|
let inner = Inner {
|
||||||
id,
|
id,
|
||||||
window: window.clone(),
|
window: window.clone(),
|
||||||
document: document.clone(),
|
|
||||||
canvas,
|
canvas,
|
||||||
previous_pointer: RefCell::new("auto"),
|
previous_pointer: RefCell::new("auto"),
|
||||||
destroy_fn: Some(destroy_fn),
|
destroy_fn: Some(destroy_fn),
|
||||||
|
@ -282,10 +280,12 @@ impl Inner {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||||
|
let canvas = &self.canvas.borrow();
|
||||||
|
|
||||||
if fullscreen.is_some() {
|
if fullscreen.is_some() {
|
||||||
self.canvas.borrow().request_fullscreen();
|
canvas.request_fullscreen();
|
||||||
} else if self.canvas.borrow().is_fullscreen() {
|
} else {
|
||||||
backend::exit_fullscreen(&self.document);
|
canvas.exit_fullscreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue