Ver código fonte

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

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Wouter Buckens 4 anos atrás
pai
commit
892c63a053

+ 7 - 0
.changes/exit-requested-event.md

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

+ 12 - 4
core/tauri-runtime-wry/src/lib.rs

@@ -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")]

+ 11 - 0
core/tauri-runtime/src/lib.rs

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

+ 41 - 9
core/tauri/src/app.rs

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