2020-09-21 06:42:07 +08:00
|
|
|
use super::event_handle::EventListenerHandle;
|
|
|
|
|
use super::media_query_handle::MediaQueryListHandle;
|
2023-06-04 01:44:53 +02:00
|
|
|
use super::pointer::PointerHandler;
|
2023-06-02 11:37:23 +02:00
|
|
|
use super::{event, ButtonsState};
|
2019-12-31 14:39:33 -08:00
|
|
|
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
2019-06-25 03:15:34 +02:00
|
|
|
use crate::error::OsError as RootOE;
|
2023-05-28 20:02:59 +02:00
|
|
|
use crate::event::{Force, MouseButton, MouseScrollDelta};
|
|
|
|
|
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState};
|
2020-01-15 21:20:14 -05:00
|
|
|
use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2019-10-11 11:45:07 -04:00
|
|
|
use std::cell::RefCell;
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
2023-03-26 17:19:26 +02:00
|
|
|
use js_sys::Promise;
|
2023-05-28 20:02:59 +02:00
|
|
|
use smol_str::SmolStr;
|
2023-03-26 17:19:26 +02:00
|
|
|
use wasm_bindgen::prelude::wasm_bindgen;
|
|
|
|
|
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
|
|
|
|
use wasm_bindgen_futures::JsFuture;
|
2020-02-17 20:25:27 +01:00
|
|
|
use web_sys::{
|
2023-06-04 01:44:53 +02:00
|
|
|
Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, WheelEvent,
|
2020-02-17 20:25:27 +01:00
|
|
|
};
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2021-03-30 21:27:32 +02:00
|
|
|
#[allow(dead_code)]
|
2019-06-25 03:15:34 +02:00
|
|
|
pub struct Canvas {
|
2020-08-29 21:34:33 +08:00
|
|
|
common: Common,
|
2022-09-04 13:45:30 +10:00
|
|
|
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
|
|
|
|
on_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
2020-09-21 06:42:07 +08:00
|
|
|
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
|
|
|
|
on_blur: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
|
|
|
|
on_keyboard_release: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
|
|
|
|
on_keyboard_press: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
|
|
|
|
on_mouse_wheel: Option<EventListenerHandle<dyn FnMut(WheelEvent)>>,
|
|
|
|
|
on_fullscreen_change: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
|
|
|
|
on_dark_mode: Option<MediaQueryListHandle>,
|
2023-06-04 01:44:53 +02:00
|
|
|
pointer_handler: PointerHandler,
|
2020-08-29 21:34:33 +08:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 01:44:53 +02:00
|
|
|
pub struct Common {
|
2023-06-05 02:44:54 +02:00
|
|
|
pub window: web_sys::Window,
|
2020-08-29 21:34:33 +08:00
|
|
|
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
|
2023-06-04 01:44:53 +02:00
|
|
|
pub raw: HtmlCanvasElement,
|
2020-08-29 21:34:33 +08:00
|
|
|
wants_fullscreen: Rc<RefCell<bool>>,
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Canvas {
|
2023-06-05 02:44:54 +02:00
|
|
|
pub fn create(
|
|
|
|
|
window: web_sys::Window,
|
|
|
|
|
attr: PlatformSpecificWindowBuilderAttributes,
|
|
|
|
|
) -> Result<Self, RootOE> {
|
2020-01-15 21:20:14 -05:00
|
|
|
let canvas = match attr.canvas {
|
|
|
|
|
Some(canvas) => canvas,
|
|
|
|
|
None => {
|
|
|
|
|
let document = window
|
|
|
|
|
.document()
|
2022-06-10 13:43:33 +03:00
|
|
|
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2020-01-15 21:20:14 -05:00
|
|
|
document
|
|
|
|
|
.create_element("canvas")
|
|
|
|
|
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?
|
|
|
|
|
.unchecked_into()
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2019-08-08 23:51:41 +02:00
|
|
|
// A tabindex is needed in order to capture local keyboard events.
|
|
|
|
|
// A "0" value means that the element should be focusable in
|
|
|
|
|
// sequential keyboard navigation, but its order is defined by the
|
|
|
|
|
// document's source order.
|
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
|
2022-07-14 13:22:31 -02:30
|
|
|
if attr.focusable {
|
|
|
|
|
canvas
|
|
|
|
|
.set_attribute("tabindex", "0")
|
|
|
|
|
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
|
|
|
|
|
}
|
2019-06-25 21:01:13 +02:00
|
|
|
|
2019-06-25 18:07:47 +02:00
|
|
|
Ok(Canvas {
|
2020-08-29 21:34:33 +08:00
|
|
|
common: Common {
|
2023-06-05 02:44:54 +02:00
|
|
|
window,
|
2020-08-29 21:34:33 +08:00
|
|
|
raw: canvas,
|
|
|
|
|
wants_fullscreen: Rc::new(RefCell::new(false)),
|
|
|
|
|
},
|
2022-09-04 13:45:30 +10:00
|
|
|
on_touch_start: None,
|
|
|
|
|
on_touch_end: None,
|
2019-06-25 21:01:13 +02:00
|
|
|
on_blur: None,
|
|
|
|
|
on_focus: None,
|
2019-06-25 21:36:24 +02:00
|
|
|
on_keyboard_release: None,
|
|
|
|
|
on_keyboard_press: None,
|
|
|
|
|
on_mouse_wheel: None,
|
2019-10-11 11:45:07 -04:00
|
|
|
on_fullscreen_change: None,
|
2020-02-17 20:25:27 +01:00
|
|
|
on_dark_mode: None,
|
2023-06-04 01:44:53 +02:00
|
|
|
pointer_handler: PointerHandler::new(),
|
2019-06-25 18:07:47 +02:00
|
|
|
})
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-13 09:43:14 +03:00
|
|
|
pub fn set_cursor_lock(&self, lock: bool) -> Result<(), RootOE> {
|
|
|
|
|
if lock {
|
2022-01-05 11:13:46 +01:00
|
|
|
self.raw().request_pointer_lock();
|
|
|
|
|
} else {
|
2023-06-05 02:44:54 +02:00
|
|
|
let document = self
|
|
|
|
|
.common
|
|
|
|
|
.window
|
2022-01-05 11:13:46 +01:00
|
|
|
.document()
|
2022-06-10 13:43:33 +03:00
|
|
|
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
|
2022-01-05 11:13:46 +01:00
|
|
|
document.exit_pointer_lock();
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-25 03:15:34 +02:00
|
|
|
pub fn set_attribute(&self, attribute: &str, value: &str) {
|
2020-08-29 21:34:33 +08:00
|
|
|
self.common
|
|
|
|
|
.raw
|
2019-06-25 03:15:34 +02:00
|
|
|
.set_attribute(attribute, value)
|
2023-01-27 07:18:58 +03:00
|
|
|
.unwrap_or_else(|err| panic!("error: {err:?}\nSet attribute: {attribute}"))
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-31 14:39:33 -08:00
|
|
|
pub fn position(&self) -> LogicalPosition<f64> {
|
2020-08-29 21:34:33 +08:00
|
|
|
let bounds = self.common.raw.get_bounding_client_rect();
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2019-12-31 14:39:33 -08:00
|
|
|
LogicalPosition {
|
|
|
|
|
x: bounds.x(),
|
|
|
|
|
y: bounds.y(),
|
|
|
|
|
}
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-31 14:39:33 -08:00
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
|
|
|
|
PhysicalSize {
|
2020-08-29 21:34:33 +08:00
|
|
|
width: self.common.raw.width(),
|
|
|
|
|
height: self.common.raw.height(),
|
2019-12-31 14:39:33 -08:00
|
|
|
}
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-25 18:07:47 +02:00
|
|
|
pub fn raw(&self) -> &HtmlCanvasElement {
|
2020-08-29 21:34:33 +08:00
|
|
|
&self.common.raw
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-04 13:45:30 +10:00
|
|
|
pub fn on_touch_start(&mut self, prevent_default: bool) {
|
|
|
|
|
self.on_touch_start = Some(self.common.add_event("touchstart", move |event: Event| {
|
|
|
|
|
if prevent_default {
|
|
|
|
|
event.prevent_default();
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-25 21:01:13 +02:00
|
|
|
pub fn on_blur<F>(&mut self, mut handler: F)
|
|
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(),
|
|
|
|
|
{
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_blur = Some(self.common.add_event("blur", move |_: FocusEvent| {
|
2019-06-25 21:01:13 +02:00
|
|
|
handler();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn on_focus<F>(&mut self, mut handler: F)
|
|
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(),
|
|
|
|
|
{
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_focus = Some(self.common.add_event("focus", move |_: FocusEvent| {
|
2019-06-25 21:01:13 +02:00
|
|
|
handler();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-14 13:22:31 -02:30
|
|
|
pub fn on_keyboard_release<F>(&mut self, mut handler: F, prevent_default: bool)
|
2019-06-25 21:01:13 +02:00
|
|
|
where
|
2023-05-28 20:02:59 +02:00
|
|
|
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
2019-06-25 21:01:13 +02:00
|
|
|
{
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_keyboard_release = Some(self.common.add_user_event(
|
|
|
|
|
"keyup",
|
|
|
|
|
move |event: KeyboardEvent| {
|
2022-07-14 13:22:31 -02:30
|
|
|
if prevent_default {
|
|
|
|
|
event.prevent_default();
|
|
|
|
|
}
|
2023-05-28 20:02:59 +02:00
|
|
|
let key = event::key(&event);
|
|
|
|
|
let modifiers = event::keyboard_modifiers(&event);
|
2019-10-11 11:45:07 -04:00
|
|
|
handler(
|
2023-05-28 20:02:59 +02:00
|
|
|
event::key_code(&event),
|
|
|
|
|
key,
|
|
|
|
|
event::key_text(&event),
|
|
|
|
|
event::key_location(&event),
|
|
|
|
|
event.repeat(),
|
|
|
|
|
modifiers,
|
2019-10-11 11:45:07 -04:00
|
|
|
);
|
2020-08-29 21:34:33 +08:00
|
|
|
},
|
|
|
|
|
));
|
2019-06-25 21:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-14 13:22:31 -02:30
|
|
|
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool)
|
2019-06-25 21:01:13 +02:00
|
|
|
where
|
2023-05-28 20:02:59 +02:00
|
|
|
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
2019-06-25 21:01:13 +02:00
|
|
|
{
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_keyboard_press = Some(self.common.add_user_event(
|
|
|
|
|
"keydown",
|
|
|
|
|
move |event: KeyboardEvent| {
|
2022-07-14 13:22:31 -02:30
|
|
|
if prevent_default {
|
2023-05-28 20:02:59 +02:00
|
|
|
event.prevent_default();
|
2020-10-29 22:13:21 +01:00
|
|
|
}
|
2023-05-28 20:02:59 +02:00
|
|
|
let key = event::key(&event);
|
|
|
|
|
let modifiers = event::keyboard_modifiers(&event);
|
2019-10-11 11:45:07 -04:00
|
|
|
handler(
|
2023-05-28 20:02:59 +02:00
|
|
|
event::key_code(&event),
|
|
|
|
|
key,
|
|
|
|
|
event::key_text(&event),
|
|
|
|
|
event::key_location(&event),
|
|
|
|
|
event.repeat(),
|
|
|
|
|
modifiers,
|
2019-10-11 11:45:07 -04:00
|
|
|
);
|
2020-08-29 21:34:33 +08:00
|
|
|
},
|
|
|
|
|
));
|
2019-06-25 18:39:41 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 02:16:55 +02:00
|
|
|
pub fn on_cursor_leave<MOD, M>(&mut self, modifier_handler: MOD, mouse_handler: M)
|
2019-06-25 18:07:47 +02:00
|
|
|
where
|
2023-06-04 02:16:55 +02:00
|
|
|
MOD: 'static + FnMut(ModifiersState),
|
|
|
|
|
M: 'static + FnMut(i32),
|
2019-06-25 18:07:47 +02:00
|
|
|
{
|
2023-06-04 02:16:55 +02:00
|
|
|
self.pointer_handler
|
|
|
|
|
.on_cursor_leave(&self.common, modifier_handler, mouse_handler)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 02:16:55 +02:00
|
|
|
pub fn on_cursor_enter<MOD, M>(&mut self, modifier_handler: MOD, mouse_handler: M)
|
2019-06-25 18:07:47 +02:00
|
|
|
where
|
2023-06-04 02:16:55 +02:00
|
|
|
MOD: 'static + FnMut(ModifiersState),
|
|
|
|
|
M: 'static + FnMut(i32),
|
2019-06-25 18:07:47 +02:00
|
|
|
{
|
2023-06-04 02:16:55 +02:00
|
|
|
self.pointer_handler
|
|
|
|
|
.on_cursor_enter(&self.common, modifier_handler, mouse_handler)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 02:16:55 +02:00
|
|
|
pub fn on_mouse_release<MOD, M, T>(
|
|
|
|
|
&mut self,
|
|
|
|
|
modifier_handler: MOD,
|
|
|
|
|
mouse_handler: M,
|
|
|
|
|
touch_handler: T,
|
|
|
|
|
) where
|
|
|
|
|
MOD: 'static + FnMut(ModifiersState),
|
|
|
|
|
M: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton),
|
2022-12-23 14:55:22 +09:00
|
|
|
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
2019-06-25 18:07:47 +02:00
|
|
|
{
|
2023-06-04 02:16:55 +02:00
|
|
|
self.pointer_handler.on_mouse_release(
|
|
|
|
|
&self.common,
|
|
|
|
|
modifier_handler,
|
|
|
|
|
mouse_handler,
|
|
|
|
|
touch_handler,
|
|
|
|
|
)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 02:16:55 +02:00
|
|
|
pub fn on_mouse_press<MOD, M, T>(
|
2023-06-04 01:50:30 +02:00
|
|
|
&mut self,
|
2023-06-04 02:16:55 +02:00
|
|
|
modifier_handler: MOD,
|
2023-06-04 01:50:30 +02:00
|
|
|
mouse_handler: M,
|
|
|
|
|
touch_handler: T,
|
|
|
|
|
prevent_default: bool,
|
|
|
|
|
) where
|
2023-06-04 02:16:55 +02:00
|
|
|
MOD: 'static + FnMut(ModifiersState),
|
|
|
|
|
M: 'static + FnMut(i32, PhysicalPosition<f64>, MouseButton),
|
2022-12-23 14:55:22 +09:00
|
|
|
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
2019-06-25 18:07:47 +02:00
|
|
|
{
|
2023-06-04 01:50:30 +02:00
|
|
|
self.pointer_handler.on_mouse_press(
|
|
|
|
|
&self.common,
|
2023-06-04 02:16:55 +02:00
|
|
|
modifier_handler,
|
2023-06-04 01:50:30 +02:00
|
|
|
mouse_handler,
|
|
|
|
|
touch_handler,
|
|
|
|
|
prevent_default,
|
|
|
|
|
)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-04 01:08:03 +02:00
|
|
|
pub fn on_cursor_move<MOD, M, T, B>(
|
2022-12-23 14:55:22 +09:00
|
|
|
&mut self,
|
2023-06-04 01:08:03 +02:00
|
|
|
modifier_handler: MOD,
|
2022-12-23 14:55:22 +09:00
|
|
|
mouse_handler: M,
|
|
|
|
|
touch_handler: T,
|
2023-06-04 01:08:03 +02:00
|
|
|
button_handler: B,
|
2022-12-23 14:55:22 +09:00
|
|
|
prevent_default: bool,
|
|
|
|
|
) where
|
2023-06-04 01:08:03 +02:00
|
|
|
MOD: 'static + FnMut(ModifiersState),
|
|
|
|
|
M: 'static + FnMut(i32, PhysicalPosition<f64>, PhysicalPosition<f64>),
|
2022-12-23 14:55:22 +09:00
|
|
|
T: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
2023-06-04 01:08:03 +02:00
|
|
|
B: 'static + FnMut(i32, PhysicalPosition<f64>, ButtonsState, MouseButton),
|
2019-06-25 18:07:47 +02:00
|
|
|
{
|
2023-06-04 01:44:53 +02:00
|
|
|
self.pointer_handler.on_cursor_move(
|
|
|
|
|
&self.common,
|
|
|
|
|
modifier_handler,
|
|
|
|
|
mouse_handler,
|
|
|
|
|
touch_handler,
|
|
|
|
|
button_handler,
|
|
|
|
|
prevent_default,
|
|
|
|
|
)
|
2022-12-23 14:55:22 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn on_touch_cancel<F>(&mut self, handler: F)
|
|
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(i32, PhysicalPosition<f64>, Force),
|
|
|
|
|
{
|
2023-06-04 01:44:53 +02:00
|
|
|
self.pointer_handler.on_touch_cancel(&self.common, handler)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-14 13:22:31 -02:30
|
|
|
pub fn on_mouse_wheel<F>(&mut self, mut handler: F, prevent_default: bool)
|
2019-06-25 18:07:47 +02:00
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState),
|
|
|
|
|
{
|
2023-06-05 02:44:54 +02:00
|
|
|
let window = self.common.window.clone();
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| {
|
2022-07-14 13:22:31 -02:30
|
|
|
if prevent_default {
|
|
|
|
|
event.prevent_default();
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-05 02:44:54 +02:00
|
|
|
if let Some(delta) = event::mouse_scroll_delta(&window, &event) {
|
2023-05-28 20:02:59 +02:00
|
|
|
let modifiers = event::mouse_modifiers(&event);
|
|
|
|
|
handler(0, delta, modifiers);
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 11:45:07 -04:00
|
|
|
pub fn on_fullscreen_change<F>(&mut self, mut handler: F)
|
|
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(),
|
|
|
|
|
{
|
2020-08-29 21:34:33 +08:00
|
|
|
self.on_fullscreen_change = Some(
|
|
|
|
|
self.common
|
|
|
|
|
.add_event("fullscreenchange", move |_: Event| handler()),
|
|
|
|
|
);
|
2019-10-11 11:45:07 -04:00
|
|
|
}
|
|
|
|
|
|
2020-02-17 20:25:27 +01:00
|
|
|
pub fn on_dark_mode<F>(&mut self, mut handler: F)
|
|
|
|
|
where
|
|
|
|
|
F: 'static + FnMut(bool),
|
|
|
|
|
{
|
2020-09-21 06:42:07 +08:00
|
|
|
let closure =
|
|
|
|
|
Closure::wrap(
|
|
|
|
|
Box::new(move |event: MediaQueryListEvent| handler(event.matches()))
|
|
|
|
|
as Box<dyn FnMut(_)>,
|
|
|
|
|
);
|
2023-06-05 02:44:54 +02:00
|
|
|
self.on_dark_mode =
|
|
|
|
|
MediaQueryListHandle::new(&self.common.window, "(prefers-color-scheme: dark)", closure);
|
2020-02-17 20:25:27 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-29 21:34:33 +08:00
|
|
|
pub fn request_fullscreen(&self) {
|
|
|
|
|
self.common.request_fullscreen()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_fullscreen(&self) -> bool {
|
|
|
|
|
self.common.is_fullscreen()
|
|
|
|
|
}
|
2020-09-21 06:42:07 +08:00
|
|
|
|
|
|
|
|
pub fn remove_listeners(&mut self) {
|
|
|
|
|
self.on_focus = None;
|
|
|
|
|
self.on_blur = None;
|
|
|
|
|
self.on_keyboard_release = None;
|
|
|
|
|
self.on_keyboard_press = None;
|
|
|
|
|
self.on_mouse_wheel = None;
|
|
|
|
|
self.on_fullscreen_change = None;
|
|
|
|
|
self.on_dark_mode = None;
|
2023-06-04 01:44:53 +02:00
|
|
|
self.pointer_handler.remove_listeners()
|
2020-09-21 06:42:07 +08:00
|
|
|
}
|
2020-08-29 21:34:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Common {
|
2023-06-04 01:44:53 +02:00
|
|
|
pub fn add_event<E, F>(
|
2020-09-21 06:42:07 +08:00
|
|
|
&self,
|
|
|
|
|
event_name: &'static str,
|
|
|
|
|
mut handler: F,
|
|
|
|
|
) -> EventListenerHandle<dyn FnMut(E)>
|
2019-06-25 18:07:47 +02:00
|
|
|
where
|
|
|
|
|
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
|
|
|
|
|
F: 'static + FnMut(E),
|
|
|
|
|
{
|
|
|
|
|
let closure = Closure::wrap(Box::new(move |event: E| {
|
|
|
|
|
{
|
|
|
|
|
let event_ref = event.as_ref();
|
|
|
|
|
event_ref.stop_propagation();
|
|
|
|
|
event_ref.cancel_bubble();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handler(event);
|
|
|
|
|
}) as Box<dyn FnMut(E)>);
|
|
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
EventListenerHandle::new(&self.raw, event_name, closure)
|
2019-06-25 18:07:47 +02:00
|
|
|
}
|
2019-10-11 11:45:07 -04:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// press) and is the only time things like a fullscreen request may be successfully completed.)
|
2023-06-04 01:44:53 +02:00
|
|
|
pub fn add_user_event<E, F>(
|
2020-09-21 06:42:07 +08:00
|
|
|
&self,
|
|
|
|
|
event_name: &'static str,
|
|
|
|
|
mut handler: F,
|
|
|
|
|
) -> EventListenerHandle<dyn FnMut(E)>
|
2019-10-11 11:45:07 -04:00
|
|
|
where
|
|
|
|
|
E: 'static + AsRef<web_sys::Event> + wasm_bindgen::convert::FromWasmAbi,
|
|
|
|
|
F: 'static + FnMut(E),
|
|
|
|
|
{
|
|
|
|
|
let wants_fullscreen = self.wants_fullscreen.clone();
|
|
|
|
|
let canvas = self.raw.clone();
|
|
|
|
|
|
|
|
|
|
self.add_event(event_name, move |event: E| {
|
|
|
|
|
handler(event);
|
|
|
|
|
|
|
|
|
|
if *wants_fullscreen.borrow() {
|
|
|
|
|
canvas
|
|
|
|
|
.request_fullscreen()
|
|
|
|
|
.expect("Failed to enter fullscreen");
|
|
|
|
|
*wants_fullscreen.borrow_mut() = false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn request_fullscreen(&self) {
|
2023-03-26 17:19:26 +02:00
|
|
|
#[wasm_bindgen]
|
|
|
|
|
extern "C" {
|
|
|
|
|
type ElementExt;
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(catch, method, js_name = requestFullscreen)]
|
|
|
|
|
fn request_fullscreen(this: &ElementExt) -> Result<JsValue, JsValue>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let raw: &ElementExt = self.raw.unchecked_ref();
|
|
|
|
|
|
|
|
|
|
// This should return a `Promise`, but Safari v<16.4 is not up-to-date with the spec.
|
|
|
|
|
match raw.request_fullscreen() {
|
|
|
|
|
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,
|
|
|
|
|
}
|
2019-10-11 11:45:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_fullscreen(&self) -> bool {
|
2023-06-05 02:44:54 +02:00
|
|
|
super::is_fullscreen(&self.window, &self.raw)
|
2019-10-11 11:45:07 -04:00
|
|
|
}
|
2019-06-25 03:15:34 +02:00
|
|
|
}
|