diff --git a/src/Languages/lang_en.json b/src/Languages/lang_en.json index 8f57852cfd..35875c11b6 100644 --- a/src/Languages/lang_en.json +++ b/src/Languages/lang_en.json @@ -2012,5 +2012,13 @@ "{0} was not found.": "{0} was not found.", "{0} was not found in {1}.": "{0} was not found in {1}.", "The package link could not be opened": "The package link could not be opened", - "A link to {0} was copied to the clipboard": "A link to {0} was copied to the clipboard" + "A link to {0} was copied to the clipboard": "A link to {0} was copied to the clipboard", + "Keep UniGetUI running in the background when its window is closed": "Keep UniGetUI running in the background when its window is closed", + "Show the UniGetUI icon in the system tray": "Show the UniGetUI icon in the system tray", + "To reopen UniGetUI, launch it again. To exit, use More > Quit UniGetUI in the navigation menu.": "To reopen UniGetUI, launch it again. To exit, use More > Quit UniGetUI in the navigation menu.", + "Notifications are not available on Linux, so you will not be told when updates are found.": "Notifications are not available on Linux, so you will not be told when updates are found.", + "UniGetUI stops checking for updates and sending notifications until you open it again.": "UniGetUI stops checking for updates and sending notifications until you open it again.", + "UniGetUI is still running": "UniGetUI is still running", + "UniGetUI keeps running in the background and checking for updates, without a system tray icon. To reopen it, launch UniGetUI again. To exit, use More > Quit UniGetUI in the navigation menu. You can change this in Settings > User interface preferences.": "UniGetUI keeps running in the background and checking for updates, without a system tray icon. To reopen it, launch UniGetUI again. To exit, use More > Quit UniGetUI in the navigation menu. You can change this in Settings > User interface preferences.", + "UniGetUI must keep running in the background when its window is closed for notifications to work": "UniGetUI must keep running in the background when its window is closed for notifications to work" } diff --git a/src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs b/src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs index 22922cc075..d5d978fbd2 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs @@ -56,7 +56,8 @@ private sealed record Entry( new("Navigation menu:", ["sidebar", "nav menu", "navigation", "layout", "docked", "overlay"], typeof(Interface_P), "NavMenuModeSelector"), new("Navigation menu order", ["sidebar", "nav menu", "navigation", "order", "reorder", "drag", "tabs", "pages", "shortcuts"], typeof(Interface_P), "NavigationOrderCard"), new("Automatically switch to software rendering when Windows has no hardware GPU", ["rendering", "gpu", "software rendering"], typeof(Interface_P), "InterfaceRenderingCard"), - new("Close UniGetUI to the system tray", ["system tray", "background", "minimize to tray"], typeof(Interface_P), "DisableSystemTrayCard"), + new("Keep UniGetUI running in the background when its window is closed", ["system tray", "background", "minimize to tray", "close", "quit"], typeof(Interface_P), "DisableSystemTrayCard"), + new("Show the UniGetUI icon in the system tray", ["system tray", "tray icon", "hide icon", "notification area"], typeof(Interface_P), "SystemTrayIconCard"), new("Manage UniGetUI autostart behaviour", ["autostart", "run at login", "startup"], typeof(Interface_P), "EditAutostartSettings"), new("Show package icons on package lists", ["package icons"], typeof(Interface_P), "InterfacePackageListsCard"), new("Show illustrations on package lists", ["illustrations", "package illustrations"], typeof(Interface_P), "PackageIllustrationsCard"), diff --git a/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs b/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs index a42a4d1eaf..280e45cc3d 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs @@ -63,7 +63,7 @@ public void UpdateStatus() { try { - _trayIcon.IsVisible = !Settings.Get(Settings.K.DisableSystemTray); + _trayIcon.IsVisible = Settings.IsSystemTrayIconVisible(); string status; string tooltip; diff --git a/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/NotificationsViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/NotificationsViewModel.cs index 76dc13883d..06893fb800 100644 --- a/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/NotificationsViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/NotificationsViewModel.cs @@ -7,11 +7,11 @@ namespace UniGetUI.Avalonia.ViewModels.Pages.SettingsPages; public partial class NotificationsViewModel : ViewModelBase { - [ObservableProperty] private bool _isSystemTrayEnabled; + [ObservableProperty] private bool _isBackgroundModeEnabled; [ObservableProperty] private bool _isNotificationsEnabled; - /// True when the system-tray-disabled warning should be shown. - public bool IsSystemTrayWarningVisible => !IsSystemTrayEnabled; + /// True when closing the window quits UniGetUI, which turns notifications off. + public bool IsSystemTrayWarningVisible => !IsBackgroundModeEnabled; // Per-type notifications are delivered only by the Windows/macOS bridges; Linux has no // delivery path, so the whole "Notification types" group is hidden there. @@ -20,7 +20,7 @@ public partial class NotificationsViewModel : ViewModelBase public NotificationsViewModel() { - _isSystemTrayEnabled = !CoreSettings.Get(CoreSettings.K.DisableSystemTray); + _isBackgroundModeEnabled = !CoreSettings.Get(CoreSettings.K.DisableSystemTray); _isNotificationsEnabled = !CoreSettings.Get(CoreSettings.K.DisableNotifications); } diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index f8b36c3c9d..9d755bc76c 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -159,6 +159,7 @@ public partial class MainWindow : Window private bool _maxButtonPressed; private TrayService? _trayService; private bool _allowClose; + private bool _showingHiddenTrayIconNotice; private readonly ApplicationShutdownCoordinator _shutdownCoordinator = new(); // Saved outer size (DIPs) awaiting a native, exact restore in OnOpened on Windows. @@ -307,6 +308,19 @@ protected override void OnClosing(WindowClosingEventArgs e) if (!_allowClose && (OperatingSystem.IsMacOS() || !Settings.Get(Settings.K.DisableSystemTray))) { e.Cancel = true; + if (_showingHiddenTrayIconNotice) + return; + + // Without a tray icon nothing on screen shows that UniGetUI is still running, so say it once. + // macOS keeps the app in the Dock, which already makes that visible. + if (!OperatingSystem.IsMacOS() + && !Settings.IsSystemTrayIconVisible() + && !Settings.Get(Settings.K.ShownHiddenTrayIconNotice)) + { + _ = ShowHiddenTrayIconNoticeThenHideAsync(); + return; + } + ViewModel.ClearAllSearchQueries(); Hide(); return; @@ -316,6 +330,46 @@ protected override void OnClosing(WindowClosingEventArgs e) QuitApplication(); } + private async Task ShowHiddenTrayIconNoticeThenHideAsync() + { + _showingHiddenTrayIconNotice = true; + try + { + Settings.Set(Settings.K.ShownHiddenTrayIconNotice, true); + var dialog = new ImmersiveConfirmationDialog( + CoreTools.Translate("UniGetUI is still running"), + new TextBlock + { + Text = CoreTools.Translate("UniGetUI keeps running in the background and checking for updates, without a system tray icon. To reopen it, launch UniGetUI again. To exit, use More > Quit UniGetUI in the navigation menu. You can change this in Settings > User interface preferences."), + FontSize = 14, + LineHeight = 20, + TextWrapping = TextWrapping.Wrap, + }, + CoreTools.Translate("OK"), + CoreTools.Translate("Quit UniGetUI")); + await dialog.ShowDialog(this); + + if (dialog.Result is false) + { + QuitApplication(); + return; + } + + ViewModel.ClearAllSearchQueries(); + Hide(); + } + catch (Exception ex) + { + Logger.Error("Could not show the background notice:"); + Logger.Error(ex); + Hide(); + } + finally + { + _showingHiddenTrayIconNotice = false; + } + } + private void ReleaseWindowResources() { SaveGeometryNow(); diff --git a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml index 6242a1a7b4..5c9f82c584 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml +++ b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml @@ -74,10 +74,18 @@ + + MainWindow.Instance?.RefreshNavigationMode(); + DisableSystemTrayCard.StateChanged += (_, _) => UpdateSystemTrayCards(); + SystemTrayIconCard.StateChanged += (_, _) => UpdateSystemTrayCards(); + UpdateSystemTrayCards(); + NavigationOrderCard.Description = CoreTools.Translate( "Drag the pages in the navigation menu, or press {0} on a focused page, to reorder them. Keyboard shortcuts follow the same order.", OperatingSystem.IsMacOS() ? "Option+Up/Down" : "Alt+Up/Down"); NavigationOrderCard.Click += (_, _) => MainWindow.Instance?.ResetNavigationOrder(); } + + // The icon only exists while UniGetUI runs in the background, so its checkbox follows the first one, + // and each card explains what its unchecked state means for the user. + private void UpdateSystemTrayCards() + { + bool keepsRunning = !CoreSettings.Get(CoreSettings.K.DisableSystemTray); + bool iconHidden = CoreSettings.Get(CoreSettings.K.HideSystemTrayIcon); + + DisableSystemTrayCard.WarningText = keepsRunning + ? "" + : CoreTools.Translate("UniGetUI stops checking for updates and sending notifications until you open it again."); + + SystemTrayIconCard.IsEnabled = keepsRunning; + string iconHint = ""; + if (keepsRunning && iconHidden) + { + iconHint = CoreTools.Translate("To reopen UniGetUI, launch it again. To exit, use More > Quit UniGetUI in the navigation menu."); + if (OperatingSystem.IsLinux()) + iconHint += " " + CoreTools.Translate("Notifications are not available on Linux, so you will not be told when updates are found."); + } + SystemTrayIconCard.WarningText = iconHint; + } } diff --git a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml index 968119aa91..2748cf5a73 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml +++ b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Notifications.axaml @@ -19,8 +19,10 @@ Margin="44,32,4,8" automation:AutomationProperties.HeadingLevel="2"/> - - +