diff --git a/GalaxyBudsClient/App.axaml.cs b/GalaxyBudsClient/App.axaml.cs index 638d57c7..212c737f 100644 --- a/GalaxyBudsClient/App.axaml.cs +++ b/GalaxyBudsClient/App.axaml.cs @@ -67,13 +67,9 @@ public override void Initialize() #if OSX NSApplication.Init(); - NSApplication.Notifications.ObserveDidBecomeActive((_, _) => - { - Dispatcher.UIThread.InvokeAsync(delegate - { - MainWindow.Instance.BringToFront(); - }); - }); + // For menu bar applications (LSUIElement=true), hide the dock icon immediately at startup. + // The dock icon will only appear when the settings window is explicitly opened. + GalaxyBudsClient.Platform.OSX.AppUtils.setHideInDock(true); #endif AvaloniaXamlLoader.Load(this); @@ -108,8 +104,16 @@ public override void OnFrameworkInitializationCompleted() { // Initialize MainWindow singleton var mainWindow = MainWindow.Instance; + +#if OSX + // On macOS with LSUIElement=true, always start as a menu bar app (no main window attached initially) + // The window will be shown when the user clicks the tray icon + desktop.MainWindow = null; + mainWindow.IsVisible = false; +#else // Stay initially minimized: don't attach a main window desktop.MainWindow = StartMinimized ? null : mainWindow; +#endif TrayManager.Init(); BatteryHistoryManager.Init(); @@ -353,4 +357,4 @@ private async void HandleOtherTouchOption(object? sender, TouchOptions e) break; } } -} \ No newline at end of file +} diff --git a/GalaxyBudsClient/Info.plist b/GalaxyBudsClient/Info.plist index 44b86169..8aeebb94 100755 --- a/GalaxyBudsClient/Info.plist +++ b/GalaxyBudsClient/Info.plist @@ -8,5 +8,7 @@ Galaxy Buds Manager CFBundleDisplayName Galaxy Buds Manager + LSUIElement + diff --git a/GalaxyBudsClient/Interface/MainWindow.axaml.cs b/GalaxyBudsClient/Interface/MainWindow.axaml.cs index 2fed110d..be8b0ce6 100644 --- a/GalaxyBudsClient/Interface/MainWindow.axaml.cs +++ b/GalaxyBudsClient/Interface/MainWindow.axaml.cs @@ -59,6 +59,23 @@ private void OnLanguageUpdated() protected override async void OnClosing(WindowClosingEventArgs e) { +#if OSX + // On macOS with LSUIElement, minimize to tray if setting is enabled + if (Settings.Data.MinimizeToTray && e.CloseReason is not (WindowCloseReason.OSShutdown or WindowCloseReason.ApplicationShutdown)) + { + BringToTray(); + e.Cancel = true; + Log.Debug("MainWindow.OnClosing: macOS menu bar app - minimized to tray"); + base.OnClosing(e); + return; + } + + // If MinimizeToTray is off, quit the app when closing the window + if (!Settings.Data.MinimizeToTray) + { + Log.Debug("MainWindow.OnClosing: macOS - MinimizeToTray disabled, closing app"); + } +#else if (Settings.Data.MinimizeToTray && PlatformUtils.SupportsTrayIcon) { // check if the cause of the termination is due to shutdown or application close request @@ -77,6 +94,7 @@ protected override async void OnClosing(WindowClosingEventArgs e) { Log.Debug("MainWindow.OnClosing: Now closing session"); } +#endif await BluetoothImpl.Instance.SendRequestAsync(MsgIds.FIND_MY_EARBUDS_STOP); await BluetoothImpl.Instance.DisconnectAsync(); @@ -124,26 +142,32 @@ public void BringToFront() { Dispatcher.UIThread.InvokeAsync(() => { +#if OSX + // On macOS, show in dock first, then make window visible + GalaxyBudsClient.Platform.OSX.AppUtils.setHideInDock(false); +#endif + if (App.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow == null) { desktop.MainWindow = this; } - if (WindowState == WindowState.Minimized) - { - WindowState = WindowState.Normal; - } + // Ensure window state is normal before showing + WindowState = WindowState.Normal; if (PlatformUtils.IsLinux) { IsVisible = false; // Workaround for some Linux DMs } + IsVisible = true; + #if OSX - GalaxyBudsClient.Platform.OSX.AppUtils.setHideInDock(false); + // On macOS, re-apply theme to restore blur/transparency effects + // This is needed because effects don't persist when window is hidden/shown + (this as IStyledWindow).ApplyTheme(this); #endif - IsVisible = true; Activate(); Topmost = true; @@ -172,11 +196,22 @@ public void ToggleVisibility() private void BringToTray() { #if OSX + // On macOS, we need to hide the window completely without minimizing to dock + // First hide visibility, then hide from dock + IsVisible = false; + ShowInTaskbar = false; GalaxyBudsClient.Platform.OSX.AppUtils.setHideInDock(true); -#endif + + // Detach the main window so macOS doesn't try to show the app in dock + if (App.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = null; + } +#else ShowInTaskbar = false; WindowState = WindowState.Minimized; IsVisible = false; +#endif } private void OnBluetoothError(object? sender, BluetoothException e) @@ -195,4 +230,4 @@ private void OnBluetoothError(object? sender, BluetoothException e) } }); } -} \ No newline at end of file +}