mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 05:21:31 +11:00
Increase accuracy of various Web APIs (#2946)
This commit is contained in:
parent
db8de03142
commit
c4d70d75c1
|
@ -13,6 +13,7 @@ And please only add new entries to the top of this list, right below the `# Unre
|
||||||
- Implement `PartialOrd` and `Ord` for `KeyCode` and `NativeKeyCode`.
|
- Implement `PartialOrd` and `Ord` for `KeyCode` and `NativeKeyCode`.
|
||||||
- On Web, implement `WindowEvent::Occluded`.
|
- On Web, implement `WindowEvent::Occluded`.
|
||||||
- On Web, fix touch location to be as accurate as mouse position.
|
- On Web, fix touch location to be as accurate as mouse position.
|
||||||
|
- On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position.
|
||||||
|
|
||||||
# 0.29.0-beta.0
|
# 0.29.0-beta.0
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
//! - [`WindowEvent::Occluded`]
|
//! - [`WindowEvent::Occluded`]
|
||||||
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
|
//! - [`WindowEvent::CursorMoved`], [`WindowEvent::CursorEntered`], [`WindowEvent::CursorLeft`],
|
||||||
//! and [`WindowEvent::Touch`].
|
//! and [`WindowEvent::Touch`].
|
||||||
|
//! - [`Window::set_outer_position()`]
|
||||||
//!
|
//!
|
||||||
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
//! [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
|
||||||
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size()
|
//! [`Window::(set_)inner_size()`]: crate::window::Window::inner_size()
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
//! [`WindowEvent::CursorEntered`]: crate::event::WindowEvent::CursorEntered
|
||||||
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
|
//! [`WindowEvent::CursorLeft`]: crate::event::WindowEvent::CursorLeft
|
||||||
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
|
//! [`WindowEvent::Touch`]: crate::event::WindowEvent::Touch
|
||||||
|
//! [`Window::set_outer_position()`]: crate::window::Window::set_outer_position()
|
||||||
//! [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
//! [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
|
||||||
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||||
|
|
|
@ -400,12 +400,17 @@ impl<T: 'static> Shared<T> {
|
||||||
if !runner.0.suspended.get() {
|
if !runner.0.suspended.get() {
|
||||||
for (id, canvas) in &*runner.0.all_canvases.borrow() {
|
for (id, canvas) in &*runner.0.all_canvases.borrow() {
|
||||||
if let Some(canvas) = canvas.upgrade() {
|
if let Some(canvas) = canvas.upgrade() {
|
||||||
if backend::is_intersecting(runner.window(), canvas.borrow().raw()) {
|
let is_visible = backend::is_visible(runner.window());
|
||||||
|
// only fire if:
|
||||||
|
// - not visible and intersects
|
||||||
|
// - not visible and we don't know if it intersects yet
|
||||||
|
// - visible and intersects
|
||||||
|
if let (false, Some(true) | None) | (true, Some(true)) =
|
||||||
|
(is_visible, canvas.borrow().is_intersecting)
|
||||||
|
{
|
||||||
runner.send_event(Event::WindowEvent {
|
runner.send_event(Event::WindowEvent {
|
||||||
window_id: *id,
|
window_id: *id,
|
||||||
event: WindowEvent::Occluded(!backend::is_visible(
|
event: WindowEvent::Occluded(!is_visible),
|
||||||
runner.window(),
|
|
||||||
)),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -705,9 +705,10 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
|
let canvas = canvas_clone.clone();
|
||||||
|
|
||||||
move |new_size| {
|
move |new_size| {
|
||||||
let canvas = RefCell::borrow(&canvas_clone);
|
let canvas = canvas.borrow();
|
||||||
canvas.set_current_size(new_size);
|
canvas.set_current_size(new_size);
|
||||||
if canvas.old_size() != new_size {
|
if canvas.old_size() != new_size {
|
||||||
canvas.set_old_size(new_size);
|
canvas.set_old_size(new_size);
|
||||||
|
@ -723,12 +724,17 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
|
|
||||||
let runner = self.runner.clone();
|
let runner = self.runner.clone();
|
||||||
canvas.on_intersection(move |is_intersecting| {
|
canvas.on_intersection(move |is_intersecting| {
|
||||||
if backend::is_visible(runner.window()) {
|
// only fire if visible while skipping the first event if it's intersecting
|
||||||
|
if backend::is_visible(runner.window())
|
||||||
|
&& !(is_intersecting && canvas_clone.borrow().is_intersecting.is_none())
|
||||||
|
{
|
||||||
runner.send_event(Event::WindowEvent {
|
runner.send_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(id),
|
window_id: RootWindowId(id),
|
||||||
event: WindowEvent::Occluded(!is_intersecting),
|
event: WindowEvent::Occluded(!is_intersecting),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canvas_clone.borrow_mut().is_intersecting = Some(is_intersecting);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub struct Canvas {
|
||||||
common: Common,
|
common: Common,
|
||||||
id: WindowId,
|
id: WindowId,
|
||||||
pub has_focus: Arc<AtomicBool>,
|
pub has_focus: Arc<AtomicBool>,
|
||||||
|
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_touch_end: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
||||||
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
||||||
|
@ -97,6 +98,7 @@ impl Canvas {
|
||||||
},
|
},
|
||||||
id,
|
id,
|
||||||
has_focus: Arc::new(AtomicBool::new(false)),
|
has_focus: Arc::new(AtomicBool::new(false)),
|
||||||
|
is_intersecting: None,
|
||||||
on_touch_start: None,
|
on_touch_start: None,
|
||||||
on_touch_end: None,
|
on_touch_end: None,
|
||||||
on_blur: None,
|
on_blur: None,
|
||||||
|
@ -134,13 +136,31 @@ impl Canvas {
|
||||||
|
|
||||||
pub fn position(&self) -> LogicalPosition<f64> {
|
pub fn position(&self) -> LogicalPosition<f64> {
|
||||||
let bounds = self.common.raw.get_bounding_client_rect();
|
let bounds = self.common.raw.get_bounding_client_rect();
|
||||||
|
let mut position = LogicalPosition {
|
||||||
LogicalPosition {
|
|
||||||
x: bounds.x(),
|
x: bounds.x(),
|
||||||
y: bounds.y(),
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position
|
||||||
|
}
|
||||||
|
|
||||||
pub fn old_size(&self) -> PhysicalSize<u32> {
|
pub fn old_size(&self) -> PhysicalSize<u32> {
|
||||||
self.common.old_size.get()
|
self.common.old_size.get()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,19 +12,9 @@ impl IntersectionObserverHandle {
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(bool),
|
F: 'static + FnMut(bool),
|
||||||
{
|
{
|
||||||
let mut skip = true;
|
|
||||||
let closure = Closure::new(move |entries: Array| {
|
let closure = Closure::new(move |entries: Array| {
|
||||||
let entry: IntersectionObserverEntry = entries.get(0).unchecked_into();
|
let entry: IntersectionObserverEntry = entries.get(0).unchecked_into();
|
||||||
|
callback(entry.is_intersecting());
|
||||||
let is_intersecting = entry.is_intersecting();
|
|
||||||
|
|
||||||
// skip first intersection
|
|
||||||
if skip && is_intersecting {
|
|
||||||
skip = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(is_intersecting);
|
|
||||||
});
|
});
|
||||||
let observer = IntersectionObserver::new(closure.as_ref().unchecked_ref())
|
let observer = IntersectionObserver::new(closure.as_ref().unchecked_ref())
|
||||||
// we don't provide any `options`
|
// we don't provide any `options`
|
||||||
|
|
|
@ -75,13 +75,17 @@ pub fn set_canvas_size(
|
||||||
) {
|
) {
|
||||||
let document = window.document().expect("Failed to obtain document");
|
let document = window.document().expect("Failed to obtain document");
|
||||||
|
|
||||||
|
if !document.contains(Some(raw)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let style = window
|
let style = window
|
||||||
.get_computed_style(raw)
|
.get_computed_style(raw)
|
||||||
.expect("Failed to obtain computed style")
|
.expect("Failed to obtain computed style")
|
||||||
// this can't fail: we aren't using a pseudo-element
|
// this can't fail: we aren't using a pseudo-element
|
||||||
.expect("Invalid pseudo-element");
|
.expect("Invalid pseudo-element");
|
||||||
|
|
||||||
if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" {
|
if style.get_property_value("display").unwrap() == "none" {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,20 +148,4 @@ pub fn is_visible(window: &web_sys::Window) -> bool {
|
||||||
document.visibility_state() == VisibilityState::Visible
|
document.visibility_state() == VisibilityState::Visible
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_intersecting(window: &web_sys::Window, canvas: &HtmlCanvasElement) -> bool {
|
|
||||||
let rect = canvas.get_bounding_client_rect();
|
|
||||||
// This should never panic.
|
|
||||||
let window_width = window.inner_width().unwrap().as_f64().unwrap() as i32;
|
|
||||||
let window_height = window.inner_height().unwrap().as_f64().unwrap() as i32;
|
|
||||||
let left = rect.left() as i32;
|
|
||||||
let width = rect.width() as i32;
|
|
||||||
let top = rect.top() as i32;
|
|
||||||
let height = rect.height() as i32;
|
|
||||||
|
|
||||||
let horizontal = left <= window_width && left + width >= 0;
|
|
||||||
let vertical = top <= window_height && top + height >= 0;
|
|
||||||
|
|
||||||
horizontal && vertical
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type RawCanvasType = HtmlCanvasElement;
|
pub type RawCanvasType = HtmlCanvasElement;
|
||||||
|
|
|
@ -127,9 +127,29 @@ impl Window {
|
||||||
|
|
||||||
pub fn set_outer_position(&self, position: Position) {
|
pub fn set_outer_position(&self, position: Position) {
|
||||||
self.inner.dispatch(move |inner| {
|
self.inner.dispatch(move |inner| {
|
||||||
let position = position.to_logical::<f64>(inner.scale_factor());
|
let mut position = position.to_logical::<f64>(inner.scale_factor());
|
||||||
|
|
||||||
let canvas = inner.canvas.borrow();
|
let canvas = inner.canvas.borrow();
|
||||||
|
let document = inner.window.document().expect("Failed to obtain document");
|
||||||
|
|
||||||
|
if document.contains(Some(canvas.raw())) {
|
||||||
|
let style = inner
|
||||||
|
.window
|
||||||
|
.get_computed_style(canvas.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 -= backend::style_size_property(&style, "margin-left")
|
||||||
|
+ backend::style_size_property(&style, "border-left-width")
|
||||||
|
+ backend::style_size_property(&style, "padding-left");
|
||||||
|
position.y -= backend::style_size_property(&style, "margin-top")
|
||||||
|
+ backend::style_size_property(&style, "border-top-width")
|
||||||
|
+ backend::style_size_property(&style, "padding-top");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
canvas.set_attribute("position", "fixed");
|
canvas.set_attribute("position", "fixed");
|
||||||
canvas.set_attribute("left", &position.x.to_string());
|
canvas.set_attribute("left", &position.x.to_string());
|
||||||
canvas.set_attribute("top", &position.y.to_string());
|
canvas.set_attribute("top", &position.y.to_string());
|
||||||
|
|
|
@ -629,8 +629,11 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the
|
/// - **iOS:** Can only be called on the main thread. Sets the top left coordinates of the
|
||||||
/// window in the screen space coordinate system.
|
/// window in the screen space coordinate system.
|
||||||
/// - **Web:** Sets the top-left coordinates relative to the viewport.
|
/// - **Web:** Sets the top-left coordinates relative to the viewport. Doesn't account for CSS
|
||||||
|
/// [`transform`].
|
||||||
/// - **Android / Wayland:** Unsupported.
|
/// - **Android / Wayland:** Unsupported.
|
||||||
|
///
|
||||||
|
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_outer_position<P: Into<Position>>(&self, position: P) {
|
pub fn set_outer_position<P: Into<Position>>(&self, position: P) {
|
||||||
self.window.set_outer_position(position.into())
|
self.window.set_outer_position(position.into())
|
||||||
|
|
Loading…
Reference in a new issue