Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ResizeObserver #2859

Merged
merged 6 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Web, fix pen treated as mouse input.
- On Web, send mouse position on button release as well.
- On Web, fix touch input not gaining or loosing focus.
- **Breaking:** On Web, dropped support for Safari versions below 13.
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
- On Web, prevent clicks on the canvas to select text.
- On Web, `EventLoopProxy` now implements `Send`.
- On Web, `Window` now implements `Send` and `Sync`.
Expand All @@ -79,6 +79,8 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
- On macOS, fix crash when dropping `Window`.
- On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to
the canvas size will be reported through `WindowEvent::Resized`.

# 0.28.6

Expand Down
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,13 @@ redox_syscall = "0.3"

[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
package = "web-sys"
version = "0.3"
version = "0.3.64"
features = [
'console',
'CssStyleDeclaration',
'Document',
'DomRect',
'DomRectReadOnly',
'Element',
'Event',
'EventTarget',
Expand All @@ -145,6 +146,11 @@ features = [
'MediaQueryList',
'Node',
'PointerEvent',
'ResizeObserver',
'ResizeObserverBoxOptions',
'ResizeObserverEntry',
'ResizeObserverOptions',
'ResizeObserverSize',
'Window',
'WheelEvent'
]
Expand Down
24 changes: 22 additions & 2 deletions examples/web.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#![allow(clippy::disallowed_methods, clippy::single_match)]

use winit::{
event::{Event, WindowEvent},
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
window::WindowBuilder,
keyboard::KeyCode,
window::{Fullscreen, WindowBuilder},
};

pub fn main() {
Expand Down Expand Up @@ -31,6 +32,25 @@ pub fn main() {
Event::MainEventsCleared => {
window.request_redraw();
}
Event::WindowEvent {
window_id,
event:
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: KeyCode::KeyF,
state: ElementState::Released,
..
},
..
},
} if window_id == window.id() => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
}
}
_ => (),
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/web/event_loop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod proxy;
mod runner;
pub(crate) mod runner;
mod state;
mod window_target;

Expand Down
184 changes: 60 additions & 124 deletions src/platform_impl/web/event_loop/runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{super::ScaleChangeArgs, backend, state::State};
use super::{backend, state::State};
use crate::dpi::PhysicalSize;
use crate::event::{Event, StartCause};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;
Expand All @@ -25,13 +26,12 @@ impl<T> Clone for Shared<T> {

pub struct Execution<T: 'static> {
runner: RefCell<RunnerEnum<T>>,
events: RefCell<VecDeque<Event<'static, T>>>,
events: RefCell<VecDeque<EventWrapper<T>>>,
id: RefCell<u32>,
window: web_sys::Window,
all_canvases: RefCell<Vec<(WindowId, Weak<RefCell<backend::Canvas>>)>>,
redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>,
scale_change_detector: RefCell<Option<backend::ScaleChangeDetector>>,
unload_event_handle: RefCell<Option<backend::UnloadEventHandle>>,
}

Expand Down Expand Up @@ -86,10 +86,31 @@ impl<T: 'static> Runner<T> {
})
}

fn handle_single_event(&mut self, event: Event<'_, T>, control: &mut ControlFlow) {
fn handle_single_event(
&mut self,
runner: &Shared<T>,
event: impl Into<EventWrapper<T>>,
control: &mut ControlFlow,
) {
let is_closed = matches!(*control, ControlFlow::ExitWithCode(_));

(self.event_handler)(event, control);
match event.into() {
EventWrapper::Event(event) => (self.event_handler)(event, control),
EventWrapper::ScaleChange {
canvas,
size,
scale,
} => {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().handle_scale_change(
runner,
|event| (self.event_handler)(event, control),
size,
scale,
)
}
}
}

// Maintain closed state, even if the callback changes it
if is_closed {
Expand All @@ -109,7 +130,6 @@ impl<T: 'static> Shared<T> {
all_canvases: RefCell::new(Vec::new()),
redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()),
scale_change_detector: RefCell::new(None),
unload_event_handle: RefCell::new(None),
}))
}
Expand Down Expand Up @@ -147,16 +167,6 @@ impl<T: 'static> Shared<T> {
}));
}

pub(crate) fn set_on_scale_change<F>(&self, handler: F)
where
F: 'static + FnMut(ScaleChangeArgs),
{
*self.0.scale_change_detector.borrow_mut() = Some(backend::ScaleChangeDetector::new(
self.window().clone(),
handler,
));
}

// Generate a strictly increasing ID
// This is used to differentiate windows when handling events
pub fn generate_id(&self) -> u32 {
Expand All @@ -168,7 +178,7 @@ impl<T: 'static> Shared<T> {

pub fn request_redraw(&self, id: WindowId) {
self.0.redraw_pending.borrow_mut().insert(id);
self.send_events(iter::empty());
self.send_events::<EventWrapper<T>>(iter::empty());
}

pub fn init(&self) {
Expand Down Expand Up @@ -196,14 +206,17 @@ impl<T: 'static> Shared<T> {
// Add an event to the event loop runner, from the user or an event handler
//
// It will determine if the event should be immediately sent to the user or buffered for later
pub fn send_event(&self, event: Event<'static, T>) {
pub(crate) fn send_event<E: Into<EventWrapper<T>>>(&self, event: E) {
self.send_events(iter::once(event));
}

// Add a series of events to the event loop runner
//
// It will determine if the event should be immediately sent to the user or buffered for later
pub fn send_events(&self, events: impl IntoIterator<Item = Event<'static, T>>) {
pub(crate) fn send_events<E: Into<EventWrapper<T>>>(
&self,
events: impl IntoIterator<Item = E>,
) {
// If the event loop is closed, it should discard any new events
if self.is_closed() {
return;
Expand Down Expand Up @@ -232,7 +245,10 @@ impl<T: 'static> Shared<T> {
}
if !process_immediately {
// Queue these events to look at later
self.0.events.borrow_mut().extend(events);
self.0
.events
.borrow_mut()
.extend(events.into_iter().map(Into::into));
return;
}
// At this point, we know this is a fresh set of events
Expand All @@ -250,13 +266,13 @@ impl<T: 'static> Shared<T> {
// Take the start event, then the events provided to this function, and run an iteration of
// the event loop
let start_event = Event::NewEvents(start_cause);
let events = iter::once(start_event).chain(events);
let events =
iter::once(EventWrapper::from(start_event)).chain(events.into_iter().map(Into::into));
self.run_until_cleared(events);
}

// Process the destroy-pending windows. This should only be called from
// `run_until_cleared` and `handle_scale_changed`, somewhere between emitting
// `NewEvents` and `MainEventsCleared`.
// `run_until_cleared`, somewhere between emitting `NewEvents` and `MainEventsCleared`.
fn process_destroy_pending_windows(&self, control: &mut ControlFlow) {
while let Some(id) = self.0.destroy_pending.borrow_mut().pop_front() {
self.0
Expand All @@ -278,10 +294,10 @@ impl<T: 'static> Shared<T> {
// cleared
//
// This will also process any events that have been queued or that are queued during processing
fn run_until_cleared(&self, events: impl Iterator<Item = Event<'static, T>>) {
fn run_until_cleared<E: Into<EventWrapper<T>>>(&self, events: impl Iterator<Item = E>) {
let mut control = self.current_control_flow();
for event in events {
self.handle_event(event, &mut control);
self.handle_event(event.into(), &mut control);
}
self.process_destroy_pending_windows(&mut control);
self.handle_event(Event::MainEventsCleared, &mut control);
Expand All @@ -301,85 +317,6 @@ impl<T: 'static> Shared<T> {
}
}

pub fn handle_scale_changed(&self, old_scale: f64, new_scale: f64) {
// If there aren't any windows, then there is nothing to do here.
if self.0.all_canvases.borrow().is_empty() {
return;
}

let start_cause = match (self.0.runner.borrow().maybe_runner())
.unwrap_or_else(|| unreachable!("`scale_changed` should not happen without a runner"))
.maybe_start_cause()
{
Some(c) => c,
// If we're in the exit state, don't do event processing
None => return,
};
let mut control = self.current_control_flow();

// Handle the start event and all other events in the queue.
self.handle_event(Event::NewEvents(start_cause), &mut control);

// It is possible for windows to be dropped before this point. We don't
// want to send `ScaleFactorChanged` for destroyed windows, so we process
// the destroy-pending windows here.
self.process_destroy_pending_windows(&mut control);

// Now handle the `ScaleFactorChanged` events.
for &(id, ref canvas) in &*self.0.all_canvases.borrow() {
let rc = match canvas.upgrade() {
Some(rc) => rc,
// This shouldn't happen, but just in case...
None => continue,
};
let canvas = rc.borrow();
// First, we send the `ScaleFactorChanged` event:
let current_size = canvas.size().get();
let logical_size = current_size.to_logical::<f64>(old_scale);
let mut new_size = logical_size.to_physical(new_scale);
self.handle_single_event_sync(
Event::WindowEvent {
window_id: id,
event: crate::event::WindowEvent::ScaleFactorChanged {
scale_factor: new_scale,
new_inner_size: &mut new_size,
},
},
&mut control,
);

// Then we resize the canvas to the new size and send a `Resized` event:
if current_size != new_size {
backend::set_canvas_size(&canvas, crate::dpi::Size::Physical(new_size));
self.handle_single_event_sync(
Event::WindowEvent {
window_id: id,
event: crate::event::WindowEvent::Resized(new_size),
},
&mut control,
);
}
}

// Process the destroy-pending windows again.
self.process_destroy_pending_windows(&mut control);
self.handle_event(Event::MainEventsCleared, &mut control);

// Discard all the pending redraw as we shall just redraw all windows.
self.0.redraw_pending.borrow_mut().clear();
for &(window_id, _) in &*self.0.all_canvases.borrow() {
self.handle_event(Event::RedrawRequested(window_id), &mut control);
}
self.handle_event(Event::RedrawEventsCleared, &mut control);

self.apply_control_flow(control);
// If the event loop is closed, it has been closed this iteration and now the closing
// event should be emitted
if self.is_closed() {
self.handle_loop_destroyed(&mut control);
}
}

fn handle_unload(&self) {
self.apply_control_flow(ControlFlow::Exit);
let mut control = self.current_control_flow();
Expand All @@ -388,35 +325,20 @@ impl<T: 'static> Shared<T> {
self.handle_event(Event::LoopDestroyed, &mut control);
}

// handle_single_event_sync takes in an event and handles it synchronously.
//
// It should only ever be called from `scale_changed`.
fn handle_single_event_sync(&self, event: Event<'_, T>, control: &mut ControlFlow) {
if self.is_closed() {
*control = ControlFlow::Exit;
}
match *self.0.runner.borrow_mut() {
RunnerEnum::Running(ref mut runner) => {
runner.handle_single_event(event, control);
}
_ => panic!("Cannot handle event synchronously without a runner"),
}
}

// handle_event takes in events and either queues them or applies a callback
//
// It should only ever be called from `run_until_cleared` and `scale_changed`.
fn handle_event(&self, event: Event<'static, T>, control: &mut ControlFlow) {
// It should only ever be called from `run_until_cleared`.
fn handle_event(&self, event: impl Into<EventWrapper<T>>, control: &mut ControlFlow) {
if self.is_closed() {
*control = ControlFlow::Exit;
}
match *self.0.runner.borrow_mut() {
RunnerEnum::Running(ref mut runner) => {
runner.handle_single_event(event, control);
runner.handle_single_event(self, event, control);
}
// If an event is being handled without a runner somehow, add it to the event queue so
// it will eventually be processed
RunnerEnum::Pending => self.0.events.borrow_mut().push_back(event),
RunnerEnum::Pending => self.0.events.borrow_mut().push_back(event.into()),
// If the Runner has been destroyed, there is nothing to do.
RunnerEnum::Destroyed => return,
}
Expand Down Expand Up @@ -482,7 +404,6 @@ impl<T: 'static> Shared<T> {
fn handle_loop_destroyed(&self, control: &mut ControlFlow) {
self.handle_event(Event::LoopDestroyed, control);
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.scale_change_detector.borrow_mut() = None;
*self.0.unload_event_handle.borrow_mut() = None;
// Dropping the `Runner` drops the event handler closure, which will in
// turn drop all `Window`s moved into the closure.
Expand Down Expand Up @@ -530,3 +451,18 @@ impl<T: 'static> Shared<T> {
}
}
}

pub(crate) enum EventWrapper<T: 'static> {
Event(Event<'static, T>),
ScaleChange {
canvas: Weak<RefCell<backend::Canvas>>,
size: PhysicalSize<u32>,
scale: f64,
},
}

impl<T> From<Event<'static, T>> for EventWrapper<T> {
fn from(value: Event<'static, T>) -> Self {
Self::Event(value)
}
}
Loading