From 644dc13e0012f7ca69892684d70231c1ebe2fd3a Mon Sep 17 00:00:00 2001 From: alvinhochun Date: Tue, 22 Sep 2020 06:19:00 +0800 Subject: [PATCH] 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 --- CHANGELOG.md | 1 + src/platform_impl/web/event_loop/runner.rs | 38 ++++++++++------------ src/platform_impl/web/window.rs | 27 ++++++++++++--- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faed9c33..cc065828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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, 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. +- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called. # 0.22.2 (2020-05-16) diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index af46e024..4c51a8a3 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -9,6 +9,7 @@ use std::{ clone::Clone, collections::{HashSet, VecDeque}, iter, + ops::Deref, rc::{Rc, Weak}, }; @@ -53,7 +54,6 @@ impl RunnerEnum { struct Runner { state: State, - is_busy: bool, event_handler: Box, &mut root::ControlFlow)>, } @@ -61,7 +61,6 @@ impl Runner { pub fn new(event_handler: Box, &mut root::ControlFlow)>) -> Self { Runner { state: State::Init, - is_busy: false, event_handler, } } @@ -87,18 +86,12 @@ impl Runner { fn handle_single_event(&mut self, event: Event<'_, T>, control: &mut root::ControlFlow) { 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); // Maintain closed state, even if the callback changes it if is_closed { *control = root::ControlFlow::Exit; } - - // An event is no longer being processed - self.is_busy = false; } } @@ -205,23 +198,25 @@ impl Shared { } // If we can run the event processing right now, or need to queue this and wait for later let mut process_immediately = true; - match &*self.0.runner.borrow() { - RunnerEnum::Running(ref runner) => { + match self.0.runner.try_borrow().as_ref().map(Deref::deref) { + Ok(RunnerEnum::Running(ref runner)) => { // If we're currently polling, queue this and wait for the poll() method to be called if let State::Poll { .. } = runner.state { 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 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`. - RunnerEnum::Destroyed => unreachable!(), + Ok(RunnerEnum::Destroyed) => unreachable!(), } if !process_immediately { // Queue these events to look at later @@ -502,12 +497,15 @@ impl Shared { // Check if the event loop is currently closed fn is_closed(&self) -> bool { - match *self.0.runner.borrow() { - RunnerEnum::Running(ref runner) => runner.state.is_exit(), + match self.0.runner.try_borrow().as_ref().map(Deref::deref) { + Ok(RunnerEnum::Running(runner)) => runner.state.is_exit(), // 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. - 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, } } diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 586da040..b78048f4 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -1,5 +1,6 @@ use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOE}; +use crate::event; use crate::icon::Icon; use crate::monitor::MonitorHandle as RootMH; use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI}; @@ -18,6 +19,7 @@ pub struct Window { previous_pointer: RefCell<&'static str>, id: Id, register_redraw_request: Box, + resize_notify_fn: Box)>, destroy_fn: Option>, } @@ -38,6 +40,14 @@ impl Window { 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 destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id))); @@ -46,13 +56,17 @@ impl Window { previous_pointer: RefCell::new("auto"), id, register_redraw_request, + resize_notify_fn, destroy_fn: Some(destroy_fn), }; - window.set_inner_size(attr.inner_size.unwrap_or(Size::Logical(LogicalSize { - width: 1024.0, - height: 768.0, - }))); + backend::set_canvas_size( + window.canvas.borrow().raw(), + attr.inner_size.unwrap_or(Size::Logical(LogicalSize { + width: 1024.0, + height: 768.0, + })), + ); window.set_title(&attr.title); window.set_maximized(attr.maximized); window.set_visible(attr.visible); @@ -112,7 +126,12 @@ impl Window { #[inline] pub fn set_inner_size(&self, size: Size) { + let old_size = self.inner_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]