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)) }) }