From 861ddea1b286bb0c07c7d0fa4b7cec4f053ac5bf Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 17 Jan 2024 20:45:30 +1100 Subject: [PATCH] Add option to prevent application shutdown This update allows the application shutdown to be cancelled. It implements the shouldQuit function, which can prevent the application from quitting if it returns false. Additional checks have been added to ensure cleanup and quit processes are performed only if required. The darwin delegate and linux and windows applications were modified to include this new functionality. --- v3/examples/window/main.go | 9 +++++--- v3/pkg/application/application.go | 21 +++++++++++++------ v3/pkg/application/application_darwin.go | 12 ++++++++--- .../application/application_darwin_delegate.h | 1 + .../application/application_darwin_delegate.m | 13 +++++++++--- v3/pkg/application/application_linux.go | 4 ++++ v3/pkg/application/application_windows.go | 4 ++++ v3/pkg/application/menuitem_windows.go | 6 ++++-- v3/pkg/application/options_application.go | 7 ++++++- v3/pkg/application/webview_window_darwin.go | 7 +++++++ 10 files changed, 66 insertions(+), 18 deletions(-) diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index 609051bc..241e8151 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -21,7 +21,7 @@ func main() { Description: "A demo of the WebviewWindow API", Assets: application.AlphaAssets, Mac: application.MacOptions{ - ApplicationShouldTerminateAfterLastWindowClosed: false, + ApplicationShouldTerminateAfterLastWindowClosed: true, }, }) app.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) { @@ -40,8 +40,11 @@ func main() { // Create a custom menu menu := app.NewMenu() - menu.AddRole(application.AppMenu) - + if runtime.GOOS == "darwin" { + menu.AddRole(application.AppMenu) + } else { + menu.AddRole(application.FileMenu) + } windowCounter := 1 // Let's make a "Demo" menu diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 6a126939..ef3f8e63 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -297,6 +297,7 @@ type App struct { // Shutdown performingShutdown bool + // Shutdown tasks are run when the application is shutting down. // They are run in the order they are added and run on the main thread. // The application option `OnShutdown` is run first. @@ -617,12 +618,11 @@ func (a *App) OnShutdown(f func()) { a.shutdownTasks = append(a.shutdownTasks, f) } -func (a *App) Quit() { +func (a *App) cleanup() { if a.performingShutdown { return } a.performingShutdown = true - // Run the shutdown tasks for _, shutdownTask := range a.shutdownTasks { InvokeSync(shutdownTask) } @@ -639,13 +639,15 @@ func (a *App) Quit() { } a.systemTrays = nil a.systemTraysLock.Unlock() - if a.impl != nil { - a.impl.destroy() - a.impl = nil - } }) } +func (a *App) Quit() { + if a.impl != nil { + InvokeSync(a.impl.destroy) + } +} + func (a *App) SetIcon(icon []byte) { if a.impl != nil { a.impl.setIcon(icon) @@ -867,3 +869,10 @@ func (a *App) Environment() EnvironmentInfo { Debug: a.isDebugMode, } } + +func (a *App) shouldQuit() bool { + if a.options.ShouldQuit != nil { + return a.options.ShouldQuit() + } + return true +} diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index 26b49dba..c540e95f 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -334,9 +334,15 @@ func processMenuItemClick(menuID C.uint) { menuItemClicked <- uint(menuID) } -//export quitApplication -func quitApplication() { - globalApplication.Quit() +//export shouldQuitApplication +func shouldQuitApplication() C.bool { + // TODO: This should be configurable + return C.bool(globalApplication.shouldQuit()) +} + +//export cleanup +func cleanup() { + globalApplication.cleanup() } func (a *App) logPlatformInfo() { diff --git a/v3/pkg/application/application_darwin_delegate.h b/v3/pkg/application/application_darwin_delegate.h index 9c11eb8a..f60dfd0b 100644 --- a/v3/pkg/application/application_darwin_delegate.h +++ b/v3/pkg/application/application_darwin_delegate.h @@ -7,6 +7,7 @@ @interface AppDelegate : NSObject @property bool shouldTerminateWhenLastWindowClosed; +@property bool shuttingDown; - (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; @end diff --git a/v3/pkg/application/application_darwin_delegate.m b/v3/pkg/application/application_darwin_delegate.m index 2573a2c9..aae5b0bd 100644 --- a/v3/pkg/application/application_darwin_delegate.m +++ b/v3/pkg/application/application_darwin_delegate.m @@ -2,7 +2,8 @@ #import "application_darwin_delegate.h" #import "../events/events_darwin.h" extern bool hasListeners(unsigned int); -extern void quitApplication(); +extern bool shouldQuitApplication(); +extern void cleanup(); @implementation AppDelegate - (void)dealloc @@ -20,8 +21,14 @@ extern void quitApplication(); } } - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - quitApplication(); - return NSTerminateCancel; + if( ! shouldQuitApplication() ) { + return NSTerminateCancel; + } + if( !self.shuttingDown ) { + self.shuttingDown = true; + cleanup(); + } + return NSTerminateNow; } - (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 0d54df1f..f098a1fc 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -116,6 +116,10 @@ func (m *linuxApp) run() error { } func (m *linuxApp) destroy() { + if !globalApplication.shouldQuit() { + return + } + globalApplication.cleanup() appDestroy(m.application) } diff --git a/v3/pkg/application/application_windows.go b/v3/pkg/application/application_windows.go index c9bccaad..c4811f08 100644 --- a/v3/pkg/application/application_windows.go +++ b/v3/pkg/application/application_windows.go @@ -192,6 +192,10 @@ func (m *windowsApp) run() error { } func (m *windowsApp) destroy() { + if !globalApplication.shouldQuit() { + return + } + globalApplication.cleanup() // Post a quit message to the main thread w32.PostQuitMessage(0) } diff --git a/v3/pkg/application/menuitem_windows.go b/v3/pkg/application/menuitem_windows.go index abea7a17..38aa25df 100644 --- a/v3/pkg/application/menuitem_windows.go +++ b/v3/pkg/application/menuitem_windows.go @@ -190,8 +190,10 @@ func newDeleteMenuItem() *MenuItem { } func newQuitMenuItem() *MenuItem { - panic("implement me") - + return newMenuItem("Quit"). + OnClick(func(ctx *Context) { + globalApplication.Quit() + }) } func newSelectAllMenuItem() *MenuItem { diff --git a/v3/pkg/application/options_application.go b/v3/pkg/application/options_application.go index 87f45c32..ca9c4c6b 100644 --- a/v3/pkg/application/options_application.go +++ b/v3/pkg/application/options_application.go @@ -52,10 +52,15 @@ type Options struct { // KeyBindings is a map of key bindings to functions KeyBindings map[string]func(window *WebviewWindow) - // OnShutdown is called when the application is about to quit. + // OnShutdown is called when the application is about to terminate. // This is useful for cleanup tasks. // The shutdown process blocks until this function returns OnShutdown func() + + // ShouldQuit is a function that is called when the user tries to quit the application. + // If the function returns true, the application will quit. + // If the function returns false, the application will not quit. + ShouldQuit func() bool } // AssetOptions defines the configuration of the AssetServer. diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 61bbf8ab..448332e1 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -956,7 +956,14 @@ func (w *macosWebviewWindow) setEnabled(enabled bool) { } func (w *macosWebviewWindow) execJS(js string) { + InvokeAsync(func() { + if globalApplication.performingShutdown { + return + } + if w.nsWindow == nil { + return + } C.windowExecJS(w.nsWindow, C.CString(js)) }) }