Add QuitMode policy to GPUI (#42391)

Applications can select a policy for when the app quits using the new
function `Application::with_quit_mode`:
- Only on explicit calls to `App::quit`
- When the last window is closed
- Platform default (former on macOS, latter everywhere else) 

Release Notes:

- N/A
This commit is contained in:
John Tur
2025-11-10 16:45:43 -05:00
committed by GitHub
parent 32ec1037e1
commit 112b5c16b7
2 changed files with 63 additions and 24 deletions
+37
View File
@@ -169,6 +169,13 @@ impl Application {
self
}
/// Configures when the application should automatically quit.
/// By default, [`QuitMode::Default`] is used.
pub fn with_quit_mode(self, mode: QuitMode) -> Self {
self.0.borrow_mut().quit_mode = mode;
self
}
/// Start the application. The provided callback will be called once the
/// app is fully launched.
pub fn run<F>(self, on_finish_launching: F)
@@ -238,6 +245,18 @@ type WindowClosedHandler = Box<dyn FnMut(&mut App)>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut App) + 'static>;
type NewEntityListener = Box<dyn FnMut(AnyEntity, &mut Option<&mut Window>, &mut App) + 'static>;
/// Defines when the application should automatically quit.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum QuitMode {
/// Use [`QuitMode::Explicit`] on macOS and [`QuitMode::LastWindowClosed`] on other platforms.
#[default]
Default,
/// Quit automatically when the last window is closed.
LastWindowClosed,
/// Quit only when requested via [`App::quit`].
Explicit,
}
#[doc(hidden)]
#[derive(Clone, PartialEq, Eq)]
pub struct SystemWindowTab {
@@ -588,6 +607,7 @@ pub struct App {
pub(crate) inspector_element_registry: InspectorElementRegistry,
#[cfg(any(test, feature = "test-support", debug_assertions))]
pub(crate) name: Option<&'static str>,
quit_mode: QuitMode,
quitting: bool,
}
@@ -659,6 +679,7 @@ impl App {
inspector_renderer: None,
#[cfg(any(feature = "inspector", debug_assertions))]
inspector_element_registry: InspectorElementRegistry::default(),
quit_mode: QuitMode::default(),
quitting: false,
#[cfg(any(test, feature = "test-support", debug_assertions))]
@@ -1172,6 +1193,12 @@ impl App {
self.http_client = new_client;
}
/// Configures when the application should automatically quit.
/// By default, [`QuitMode::Default`] is used.
pub fn set_quit_mode(&mut self, mode: QuitMode) {
self.quit_mode = mode;
}
/// Returns the SVG renderer used by the application.
pub fn svg_renderer(&self) -> SvgRenderer {
self.svg_renderer.clone()
@@ -1379,6 +1406,16 @@ impl App {
callback(cx);
true
});
let quit_on_empty = match cx.quit_mode {
QuitMode::Explicit => false,
QuitMode::LastWindowClosed => true,
QuitMode::Default => !cfg!(macos),
};
if quit_on_empty && cx.windows.is_empty() {
cx.quit();
}
} else {
cx.windows.get_mut(id)?.replace(window);
}
+26 -24
View File
@@ -15,7 +15,7 @@ use extension::ExtensionHostProxy;
use fs::{Fs, RealFs};
use futures::{StreamExt, channel::oneshot, future};
use git::GitHostingProviderRegistry;
use gpui::{App, AppContext, Application, AsyncApp, Focusable as _, UpdateGlobal as _};
use gpui::{App, AppContext, Application, AsyncApp, Focusable as _, QuitMode, UpdateGlobal as _};
use gpui_tokio::Tokio;
use language::LanguageRegistry;
@@ -87,31 +87,33 @@ fn files_not_created_on_launch(errors: HashMap<io::ErrorKind, Vec<&Path>>) {
.collect::<Vec<_>>().join("\n\n");
eprintln!("{message}: {error_details}");
Application::new().run(move |cx| {
if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |_, cx| {
cx.new(|_| gpui::Empty)
}) {
window
.update(cx, |_, window, cx| {
let response = window.prompt(
gpui::PromptLevel::Critical,
message,
Some(&error_details),
&["Exit"],
cx,
);
Application::new()
.with_quit_mode(QuitMode::Explicit)
.run(move |cx| {
if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |_, cx| {
cx.new(|_| gpui::Empty)
}) {
window
.update(cx, |_, window, cx| {
let response = window.prompt(
gpui::PromptLevel::Critical,
message,
Some(&error_details),
&["Exit"],
cx,
);
cx.spawn_in(window, async move |_, cx| {
response.await?;
cx.update(|_, cx| cx.quit())
cx.spawn_in(window, async move |_, cx| {
response.await?;
cx.update(|_, cx| cx.quit())
})
.detach_and_log_err(cx);
})
.detach_and_log_err(cx);
})
.log_err();
} else {
fail_to_open_window(anyhow::anyhow!("{message}: {error_details}"), cx)
}
})
.log_err();
} else {
fail_to_open_window(anyhow::anyhow!("{message}: {error_details}"), cx)
}
})
}
fn fail_to_open_window_async(e: anyhow::Error, cx: &mut AsyncApp) {