feat(#2287): Add ExitRequested event to let users prevent app from exiting (#2293)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Wouter Buckens 2021-08-09 02:12:32 +02:00 committed by GitHub
parent fd80dd97e8
commit 892c63a053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 13 deletions

View File

@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
---
Add `ExitRequested` event that allows preventing the app from exiting when all windows are closed, and an `AppHandle.exit()` function to exit the app manually.

View File

@ -13,8 +13,8 @@ use tauri_runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
},
ClipboardManager, Dispatch, Error, GlobalShortcutManager, Icon, Result, RunEvent, RunIteration,
Runtime, RuntimeHandle, UserAttentionType,
ClipboardManager, Dispatch, Error, ExitRequestedEventAction, GlobalShortcutManager, Icon, Result,
RunEvent, RunIteration, Runtime, RuntimeHandle, UserAttentionType,
};
#[cfg(feature = "menu")]
@ -2063,8 +2063,16 @@ fn on_window_close<'a>(
callback(RunEvent::WindowClose(webview.label));
}
if windows.is_empty() {
*control_flow = ControlFlow::Exit;
callback(RunEvent::Exit);
let (tx, rx) = channel();
callback(RunEvent::ExitRequested { tx });
let recv = rx.try_recv();
let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
if !should_prevent {
*control_flow = ControlFlow::Exit;
callback(RunEvent::Exit);
}
}
// TODO: tao does not fire the destroyed event properly
#[cfg(target_os = "linux")]

View File

@ -170,6 +170,10 @@ impl Icon {
pub enum RunEvent {
/// Event loop is exiting.
Exit,
/// Event loop is about to exit
ExitRequested {
tx: Sender<ExitRequestedEventAction>,
},
/// Window close was requested by the user.
CloseRequested {
/// The window label.
@ -181,6 +185,13 @@ pub enum RunEvent {
WindowClose(String),
}
/// Action to take when the event loop is about to exit
#[derive(Debug)]
pub enum ExitRequestedEventAction {
/// Prevent the event loop from exiting
Prevent,
}
/// A system tray event.
pub enum SystemTrayEvent {
MenuItemClick(u16),

View File

@ -15,7 +15,7 @@ use crate::{
runtime::{
webview::{CustomProtocol, WebviewAttributes, WindowBuilder},
window::{PendingWindow, WindowEvent},
Dispatch, RunEvent, Runtime,
Dispatch, ExitRequestedEventAction, RunEvent, Runtime,
},
sealed::{ManagerBase, RuntimeOrDispatch},
Context, Invoke, InvokeError, Manager, StateManager, Window,
@ -47,7 +47,19 @@ pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(GlobalWindowEvent<R>)
#[cfg(feature = "system-tray")]
type SystemTrayEventListener<R> = Box<dyn Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync>;
/// Api exposed on the `ExitRequested` event.
#[derive(Debug)]
pub struct ExitRequestApi(Sender<ExitRequestedEventAction>);
impl ExitRequestApi {
/// Prevents the app from exiting
pub fn prevent_exit(&self) {
self.0.send(ExitRequestedEventAction::Prevent).unwrap();
}
}
/// Api exposed on the `CloseRequested` event.
#[derive(Debug)]
pub struct CloseRequestApi(Sender<bool>);
impl CloseRequestApi {
@ -58,10 +70,17 @@ impl CloseRequestApi {
}
/// An application event, triggered from the event loop.
#[derive(Debug)]
#[non_exhaustive]
pub enum Event {
/// Event loop is exiting.
Exit,
/// The app is about to exit
#[non_exhaustive]
ExitRequested {
/// Event API
api: ExitRequestApi,
},
/// Window close was requested by the user.
#[non_exhaustive]
CloseRequested {
@ -223,6 +242,23 @@ impl<R: Runtime> AppHandle<R> {
.register(plugin);
Ok(())
}
/// Exits the app
pub fn exit(&self, exit_code: i32) {
std::process::exit(exit_code);
}
/// Runs necessary cleanup tasks before exiting the process
fn cleanup_before_exit(&self) {
#[cfg(shell_execute)]
{
crate::api::process::kill_children();
}
#[cfg(all(windows, feature = "system-tray"))]
{
let _ = self.remove_system_tray();
}
}
}
impl<R: Runtime> Manager<R> for AppHandle<R> {}
@ -356,14 +392,7 @@ impl<R: Runtime> App<R> {
let manager = self.manager.clone();
self.runtime.take().unwrap().run(move |event| match event {
RunEvent::Exit => {
#[cfg(shell_execute)]
{
crate::api::process::kill_children();
}
#[cfg(all(windows, feature = "system-tray"))]
{
let _ = app_handle.remove_system_tray();
}
app_handle.cleanup_before_exit();
callback(&app_handle, Event::Exit);
}
_ => {
@ -372,6 +401,9 @@ impl<R: Runtime> App<R> {
&app_handle,
match event {
RunEvent::Exit => Event::Exit,
RunEvent::ExitRequested { tx } => Event::ExitRequested {
api: ExitRequestApi(tx),
},
RunEvent::CloseRequested { label, signal_tx } => Event::CloseRequested {
label: label.parse().unwrap_or_else(|_| unreachable!()),
api: CloseRequestApi(signal_tx),