App::prevent_shutdown()

This commit is contained in:
Jonathan Johnson 2024-09-12 07:37:14 -07:00
parent 93479b8111
commit 331bfdd353
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
3 changed files with 76 additions and 8 deletions

View file

@ -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

View file

@ -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<ShutdownGuard<AppMessage>> {
self.proxy
.send_event(EventLoopMessage::PreventShutdown)
.ok()
.map(|()| ShutdownGuard { app: self.clone() })
}
}
impl<AppMessage> Clone for App<AppMessage>
@ -540,13 +562,27 @@ where
/// A collection of open windows.
pub struct Windows<Message> {
data: Arc<Mutex<HashMap<WindowId, OpenWindow<Message>>>>,
data: Arc<Mutex<WindowsData<Message>>>,
}
struct WindowsData<Message> {
open: HashMap<WindowId, OpenWindow<Message>>,
guards: usize,
}
impl<Message> WindowsData<Message> {
fn should_shutdown(&self) -> bool {
self.open.is_empty() && self.guards == 0
}
}
impl<Message> Default for Windows<Message> {
fn default() -> Self {
Self {
data: Arc::default(),
data: Arc::new(Mutex::new(WindowsData {
open: HashMap::new(),
guards: 0,
})),
}
}
}
@ -564,7 +600,7 @@ impl<Message> Windows<Message> {
/// been opened and is still open.
pub fn get(&self, id: WindowId) -> Option<Arc<winit::window::Window>> {
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<Message> Windows<Message> {
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<Message> Windows<Message> {
fn send(&self, window: WindowId, message: WindowMessage<Message>) {
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<Message> Windows<Message> {
}
Err(mpsc::TrySendError::Disconnected(_)) => {
// Window no longer active, remove it.
data.remove(&window);
data.open.remove(&window);
}
}
}
@ -654,10 +690,21 @@ impl<Message> Windows<Message> {
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<User> {
winit: OpenedWindow,
sender: Arc<mpsc::SyncSender<WindowMessage<User>>>,
}
/// A guard preventing an [`App`] from shutting down.
pub struct ShutdownGuard<Message>
where
Message: crate::Message,
{
app: App<Message>,
}
impl<Message> Drop for ShutdownGuard<Message>
where
Message: crate::Message,
{
fn drop(&mut self) {
let _ = self.app.proxy.send_event(EventLoopMessage::AllowShutdown);
}
}

View file

@ -60,6 +60,8 @@ where
message: AppMessage,
response_sender: mpsc::SyncSender<AppMessage::Response>,
},
PreventShutdown,
AllowShutdown,
}
#[derive(Debug)]