Implement ResizeObserver (#2859)
Co-authored-by: Liam Murphy <43807659+Liamolucko@users.noreply.github.com>
This commit is contained in:
parent
7ce86c3d2a
commit
9a9c9b15ba
12 changed files with 584 additions and 364 deletions
|
|
@ -1,5 +1,5 @@
|
|||
mod proxy;
|
||||
mod runner;
|
||||
pub(crate) mod runner;
|
||||
mod state;
|
||||
mod window_target;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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>>,
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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),
|
||||
}))
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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();
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use std::sync::Arc;
|
|||
|
||||
use raw_window_handle::{RawDisplayHandle, WebDisplayHandle};
|
||||
|
||||
use super::runner::EventWrapper;
|
||||
use super::{
|
||||
super::{monitor::MonitorHandle, KeyEventExtra},
|
||||
backend,
|
||||
|
|
@ -16,7 +17,6 @@ use super::{
|
|||
runner,
|
||||
window::WindowId,
|
||||
};
|
||||
use crate::dpi::Size;
|
||||
use crate::event::{
|
||||
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase,
|
||||
WindowEvent,
|
||||
|
|
@ -71,10 +71,6 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
|
||||
pub fn run(&self, event_handler: Box<runner::EventHandler<T>>) {
|
||||
self.runner.set_listener(event_handler);
|
||||
let runner = self.runner.clone();
|
||||
self.runner.set_on_scale_change(move |arg| {
|
||||
runner.handle_scale_changed(arg.old_scale, arg.new_scale)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn generate_id(&self) -> WindowId {
|
||||
|
|
@ -595,35 +591,6 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
prevent_default,
|
||||
);
|
||||
|
||||
// The size to restore to after exiting fullscreen.
|
||||
let mut intended_size = canvas.size().get();
|
||||
|
||||
canvas.on_fullscreen_change({
|
||||
let window = self.runner.window().clone();
|
||||
let runner = self.runner.clone();
|
||||
|
||||
move || {
|
||||
let canvas = canvas_clone.borrow();
|
||||
|
||||
// If the canvas is marked as fullscreen, it is moving *into* fullscreen
|
||||
// If it is not, it is moving *out of* fullscreen
|
||||
let new_size = if backend::is_fullscreen(&window, canvas.raw()) {
|
||||
intended_size = canvas.size().get();
|
||||
|
||||
backend::window_size(&window).to_physical(backend::scale_factor(&window))
|
||||
} else {
|
||||
intended_size
|
||||
};
|
||||
|
||||
backend::set_canvas_size(&canvas, Size::Physical(new_size));
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Resized(new_size),
|
||||
});
|
||||
runner.request_redraw(RootWindowId(id));
|
||||
}
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
canvas.on_touch_cancel(move |device_id, location, force| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
|
|
@ -650,6 +617,37 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
event: WindowEvent::ThemeChanged(theme),
|
||||
});
|
||||
});
|
||||
|
||||
canvas.on_resize_scale(
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
let canvas = canvas_clone.clone();
|
||||
|
||||
move |size, scale| {
|
||||
runner.send_event(EventWrapper::ScaleChange {
|
||||
canvas: Rc::downgrade(&canvas),
|
||||
size,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
let runner = self.runner.clone();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue