2023-06-23 16:21:41 +02:00
|
|
|
mod animation_frame;
|
2019-06-25 03:15:34 +02:00
|
|
|
mod canvas;
|
2023-06-14 10:26:26 +02:00
|
|
|
pub mod event;
|
2020-09-21 06:42:07 +08:00
|
|
|
mod event_handle;
|
2023-07-11 00:34:02 +02:00
|
|
|
mod fullscreen;
|
2023-07-10 02:02:38 +02:00
|
|
|
mod intersection_handle;
|
2020-09-21 06:42:07 +08:00
|
|
|
mod media_query_handle;
|
2023-06-04 01:44:53 +02:00
|
|
|
mod pointer;
|
2023-06-14 09:43:53 +02:00
|
|
|
mod resize_scaling;
|
2024-11-21 17:37:03 +01:00
|
|
|
mod safe_area;
|
2023-08-25 21:40:21 +02:00
|
|
|
mod schedule;
|
2019-06-25 03:15:34 +02:00
|
|
|
|
2024-07-23 17:05:55 +02:00
|
|
|
use std::cell::OnceCell;
|
2024-07-05 16:07:01 +02:00
|
|
|
|
2025-05-17 04:26:09 +02:00
|
|
|
use dpi::{LogicalPosition, LogicalSize};
|
2024-07-05 16:07:01 +02:00
|
|
|
use js_sys::Array;
|
2020-09-21 06:42:07 +08:00
|
|
|
use wasm_bindgen::closure::Closure;
|
2024-07-05 16:07:01 +02:00
|
|
|
use wasm_bindgen::prelude::wasm_bindgen;
|
|
|
|
|
use wasm_bindgen::JsCast;
|
2024-07-23 20:33:10 +02:00
|
|
|
use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState};
|
2019-06-25 18:07:47 +02:00
|
|
|
|
2019-06-25 03:15:34 +02:00
|
|
|
pub use self::canvas::{Canvas, Style};
|
2023-06-14 10:26:26 +02:00
|
|
|
pub use self::event_handle::EventListenerHandle;
|
2023-06-14 09:43:53 +02:00
|
|
|
pub use self::resize_scaling::ResizeScaleHandle;
|
2024-11-21 17:37:03 +01:00
|
|
|
pub use self::safe_area::SafeAreaHandle;
|
2023-08-25 21:40:21 +02:00
|
|
|
pub use self::schedule::Schedule;
|
2024-07-07 18:38:50 +02:00
|
|
|
|
2019-06-25 03:15:34 +02:00
|
|
|
pub fn throw(msg: &str) {
|
|
|
|
|
wasm_bindgen::throw_str(msg);
|
|
|
|
|
}
|
2019-06-25 18:07:47 +02:00
|
|
|
|
2023-06-28 12:54:21 +02:00
|
|
|
pub struct PageTransitionEventHandle {
|
|
|
|
|
_show_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
|
|
|
|
_hide_listener: event_handle::EventListenerHandle<dyn FnMut(PageTransitionEvent)>,
|
2020-09-21 06:42:07 +08:00
|
|
|
}
|
|
|
|
|
|
2023-06-28 12:54:21 +02:00
|
|
|
pub fn on_page_transition(
|
2023-08-28 19:18:10 +02:00
|
|
|
window: web_sys::Window,
|
2023-06-28 12:54:21 +02:00
|
|
|
show_handler: impl FnMut(PageTransitionEvent) + 'static,
|
|
|
|
|
hide_handler: impl FnMut(PageTransitionEvent) + 'static,
|
|
|
|
|
) -> PageTransitionEventHandle {
|
|
|
|
|
let show_closure = Closure::new(show_handler);
|
|
|
|
|
let hide_closure = Closure::new(hide_handler);
|
2019-09-11 11:47:03 -04:00
|
|
|
|
2023-08-28 19:18:10 +02:00
|
|
|
let show_listener =
|
|
|
|
|
event_handle::EventListenerHandle::new(window.clone(), "pageshow", show_closure);
|
2023-06-28 12:54:21 +02:00
|
|
|
let hide_listener = event_handle::EventListenerHandle::new(window, "pagehide", hide_closure);
|
|
|
|
|
PageTransitionEventHandle { _show_listener: show_listener, _hide_listener: hide_listener }
|
2019-09-11 11:47:03 -04:00
|
|
|
}
|
|
|
|
|
|
2023-06-05 02:44:54 +02:00
|
|
|
pub fn scale_factor(window: &web_sys::Window) -> f64 {
|
2019-12-31 14:39:33 -08:00
|
|
|
window.device_pixel_ratio()
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 22:46:51 +01:00
|
|
|
fn fix_canvas_size(style: &Style, mut size: LogicalSize<f64>) -> LogicalSize<f64> {
|
|
|
|
|
if style.get("box-sizing") == "border-box" {
|
2023-07-11 13:14:40 +02:00
|
|
|
size.width += style_size_property(style, "border-left-width")
|
|
|
|
|
+ style_size_property(style, "border-right-width")
|
|
|
|
|
+ style_size_property(style, "padding-left")
|
|
|
|
|
+ style_size_property(style, "padding-right");
|
|
|
|
|
size.height += style_size_property(style, "border-top-width")
|
|
|
|
|
+ style_size_property(style, "border-bottom-width")
|
|
|
|
|
+ style_size_property(style, "padding-top")
|
|
|
|
|
+ style_size_property(style, "padding-bottom");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 09:43:53 +02:00
|
|
|
pub fn set_canvas_size(
|
2023-07-11 00:11:06 +02:00
|
|
|
document: &Document,
|
2023-06-14 09:43:53 +02:00
|
|
|
raw: &HtmlCanvasElement,
|
2023-11-10 22:46:51 +01:00
|
|
|
style: &Style,
|
2023-07-11 13:14:40 +02:00
|
|
|
new_size: LogicalSize<f64>,
|
2023-06-14 09:43:53 +02:00
|
|
|
) {
|
2023-11-10 22:46:51 +01:00
|
|
|
if !document.contains(Some(raw)) || style.get("display") == "none" {
|
2023-07-10 23:55:43 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-11 13:14:40 +02:00
|
|
|
let new_size = fix_canvas_size(style, new_size);
|
|
|
|
|
|
2023-11-10 22:46:51 +01:00
|
|
|
style.set("width", &format!("{}px", new_size.width));
|
|
|
|
|
style.set("height", &format!("{}px", new_size.height));
|
2023-07-11 13:14:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_canvas_min_size(
|
|
|
|
|
document: &Document,
|
|
|
|
|
raw: &HtmlCanvasElement,
|
2023-11-10 22:46:51 +01:00
|
|
|
style: &Style,
|
2023-07-11 13:14:40 +02:00
|
|
|
dimensions: Option<LogicalSize<f64>>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(dimensions) = dimensions {
|
2023-11-10 22:46:51 +01:00
|
|
|
if !document.contains(Some(raw)) || style.get("display") == "none" {
|
2023-07-11 13:14:40 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_size = fix_canvas_size(style, dimensions);
|
|
|
|
|
|
2023-11-10 22:46:51 +01:00
|
|
|
style.set("min-width", &format!("{}px", new_size.width));
|
|
|
|
|
style.set("min-height", &format!("{}px", new_size.height));
|
2023-07-11 13:14:40 +02:00
|
|
|
} else {
|
2023-11-10 22:46:51 +01:00
|
|
|
style.remove("min-width");
|
|
|
|
|
style.remove("min-height");
|
2023-06-14 09:43:53 +02:00
|
|
|
}
|
2023-07-11 13:14:40 +02:00
|
|
|
}
|
2023-06-14 09:43:53 +02:00
|
|
|
|
2023-07-11 13:14:40 +02:00
|
|
|
pub fn set_canvas_max_size(
|
|
|
|
|
document: &Document,
|
|
|
|
|
raw: &HtmlCanvasElement,
|
2023-11-10 22:46:51 +01:00
|
|
|
style: &Style,
|
2023-07-11 13:14:40 +02:00
|
|
|
dimensions: Option<LogicalSize<f64>>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(dimensions) = dimensions {
|
2023-11-10 22:46:51 +01:00
|
|
|
if !document.contains(Some(raw)) || style.get("display") == "none" {
|
2023-07-11 13:14:40 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_size = fix_canvas_size(style, dimensions);
|
|
|
|
|
|
2023-11-10 22:46:51 +01:00
|
|
|
style.set("max-width", &format!("{}px", new_size.width));
|
|
|
|
|
style.set("max-height", &format!("{}px", new_size.height));
|
2023-07-11 13:14:40 +02:00
|
|
|
} else {
|
2023-11-10 22:46:51 +01:00
|
|
|
style.remove("max-width");
|
|
|
|
|
style.remove("max-height");
|
2023-06-14 09:43:53 +02:00
|
|
|
}
|
2023-07-11 13:14:40 +02:00
|
|
|
}
|
2023-06-14 09:43:53 +02:00
|
|
|
|
2023-07-11 13:14:40 +02:00
|
|
|
pub fn set_canvas_position(
|
|
|
|
|
document: &Document,
|
|
|
|
|
raw: &HtmlCanvasElement,
|
2023-11-10 22:46:51 +01:00
|
|
|
style: &Style,
|
2023-07-11 13:14:40 +02:00
|
|
|
mut position: LogicalPosition<f64>,
|
|
|
|
|
) {
|
2023-11-10 22:46:51 +01:00
|
|
|
if document.contains(Some(raw)) && style.get("display") != "none" {
|
2023-07-11 13:14:40 +02:00
|
|
|
position.x -= style_size_property(style, "margin-left")
|
|
|
|
|
+ style_size_property(style, "border-left-width")
|
|
|
|
|
+ style_size_property(style, "padding-left");
|
|
|
|
|
position.y -= style_size_property(style, "margin-top")
|
|
|
|
|
+ style_size_property(style, "border-top-width")
|
|
|
|
|
+ style_size_property(style, "padding-top");
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 22:46:51 +01:00
|
|
|
style.set("position", "fixed");
|
|
|
|
|
style.set("left", &format!("{}px", position.x));
|
|
|
|
|
style.set("top", &format!("{}px", position.y));
|
2023-06-14 09:43:53 +02:00
|
|
|
}
|
2023-06-08 08:40:26 +02:00
|
|
|
|
2023-06-14 09:43:53 +02:00
|
|
|
/// This function will panic if the element is not inserted in the DOM
|
|
|
|
|
/// or is not a CSS property that represents a size in pixel.
|
2023-11-10 22:46:51 +01:00
|
|
|
pub fn style_size_property(style: &Style, property: &str) -> f64 {
|
|
|
|
|
let prop = style.get(property);
|
2023-06-14 09:43:53 +02:00
|
|
|
prop.strip_suffix("px")
|
|
|
|
|
.expect("Element was not inserted into the DOM or is not a size in pixel")
|
|
|
|
|
.parse()
|
|
|
|
|
.expect("CSS property is not a size in pixel")
|
2020-08-17 16:48:29 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-05 02:44:54 +02:00
|
|
|
pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
|
|
|
|
|
window.match_media("(prefers-color-scheme: dark)").ok().flatten().map(|media| media.matches())
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-11 00:11:06 +02:00
|
|
|
pub fn is_visible(document: &Document) -> bool {
|
2023-07-10 02:02:38 +02:00
|
|
|
document.visibility_state() == VisibilityState::Visible
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-15 21:20:14 -05:00
|
|
|
pub type RawCanvasType = HtmlCanvasElement;
|
2024-07-05 16:07:01 +02:00
|
|
|
|
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
|
pub enum Engine {
|
|
|
|
|
Chromium,
|
|
|
|
|
Gecko,
|
|
|
|
|
WebKit,
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 17:05:55 +02:00
|
|
|
struct UserAgentData {
|
|
|
|
|
engine: Option<Engine>,
|
|
|
|
|
chrome_linux: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_local! {
|
|
|
|
|
static USER_AGENT_DATA: OnceCell<UserAgentData> = const { OnceCell::new() };
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 20:33:10 +02:00
|
|
|
pub fn chrome_linux(navigator: &Navigator) -> bool {
|
|
|
|
|
USER_AGENT_DATA.with(|data| data.get_or_init(|| user_agent(navigator)).chrome_linux)
|
2024-07-23 17:05:55 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 20:33:10 +02:00
|
|
|
pub fn engine(navigator: &Navigator) -> Option<Engine> {
|
|
|
|
|
USER_AGENT_DATA.with(|data| data.get_or_init(|| user_agent(navigator)).engine)
|
2024-07-23 17:05:55 +02:00
|
|
|
}
|
2024-07-05 16:07:01 +02:00
|
|
|
|
2024-07-23 20:33:10 +02:00
|
|
|
fn user_agent(navigator: &Navigator) -> UserAgentData {
|
2024-07-05 16:07:01 +02:00
|
|
|
#[wasm_bindgen]
|
|
|
|
|
extern "C" {
|
|
|
|
|
#[wasm_bindgen(extends = Navigator)]
|
|
|
|
|
type NavigatorExt;
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(method, getter, js_name = userAgentData)]
|
|
|
|
|
fn user_agent_data(this: &NavigatorExt) -> Option<NavigatorUaData>;
|
|
|
|
|
|
|
|
|
|
type NavigatorUaData;
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(method, getter)]
|
|
|
|
|
fn brands(this: &NavigatorUaData) -> Array;
|
|
|
|
|
|
2024-07-23 17:05:55 +02:00
|
|
|
#[wasm_bindgen(method, getter)]
|
|
|
|
|
fn platform(this: &NavigatorUaData) -> String;
|
|
|
|
|
|
2024-07-05 16:07:01 +02:00
|
|
|
type NavigatorUaBrandVersion;
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(method, getter)]
|
|
|
|
|
fn brand(this: &NavigatorUaBrandVersion) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 20:33:10 +02:00
|
|
|
let navigator: &NavigatorExt = navigator.unchecked_ref();
|
2024-07-05 16:07:01 +02:00
|
|
|
|
2024-07-23 17:05:55 +02:00
|
|
|
if let Some(data) = navigator.user_agent_data() {
|
|
|
|
|
let engine = 'engine: {
|
2024-07-05 16:07:01 +02:00
|
|
|
for brand in data
|
|
|
|
|
.brands()
|
|
|
|
|
.iter()
|
|
|
|
|
.map(NavigatorUaBrandVersion::unchecked_from_js)
|
|
|
|
|
.map(|brand| brand.brand())
|
|
|
|
|
{
|
|
|
|
|
match brand.as_str() {
|
2024-07-23 17:05:55 +02:00
|
|
|
"Chromium" => break 'engine Some(Engine::Chromium),
|
2024-07-05 16:07:01 +02:00
|
|
|
// TODO: verify when Firefox actually implements it.
|
2024-07-23 17:05:55 +02:00
|
|
|
"Gecko" => break 'engine Some(Engine::Gecko),
|
2024-07-05 16:07:01 +02:00
|
|
|
// TODO: verify when Safari actually implements it.
|
2024-07-23 17:05:55 +02:00
|
|
|
"WebKit" => break 'engine Some(Engine::WebKit),
|
2024-07-05 16:07:01 +02:00
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
2024-07-23 17:05:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let chrome_linux = matches!(engine, Some(Engine::Chromium))
|
|
|
|
|
.then(|| data.platform() == "Linux")
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
UserAgentData { engine, chrome_linux }
|
|
|
|
|
} else {
|
|
|
|
|
let engine = 'engine: {
|
|
|
|
|
let Ok(data) = navigator.user_agent() else {
|
|
|
|
|
break 'engine None;
|
|
|
|
|
};
|
2024-07-05 16:07:01 +02:00
|
|
|
|
|
|
|
|
if data.contains("Chrome/") {
|
|
|
|
|
Some(Engine::Chromium)
|
|
|
|
|
} else if data.contains("Gecko/") {
|
|
|
|
|
Some(Engine::Gecko)
|
|
|
|
|
} else if data.contains("AppleWebKit/") {
|
|
|
|
|
Some(Engine::WebKit)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2024-07-23 17:05:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UserAgentData { engine, chrome_linux: false }
|
|
|
|
|
}
|
2024-07-05 16:07:01 +02:00
|
|
|
}
|