On Web, cache commonly used values (#2947)

This commit is contained in:
daxpedda 2023-07-11 00:11:06 +02:00 committed by GitHub
parent c4d70d75c1
commit af26f01b95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 126 deletions

View file

@ -21,7 +21,9 @@ use smol_str::SmolStr;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent};
use web_sys::{
CssStyleDeclaration, Document, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, WheelEvent,
};
#[allow(dead_code)]
pub struct Canvas {
@ -44,8 +46,10 @@ pub struct Canvas {
pub struct Common {
pub window: web_sys::Window,
pub document: Document,
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
pub raw: HtmlCanvasElement,
style: CssStyleDeclaration,
old_size: Rc<Cell<PhysicalSize<u32>>>,
current_size: Rc<Cell<PhysicalSize<u32>>>,
wants_fullscreen: Rc<RefCell<bool>>,
@ -55,21 +59,16 @@ impl Canvas {
pub fn create(
id: WindowId,
window: web_sys::Window,
document: Document,
attr: &WindowAttributes,
platform_attr: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOE> {
let canvas = match platform_attr.canvas {
Some(canvas) => canvas,
None => {
let document = window
.document()
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
document
.create_element("canvas")
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?
.unchecked_into()
}
None => document
.create_element("canvas")
.map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?
.unchecked_into(),
};
// A tabindex is needed in order to capture local keyboard events.
@ -83,15 +82,24 @@ impl Canvas {
.map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?;
}
#[allow(clippy::disallowed_methods)]
let style = window
.get_computed_style(&canvas)
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");
if let Some(size) = attr.inner_size {
let size = size.to_logical(super::scale_factor(&window));
super::set_canvas_size(&window, &canvas, size);
super::set_canvas_size(&document, &canvas, &style, size);
}
Ok(Canvas {
common: Common {
window,
document,
raw: canvas,
style,
old_size: Rc::default(),
current_size: Rc::default(),
wants_fullscreen: Rc::new(RefCell::new(false)),
@ -117,12 +125,7 @@ impl Canvas {
if lock {
self.raw().request_pointer_lock();
} else {
let document = self
.common
.window
.document()
.ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?;
document.exit_pointer_lock();
self.common.document.exit_pointer_lock();
}
Ok(())
}
@ -141,50 +144,58 @@ impl Canvas {
y: bounds.y(),
};
let document = self.window().document().expect("Failed to obtain document");
if document.contains(Some(self.raw())) {
let style = self
.window()
.get_computed_style(self.raw())
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");
if style.get_property_value("display").unwrap() != "none" {
position.x += super::style_size_property(&style, "border-left-width")
+ super::style_size_property(&style, "padding-left");
position.y += super::style_size_property(&style, "border-top-width")
+ super::style_size_property(&style, "padding-top");
}
if self.document().contains(Some(self.raw()))
&& self.style().get_property_value("display").unwrap() != "none"
{
position.x += super::style_size_property(self.style(), "border-left-width")
+ super::style_size_property(self.style(), "padding-left");
position.y += super::style_size_property(self.style(), "border-top-width")
+ super::style_size_property(self.style(), "padding-top");
}
position
}
#[inline]
pub fn old_size(&self) -> PhysicalSize<u32> {
self.common.old_size.get()
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.common.current_size.get()
}
#[inline]
pub fn set_old_size(&self, size: PhysicalSize<u32>) {
self.common.old_size.set(size)
}
#[inline]
pub fn set_current_size(&self, size: PhysicalSize<u32>) {
self.common.current_size.set(size)
}
#[inline]
pub fn window(&self) -> &web_sys::Window {
&self.common.window
}
#[inline]
pub fn document(&self) -> &Document {
&self.common.document
}
#[inline]
pub fn raw(&self) -> &HtmlCanvasElement {
&self.common.raw
}
#[inline]
pub fn style(&self) -> &CssStyleDeclaration {
&self.common.style
}
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 {
@ -382,7 +393,9 @@ impl Canvas {
{
self.on_resize_scale = Some(ResizeScaleHandle::new(
self.window().clone(),
self.document().clone(),
self.raw().clone(),
self.style().clone(),
scale_handler,
size_handler,
));
@ -425,7 +438,7 @@ impl Canvas {
// Then we resize the canvas to the new size, a new
// `Resized` event will be sent by the `ResizeObserver`:
let new_size = new_size.to_logical(scale);
super::set_canvas_size(self.window(), self.raw(), new_size);
super::set_canvas_size(self.document(), self.raw(), self.style(), new_size);
// Set the size might not trigger the event because the calculation is inaccurate.
self.on_resize_scale
@ -527,6 +540,6 @@ impl Common {
}
pub fn is_fullscreen(&self) -> bool {
super::is_fullscreen(&self.window, &self.raw)
super::is_fullscreen(&self.document, &self.raw)
}
}

View file

@ -18,16 +18,14 @@ use crate::platform::web::WindowExtWebSys;
use crate::window::Window;
use wasm_bindgen::closure::Closure;
use web_sys::{
CssStyleDeclaration, Element, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
CssStyleDeclaration, Document, Element, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
};
pub fn throw(msg: &str) {
wasm_bindgen::throw_str(msg);
}
pub fn exit_fullscreen(window: &web_sys::Window) {
let document = window.document().expect("Failed to obtain document");
pub fn exit_fullscreen(document: &Document) {
document.exit_fullscreen();
}
@ -69,35 +67,28 @@ pub fn scale_factor(window: &web_sys::Window) -> f64 {
}
pub fn set_canvas_size(
window: &web_sys::Window,
document: &Document,
raw: &HtmlCanvasElement,
style: &CssStyleDeclaration,
mut new_size: LogicalSize<f64>,
) {
let document = window.document().expect("Failed to obtain document");
if !document.contains(Some(raw)) {
return;
}
let style = window
.get_computed_style(raw)
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");
if style.get_property_value("display").unwrap() == "none" {
return;
}
if style.get_property_value("box-sizing").unwrap() == "border-box" {
new_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");
new_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");
new_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");
new_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");
}
set_canvas_style_property(raw, "width", &format!("{}px", new_size.width));
@ -123,9 +114,7 @@ pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value:
.unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}"))
}
pub fn is_fullscreen(window: &web_sys::Window, canvas: &HtmlCanvasElement) -> bool {
let document = window.document().expect("Failed to obtain document");
pub fn is_fullscreen(document: &Document, canvas: &HtmlCanvasElement) -> bool {
match document.fullscreen_element() {
Some(elem) => {
let canvas: &Element = canvas;
@ -143,8 +132,7 @@ pub fn is_dark_mode(window: &web_sys::Window) -> Option<bool> {
.map(|media| media.matches())
}
pub fn is_visible(window: &web_sys::Window) -> bool {
let document = window.document().expect("Failed to obtain document");
pub fn is_visible(document: &Document) -> bool {
document.visibility_state() == VisibilityState::Visible
}

View file

@ -3,8 +3,9 @@ use once_cell::unsync::Lazy;
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{
HtmlCanvasElement, MediaQueryList, ResizeObserver, ResizeObserverBoxOptions,
ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize, Window,
CssStyleDeclaration, Document, HtmlCanvasElement, MediaQueryList, ResizeObserver,
ResizeObserverBoxOptions, ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize,
Window,
};
use crate::dpi::{LogicalSize, PhysicalSize};
@ -20,7 +21,9 @@ pub struct ResizeScaleHandle(Rc<RefCell<ResizeScaleInternal>>);
impl ResizeScaleHandle {
pub(crate) fn new<S, R>(
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
scale_handler: S,
resize_handler: R,
) -> Self
@ -30,7 +33,9 @@ impl ResizeScaleHandle {
{
Self(ResizeScaleInternal::new(
window,
document,
canvas,
style,
scale_handler,
resize_handler,
))
@ -45,7 +50,9 @@ impl ResizeScaleHandle {
/// changes of the `devicePixelRatio`.
struct ResizeScaleInternal {
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
mql: MediaQueryListHandle,
observer: ResizeObserver,
_observer_closure: Closure<dyn FnMut(Array, ResizeObserver)>,
@ -57,7 +64,9 @@ struct ResizeScaleInternal {
impl ResizeScaleInternal {
fn new<S, R>(
window: Window,
document: Document,
canvas: HtmlCanvasElement,
style: CssStyleDeclaration,
scale_handler: S,
resize_handler: R,
) -> Rc<RefCell<Self>>
@ -80,7 +89,7 @@ impl ResizeScaleInternal {
if let Some(rc_self) = weak_self.upgrade() {
let mut this = rc_self.borrow_mut();
let size = Self::process_entry(&this.window, &this.canvas, entries);
let size = this.process_entry(entries);
if this.notify_scale.replace(false) {
let scale = backend::scale_factor(&this.window);
@ -94,7 +103,9 @@ impl ResizeScaleInternal {
RefCell::new(Self {
window,
document,
canvas,
style,
mql,
observer,
_observer_closure: observer_closure,
@ -142,17 +153,8 @@ impl ResizeScaleInternal {
}
fn notify(&mut self) {
let style = self
.window
.get_computed_style(&self.canvas)
.expect("Failed to obtain computed style")
// this can't fail: we aren't using a pseudo-element
.expect("Invalid pseudo-element");
let document = self.window.document().expect("Failed to obtain document");
if !document.contains(Some(&self.canvas))
|| style.get_property_value("display").unwrap() == "none"
if !self.document.contains(Some(&self.canvas))
|| self.style.get_property_value("display").unwrap() == "none"
{
let size = PhysicalSize::new(0, 0);
@ -175,19 +177,19 @@ impl ResizeScaleInternal {
}
let mut size = LogicalSize::new(
backend::style_size_property(&style, "width"),
backend::style_size_property(&style, "height"),
backend::style_size_property(&self.style, "width"),
backend::style_size_property(&self.style, "height"),
);
if style.get_property_value("box-sizing").unwrap() == "border-box" {
size.width -= backend::style_size_property(&style, "border-left-width")
+ backend::style_size_property(&style, "border-right-width")
+ backend::style_size_property(&style, "padding-left")
+ backend::style_size_property(&style, "padding-right");
size.height -= backend::style_size_property(&style, "border-top-width")
+ backend::style_size_property(&style, "border-bottom-width")
+ backend::style_size_property(&style, "padding-top")
+ backend::style_size_property(&style, "padding-bottom");
if self.style.get_property_value("box-sizing").unwrap() == "border-box" {
size.width -= backend::style_size_property(&self.style, "border-left-width")
+ backend::style_size_property(&self.style, "border-right-width")
+ backend::style_size_property(&self.style, "padding-left")
+ backend::style_size_property(&self.style, "padding-right");
size.height -= backend::style_size_property(&self.style, "border-top-width")
+ backend::style_size_property(&self.style, "border-bottom-width")
+ backend::style_size_property(&self.style, "padding-top")
+ backend::style_size_property(&self.style, "padding-bottom");
}
let size = size.to_physical(backend::scale_factor(&self.window));
@ -229,11 +231,7 @@ impl ResizeScaleInternal {
this.notify();
}
fn process_entry(
window: &Window,
canvas: &HtmlCanvasElement,
entries: Array,
) -> PhysicalSize<u32> {
fn process_entry(&self, entries: Array) -> PhysicalSize<u32> {
let entry: ResizeObserverEntry = entries.get(0).unchecked_into();
// Safari doesn't support `devicePixelContentBoxSize`
@ -241,7 +239,7 @@ impl ResizeScaleInternal {
let rect = entry.content_rect();
return LogicalSize::new(rect.width(), rect.height())
.to_physical(backend::scale_factor(window));
.to_physical(backend::scale_factor(&self.window));
}
let entry: ResizeObserverSize = entry
@ -249,13 +247,8 @@ impl ResizeScaleInternal {
.get(0)
.unchecked_into();
let style = window
.get_computed_style(canvas)
.expect("Failed to get computed style of canvas")
// this can only be empty if we provided an invalid `pseudoElt`
.expect("`getComputedStyle` can not be empty");
let writing_mode = style
let writing_mode = self
.style
.get_property_value("writing-mode")
.expect("`writing-mode` is a valid CSS property");