Skip to content

Commit

Permalink
Account for no initial ResizeObserver event on Safari
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Jun 11, 2023
1 parent c2a8a87 commit 0eb1d3e
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 64 deletions.
18 changes: 11 additions & 7 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,13 +542,17 @@ impl<T> EventLoopWindowTarget<T> {
{
let runner = self.runner.clone();

move |size| {
RefCell::borrow(&canvas_clone).set_inner_size(size);
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Resized(size),
});
runner.request_redraw(RootWindowId(id));
move |new_size| {
let canvas = RefCell::borrow(&canvas_clone);
canvas.set_current_size(new_size);
if canvas.old_size() != new_size {
canvas.set_old_size(new_size);
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Resized(new_size),
});
runner.request_redraw(RootWindowId(id));
}
}
},
);
Expand Down
28 changes: 19 additions & 9 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ pub struct Common {
pub window: web_sys::Window,
/// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
pub raw: HtmlCanvasElement,
size: Rc<Cell<PhysicalSize<u32>>>,
old_size: Rc<Cell<PhysicalSize<u32>>>,
current_size: Rc<Cell<PhysicalSize<u32>>>,
wants_fullscreen: Rc<RefCell<bool>>,
}

Expand Down Expand Up @@ -85,7 +86,8 @@ impl Canvas {
common: Common {
window,
raw: canvas,
size: Rc::default(),
old_size: Rc::default(),
current_size: Rc::default(),
wants_fullscreen: Rc::new(RefCell::new(false)),
},
id,
Expand Down Expand Up @@ -132,12 +134,20 @@ impl Canvas {
}
}

pub fn old_size(&self) -> PhysicalSize<u32> {
self.common.old_size.get()
}

pub fn inner_size(&self) -> PhysicalSize<u32> {
self.common.size.get()
self.common.current_size.get()
}

pub fn set_old_size(&self, size: PhysicalSize<u32>) {
self.common.old_size.set(size)
}

pub fn set_inner_size(&self, size: PhysicalSize<u32>) {
self.common.size.set(size)
pub fn set_current_size(&self, size: PhysicalSize<u32>) {
self.common.current_size.set(size)
}

pub fn window(&self) -> &web_sys::Window {
Expand Down Expand Up @@ -365,8 +375,7 @@ impl Canvas {
scale: f64,
) {
// First, we send the `ScaleFactorChanged` event:
let old_size = self.inner_size();
self.set_inner_size(current_size);
self.set_current_size(current_size);
let mut new_size = current_size;
event_handler(crate::event::Event::WindowEvent {
window_id: RootWindowId(self.id),
Expand All @@ -386,9 +395,10 @@ impl Canvas {
self.on_resize_scale
.as_ref()
.expect("expected Window to still be active")
.create_oneshot_resize();
} else if old_size != new_size {
.notify_resize();
} else if self.old_size() != new_size {
// Then we at least send a resized event.
self.set_old_size(new_size);
runner.send_event(crate::event::Event::WindowEvent {
window_id: RootWindowId(self.id),
event: crate::event::WindowEvent::Resized(new_size),
Expand Down
23 changes: 12 additions & 11 deletions src/platform_impl/web/web_sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,6 @@ pub fn set_canvas_size(
return;
}

/// 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.
fn style_size_property(style: &CssStyleDeclaration, property: &str) -> f64 {
let prop = style
.get_property_value(property)
.expect("Found invalid property");
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")
}
let style = window
.get_computed_style(raw)
.expect("Failed to obtain computed style")
Expand All @@ -98,6 +87,18 @@ pub fn set_canvas_size(
set_canvas_style_property(raw, "height", &format!("{}px", new_size.height));
}

/// 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.
pub fn style_size_property(style: &CssStyleDeclaration, property: &str) -> f64 {
let prop = style
.get_property_value(property)
.expect("Found invalid property");
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")
}

pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value: &str) {
let style = raw.style();
style
Expand Down
105 changes: 68 additions & 37 deletions src/platform_impl/web/web_sys/resize_scaling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::super::backend;
use super::media_query_handle::MediaQueryListHandle;

use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak};
use std::rc::Rc;

pub struct ResizeScaleHandle(Rc<RefCell<ResizeScaleInternal>>);

Expand All @@ -36,12 +36,8 @@ impl ResizeScaleHandle {
))
}

pub(crate) fn create_oneshot_resize(&self) {
self.0
.borrow()
.create_oneshot_observer(Rc::downgrade(&self.0), |this, size| {
(this.resize_handler)(size)
})
pub(crate) fn notify_resize(&self) {
self.0.borrow_mut().notify()
}
}

Expand All @@ -55,7 +51,7 @@ struct ResizeScaleInternal {
_observer_closure: Closure<dyn FnMut(Array, ResizeObserver)>,
scale_handler: Box<dyn FnMut(PhysicalSize<u32>, f64)>,
resize_handler: Box<dyn FnMut(PhysicalSize<u32>)>,
resize_enabled: Cell<bool>,
notify_scale: Cell<bool>,
}

impl ResizeScaleInternal {
Expand Down Expand Up @@ -84,8 +80,12 @@ impl ResizeScaleInternal {
if let Some(rc_self) = weak_self.upgrade() {
let mut this = rc_self.borrow_mut();

if this.resize_enabled.get() {
let size = Self::process_entry(&this.window, &this.canvas, entries);
let size = Self::process_entry(&this.window, &this.canvas, entries);

if this.notify_scale.replace(false) {
let scale = backend::scale_factor(&this.window);
(this.scale_handler)(size, scale)
} else {
(this.resize_handler)(size)
}
}
Expand All @@ -100,7 +100,7 @@ impl ResizeScaleInternal {
_observer_closure: observer_closure,
scale_handler: Box::new(scale_handler),
resize_handler: Box::new(resize_handler),
resize_enabled: Cell::new(true),
notify_scale: Cell::new(false),
})
})
}
Expand Down Expand Up @@ -141,26 +141,61 @@ impl ResizeScaleInternal {
observer
}

fn create_oneshot_observer(
&self,
weak_self: Weak<RefCell<Self>>,
handler: impl 'static + FnOnce(&mut Self, PhysicalSize<u32>),
) {
self.resize_enabled.set(false);
fn notify(&mut self) {
let document = self.window.document().expect("Failed to obtain document");

let observer_closure =
Closure::once_into_js(move |entries: Array, observer: ResizeObserver| {
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);
this.resize_enabled.set(true);
if !document.contains(Some(&self.canvas)) {
let size = PhysicalSize::new(0, 0);

handler(&mut this, size)
}
if self.notify_scale.replace(false) {
let scale = backend::scale_factor(&self.window);
(self.scale_handler)(size, scale)
} else {
(self.resize_handler)(size)
}

observer.disconnect();
});
Self::create_observer(&self.canvas, &observer_closure);
return;
}

// Safari doesn't support `devicePixelContentBoxSize`
if has_device_pixel_support() {
self.observer.unobserve(&self.canvas);
self.observer.observe(&self.canvas);

return;
}

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 mut size = LogicalSize::new(
backend::style_size_property(&style, "width"),
backend::style_size_property(&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");
}

let size = size.to_physical(backend::scale_factor(&self.window));

if self.notify_scale.replace(false) {
let scale = backend::scale_factor(&self.window);
(self.scale_handler)(size, scale)
} else {
(self.resize_handler)(size)
}
}

fn handle_scale(this: Rc<RefCell<Self>>, mql: &MediaQueryList) {
Expand All @@ -181,19 +216,15 @@ impl ResizeScaleInternal {
return;
}

let new_mql = Self::create_mql(&this.window, {
let weak_self = weak_self.clone();
move |mql| {
if let Some(rc_self) = weak_self.upgrade() {
Self::handle_scale(rc_self, mql);
}
let new_mql = Self::create_mql(&this.window, move |mql| {
if let Some(rc_self) = weak_self.upgrade() {
Self::handle_scale(rc_self, mql);
}
});
this.mql = new_mql;

this.create_oneshot_observer(weak_self, move |this, size| {
(this.scale_handler)(size, scale)
});
this.notify_scale.set(true);
this.notify();
}

fn process_entry(
Expand Down

0 comments on commit 0eb1d3e

Please sign in to comment.