From 9a3055934392b64858e6153635f08a31d7ffe11f Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 14 Mar 2026 19:38:41 -0700 Subject: [PATCH 1/3] handle click dock icon to reopen app for Mac --- ui/app_delegate_darwin.go | 27 ++++++++++++++++++++++++++ ui/app_delegate_darwin.m | 41 +++++++++++++++++++++++++++++++++++++++ ui/app_delegate_other.go | 8 ++++++++ ui/mainwindow.go | 3 ++- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 ui/app_delegate_darwin.go create mode 100644 ui/app_delegate_darwin.m create mode 100644 ui/app_delegate_other.go diff --git a/ui/app_delegate_darwin.go b/ui/app_delegate_darwin.go new file mode 100644 index 0000000..0138fc4 --- /dev/null +++ b/ui/app_delegate_darwin.go @@ -0,0 +1,27 @@ +//go:build darwin + +package ui + +/* +void installReopenDelegate(); +*/ +import "C" + +import "fyne.io/fyne/v2" + +var darwinAppDelegateReopenWindow fyne.Window + +func installReopenHandler(w fyne.Window) { + darwinAppDelegateReopenWindow = w + C.installReopenDelegate() +} + +//export appReopened +func appReopened() { + if darwinAppDelegateReopenWindow == nil { + return + } + go func() { + fyne.Do(darwinAppDelegateReopenWindow.Show) + }() +} diff --git a/ui/app_delegate_darwin.m b/ui/app_delegate_darwin.m new file mode 100644 index 0000000..063cdaf --- /dev/null +++ b/ui/app_delegate_darwin.m @@ -0,0 +1,41 @@ +//go:build darwin + +#import + +extern void appReopened(void); + +@interface ReopenDelegate : NSObject +@property (nonatomic, strong) id wrapped; +@end + + +@implementation ReopenDelegate + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)app + hasVisibleWindows:(BOOL)hasVisible { + if (!hasVisible) { + appReopened(); + } + if ([self.wrapped respondsToSelector:_cmd]) { + return [self.wrapped applicationShouldHandleReopen:app + hasVisibleWindows:hasVisible]; + } + return YES; +} + +- (BOOL)respondsToSelector:(SEL)sel { + return [super respondsToSelector:sel] || + [self.wrapped respondsToSelector:sel]; +} + +- (id)forwardingTargetForSelector:(SEL)sel { + return [self.wrapped respondsToSelector:sel] ? self.wrapped : nil; +} + +@end + +void installReopenDelegate(void) { + ReopenDelegate *d = [ReopenDelegate new]; + d.wrapped = [NSApp delegate]; + [NSApp setDelegate:d]; +} diff --git a/ui/app_delegate_other.go b/ui/app_delegate_other.go new file mode 100644 index 0000000..fa39367 --- /dev/null +++ b/ui/app_delegate_other.go @@ -0,0 +1,8 @@ +//go:build !darwin + +package ui + +import "fyne.io/fyne/v2" + +func installReopenHandler(w fyne.Window) { +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index ba7e2e3..6393eb2 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -61,6 +61,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, Window: fyneApp.NewWindow(displayAppName), theme: myTheme.NewMyTheme(&app.Config.Theme, app.ThemesDir()), } + installReopenHandler(m.Window) fynetooltip.SetToolTipTextSizeName(myTheme.SizeNameSubText) m.theme.NormalFont = app.Config.Application.FontNormalTTF @@ -184,7 +185,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, m.Window.SetCloseIntercept(func() { m.SaveWindowSettings() - if app.Config.Application.CloseToSystemTray && m.HaveSystemTray() { + if runtime.GOOS == "darwin" || (app.Config.Application.CloseToSystemTray && m.HaveSystemTray()) { m.Window.Hide() } else { m.Window.Close() From d43e0c126b724ab777092a570617441a6ab69ced Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Mon, 30 Mar 2026 17:57:20 -0700 Subject: [PATCH 2/3] Fix macOS quit: intercept applicationShouldTerminate: to allow real exit GLFW's applicationShouldTerminate: converts all quit paths (Cmd+Q, dock, menu bar) into window close requests and returns NSTerminateCancel, so our close-to-hide intercept was hiding the window instead of letting the app exit. Fix by implementing applicationShouldTerminate: in ReopenDelegate, setting a darwinQuitting flag before forwarding to GLFW's delegate. The close intercept checks this flag and calls Window.Close() instead of Hide() when quitting. Co-Authored-By: Claude Sonnet 4.6 --- ui/app_delegate_darwin.go | 13 +++++++++++++ ui/app_delegate_darwin.m | 11 +++++++++++ ui/app_delegate_other.go | 4 ++++ ui/mainwindow.go | 2 +- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ui/app_delegate_darwin.go b/ui/app_delegate_darwin.go index 0138fc4..7b430a8 100644 --- a/ui/app_delegate_darwin.go +++ b/ui/app_delegate_darwin.go @@ -11,6 +11,14 @@ import "fyne.io/fyne/v2" var darwinAppDelegateReopenWindow fyne.Window +// darwinQuitting is set to true when applicationShouldTerminate: fires so +// the close intercept knows to actually close instead of hide. +var darwinQuitting bool + +func isRealQuit() bool { + return darwinQuitting +} + func installReopenHandler(w fyne.Window) { darwinAppDelegateReopenWindow = w C.installReopenDelegate() @@ -25,3 +33,8 @@ func appReopened() { fyne.Do(darwinAppDelegateReopenWindow.Show) }() } + +//export appShouldTerminate +func appShouldTerminate() { + darwinQuitting = true +} diff --git a/ui/app_delegate_darwin.m b/ui/app_delegate_darwin.m index 063cdaf..67621ce 100644 --- a/ui/app_delegate_darwin.m +++ b/ui/app_delegate_darwin.m @@ -3,6 +3,7 @@ #import extern void appReopened(void); +extern void appShouldTerminate(void); @interface ReopenDelegate : NSObject @property (nonatomic, strong) id wrapped; @@ -11,6 +12,16 @@ extern void appReopened(void); @implementation ReopenDelegate +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + // Set the quitting flag before GLFW's delegate fires close requests for + // each window, so our close intercept knows to close rather than hide. + appShouldTerminate(); + if ([self.wrapped respondsToSelector:_cmd]) { + return [self.wrapped applicationShouldTerminate:sender]; + } + return NSTerminateNow; +} + - (BOOL)applicationShouldHandleReopen:(NSApplication *)app hasVisibleWindows:(BOOL)hasVisible { if (!hasVisible) { diff --git a/ui/app_delegate_other.go b/ui/app_delegate_other.go index fa39367..c48ba75 100644 --- a/ui/app_delegate_other.go +++ b/ui/app_delegate_other.go @@ -6,3 +6,7 @@ import "fyne.io/fyne/v2" func installReopenHandler(w fyne.Window) { } + +func isRealQuit() bool { + return false +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 6393eb2..61581d0 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -185,7 +185,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string, m.Window.SetCloseIntercept(func() { m.SaveWindowSettings() - if runtime.GOOS == "darwin" || (app.Config.Application.CloseToSystemTray && m.HaveSystemTray()) { + if (runtime.GOOS == "darwin" && !isRealQuit()) || (app.Config.Application.CloseToSystemTray && m.HaveSystemTray()) { m.Window.Hide() } else { m.Window.Close() From 40781f668a5faef0825f52a98b82b9f3bcfd10b5 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Mon, 30 Mar 2026 18:24:58 -0700 Subject: [PATCH 3/3] Hide window on Cmd+W for macOS (close-window-to-hide behavior) Co-Authored-By: Claude Sonnet 4.6 --- ui/mainwindow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 61581d0..e32f006 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -515,7 +515,7 @@ func (m *MainWindow) addShortcuts() { m.Controller.SelectAll() }) m.Canvas().AddShortcut(&shortcuts.ShortcutCloseWindow, func(_ fyne.Shortcut) { - if m.App.Config.Application.CloseToSystemTray && m.HaveSystemTray() { + if runtime.GOOS == "darwin" || (m.App.Config.Application.CloseToSystemTray && m.HaveSystemTray()) { m.Window.Hide() } })