web: Emit WindowEvent::Resized on Window::set_inner_size (#1717)

* web: Allow event to be queued from inside the EventLoop handler

The Runner is behind a RefCell, which is mutably borrowed when the event
handler is being called. To queue events, `send_events` needs to check
`is_closed()` and the `is_busy` flag, but it cannot be done since the
RefCell is already locked. This commit changes the conditions to work
without needing a successful borrow.

* web: Emit WindowEvent::Resized on Window::set_inner_size

* Update changelog
This commit is contained in:
alvinhochun 2020-09-22 06:19:00 +08:00 committed by GitHub
parent 47e7aa4209
commit 644dc13e00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 24 deletions

View file

@ -40,6 +40,7 @@
- On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed. - On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed.
- On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed. - On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed.
- **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped. - **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped.
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
# 0.22.2 (2020-05-16) # 0.22.2 (2020-05-16)

View file

@ -9,6 +9,7 @@ use std::{
clone::Clone, clone::Clone,
collections::{HashSet, VecDeque}, collections::{HashSet, VecDeque},
iter, iter,
ops::Deref,
rc::{Rc, Weak}, rc::{Rc, Weak},
}; };
@ -53,7 +54,6 @@ impl<T: 'static> RunnerEnum<T> {
struct Runner<T: 'static> { struct Runner<T: 'static> {
state: State, state: State,
is_busy: bool,
event_handler: Box<dyn FnMut(Event<'_, T>, &mut root::ControlFlow)>, event_handler: Box<dyn FnMut(Event<'_, T>, &mut root::ControlFlow)>,
} }
@ -61,7 +61,6 @@ impl<T: 'static> Runner<T> {
pub fn new(event_handler: Box<dyn FnMut(Event<'_, T>, &mut root::ControlFlow)>) -> Self { pub fn new(event_handler: Box<dyn FnMut(Event<'_, T>, &mut root::ControlFlow)>) -> Self {
Runner { Runner {
state: State::Init, state: State::Init,
is_busy: false,
event_handler, event_handler,
} }
} }
@ -87,18 +86,12 @@ impl<T: 'static> Runner<T> {
fn handle_single_event(&mut self, event: Event<'_, T>, control: &mut root::ControlFlow) { fn handle_single_event(&mut self, event: Event<'_, T>, control: &mut root::ControlFlow) {
let is_closed = *control == root::ControlFlow::Exit; let is_closed = *control == root::ControlFlow::Exit;
// An event is being processed, so the runner should be marked busy
self.is_busy = true;
(self.event_handler)(event, control); (self.event_handler)(event, control);
// Maintain closed state, even if the callback changes it // Maintain closed state, even if the callback changes it
if is_closed { if is_closed {
*control = root::ControlFlow::Exit; *control = root::ControlFlow::Exit;
} }
// An event is no longer being processed
self.is_busy = false;
} }
} }
@ -205,23 +198,25 @@ impl<T: 'static> Shared<T> {
} }
// If we can run the event processing right now, or need to queue this and wait for later // If we can run the event processing right now, or need to queue this and wait for later
let mut process_immediately = true; let mut process_immediately = true;
match &*self.0.runner.borrow() { match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
RunnerEnum::Running(ref runner) => { Ok(RunnerEnum::Running(ref runner)) => {
// If we're currently polling, queue this and wait for the poll() method to be called // If we're currently polling, queue this and wait for the poll() method to be called
if let State::Poll { .. } = runner.state { if let State::Poll { .. } = runner.state {
process_immediately = false; process_immediately = false;
} }
// If the runner is busy, queue this and wait for it to process it later
if runner.is_busy {
process_immediately = false;
}
} }
RunnerEnum::Pending => { Ok(RunnerEnum::Pending) => {
// The runner still hasn't been attached: queue this event and wait for it to be // The runner still hasn't been attached: queue this event and wait for it to be
process_immediately = false; process_immediately = false;
} }
// Some other code is mutating the runner, which most likely means
// the event loop is running and busy. So we queue this event for
// it to be processed later.
Err(_) => {
process_immediately = false;
}
// This is unreachable since `self.is_closed() == true`. // This is unreachable since `self.is_closed() == true`.
RunnerEnum::Destroyed => unreachable!(), Ok(RunnerEnum::Destroyed) => unreachable!(),
} }
if !process_immediately { if !process_immediately {
// Queue these events to look at later // Queue these events to look at later
@ -502,12 +497,15 @@ impl<T: 'static> Shared<T> {
// Check if the event loop is currently closed // Check if the event loop is currently closed
fn is_closed(&self) -> bool { fn is_closed(&self) -> bool {
match *self.0.runner.borrow() { match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
RunnerEnum::Running(ref runner) => runner.state.is_exit(), Ok(RunnerEnum::Running(runner)) => runner.state.is_exit(),
// The event loop is not closed since it is not initialized. // The event loop is not closed since it is not initialized.
RunnerEnum::Pending => false, Ok(RunnerEnum::Pending) => false,
// The event loop is closed since it has been destroyed. // The event loop is closed since it has been destroyed.
RunnerEnum::Destroyed => true, Ok(RunnerEnum::Destroyed) => true,
// Some other code is mutating the runner, which most likely means
// the event loop is running and busy.
Err(_) => false,
} }
} }

View file

@ -1,5 +1,6 @@
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOE}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::event;
use crate::icon::Icon; use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMH; use crate::monitor::MonitorHandle as RootMH;
use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI}; use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI};
@ -18,6 +19,7 @@ pub struct Window {
previous_pointer: RefCell<&'static str>, previous_pointer: RefCell<&'static str>,
id: Id, id: Id,
register_redraw_request: Box<dyn Fn()>, register_redraw_request: Box<dyn Fn()>,
resize_notify_fn: Box<dyn Fn(PhysicalSize<u32>)>,
destroy_fn: Option<Box<dyn FnOnce()>>, destroy_fn: Option<Box<dyn FnOnce()>>,
} }
@ -38,6 +40,14 @@ impl Window {
target.register(&mut canvas, id); target.register(&mut canvas, id);
let runner = target.runner.clone();
let resize_notify_fn = Box::new(move |new_size| {
runner.send_event(event::Event::WindowEvent {
window_id: RootWI(id),
event: event::WindowEvent::Resized(new_size),
});
});
let runner = target.runner.clone(); let runner = target.runner.clone();
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id))); let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
@ -46,13 +56,17 @@ impl Window {
previous_pointer: RefCell::new("auto"), previous_pointer: RefCell::new("auto"),
id, id,
register_redraw_request, register_redraw_request,
resize_notify_fn,
destroy_fn: Some(destroy_fn), destroy_fn: Some(destroy_fn),
}; };
window.set_inner_size(attr.inner_size.unwrap_or(Size::Logical(LogicalSize { backend::set_canvas_size(
width: 1024.0, window.canvas.borrow().raw(),
height: 768.0, attr.inner_size.unwrap_or(Size::Logical(LogicalSize {
}))); width: 1024.0,
height: 768.0,
})),
);
window.set_title(&attr.title); window.set_title(&attr.title);
window.set_maximized(attr.maximized); window.set_maximized(attr.maximized);
window.set_visible(attr.visible); window.set_visible(attr.visible);
@ -112,7 +126,12 @@ impl Window {
#[inline] #[inline]
pub fn set_inner_size(&self, size: Size) { pub fn set_inner_size(&self, size: Size) {
let old_size = self.inner_size();
backend::set_canvas_size(self.canvas.borrow().raw(), size); backend::set_canvas_size(self.canvas.borrow().raw(), size);
let new_size = self.inner_size();
if old_size != new_size {
(self.resize_notify_fn)(new_size);
}
} }
#[inline] #[inline]