diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c1b37..9fff3b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `WindowBehavior::moved` is called when the window moves. - `RunningWindow::outer_size` is a new function that returns the window's current size including decorations. +- `App::prevent_shutdown()` returns a guard that prevents the application from + closing automatically when the final window is closed. ### Changed diff --git a/src/lib.rs b/src/lib.rs index 926d7e8..1f8b082 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,6 +325,14 @@ where ExecutingApp::new(&self.running.windows, event_loop), )); } + EventLoopMessage::PreventShutdown => { + self.running.windows.prevent_shutdown(); + } + EventLoopMessage::AllowShutdown => { + if self.running.windows.allow_shutdown() { + exit(0) + } + } } } } @@ -365,6 +373,20 @@ where .ok()?; response_receiver.recv().ok() } + + /// Creates a guard that prevents this app from shutting down. + /// + /// If the app is not currently running, this function returns None. + /// + /// Once a guard is allocated the app will not be closed automatically when + /// the final window is closed. If the final shutdown guard is dropped while + /// no windows are open, the app will be closed. + pub fn prevent_shutdown(&self) -> Option> { + self.proxy + .send_event(EventLoopMessage::PreventShutdown) + .ok() + .map(|()| ShutdownGuard { app: self.clone() }) + } } impl Clone for App @@ -540,13 +562,27 @@ where /// A collection of open windows. pub struct Windows { - data: Arc>>>, + data: Arc>>, +} + +struct WindowsData { + open: HashMap>, + guards: usize, +} + +impl WindowsData { + fn should_shutdown(&self) -> bool { + self.open.is_empty() && self.guards == 0 + } } impl Default for Windows { fn default() -> Self { Self { - data: Arc::default(), + data: Arc::new(Mutex::new(WindowsData { + open: HashMap::new(), + guards: 0, + })), } } } @@ -564,7 +600,7 @@ impl Windows { /// been opened and is still open. pub fn get(&self, id: WindowId) -> Option> { let windows = self.data.lock().unwrap_or_else(PoisonError::into_inner); - windows.get(&id).and_then(|w| w.winit.winit()) + windows.open.get(&id).and_then(|w| w.winit.winit()) } #[allow(unsafe_code)] @@ -626,7 +662,7 @@ impl Windows { let id = winit.id(); let winit = OpenedWindow(Arc::new(Mutex::new(Some(winit)))); let mut windows = self.data.lock().unwrap_or_else(PoisonError::into_inner); - windows.insert( + windows.open.insert( id, OpenWindow { winit: winit.clone(), @@ -638,7 +674,7 @@ impl Windows { fn send(&self, window: WindowId, message: WindowMessage) { let mut data = self.data.lock().unwrap_or_else(PoisonError::into_inner); - if let Some(open_window) = data.get(&window) { + if let Some(open_window) = data.open.get(&window) { match open_window.sender.try_send(message) { Ok(()) => {} Err(mpsc::TrySendError::Full(_)) => { @@ -646,7 +682,7 @@ impl Windows { } Err(mpsc::TrySendError::Disconnected(_)) => { // Window no longer active, remove it. - data.remove(&window); + data.open.remove(&window); } } } @@ -654,10 +690,21 @@ impl Windows { fn close(&self, window: WindowId) -> bool { let mut data = self.data.lock().unwrap_or_else(PoisonError::into_inner); - if let Some(closed) = data.remove(&window) { + if let Some(closed) = data.open.remove(&window) { closed.winit.close(); } - data.is_empty() + data.should_shutdown() + } + + fn prevent_shutdown(&self) { + let mut data = self.data.lock().unwrap_or_else(PoisonError::into_inner); + data.guards += 1; + } + + fn allow_shutdown(&self) -> bool { + let mut data = self.data.lock().unwrap_or_else(PoisonError::into_inner); + data.guards -= 1; + data.should_shutdown() } } @@ -665,3 +712,20 @@ struct OpenWindow { winit: OpenedWindow, sender: Arc>>, } + +/// A guard preventing an [`App`] from shutting down. +pub struct ShutdownGuard +where + Message: crate::Message, +{ + app: App, +} + +impl Drop for ShutdownGuard +where + Message: crate::Message, +{ + fn drop(&mut self) { + let _ = self.app.proxy.send_event(EventLoopMessage::AllowShutdown); + } +} diff --git a/src/private.rs b/src/private.rs index 7018d23..8c34f06 100644 --- a/src/private.rs +++ b/src/private.rs @@ -60,6 +60,8 @@ where message: AppMessage, response_sender: mpsc::SyncSender, }, + PreventShutdown, + AllowShutdown, } #[derive(Debug)]