Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/Languages/lang_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
3 changes: 2 additions & 1 deletion src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
2 changes: 1 addition & 1 deletion src/UniGetUI.Avalonia/Infrastructure/TrayService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void UpdateStatus()
{
try
{
_trayIcon.IsVisible = !Settings.Get(Settings.K.DisableSystemTray);
_trayIcon.IsVisible = Settings.IsSystemTrayIconVisible();

string status;
string tooltip;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>True when the system-tray-disabled warning should be shown.</summary>
public bool IsSystemTrayWarningVisible => !IsSystemTrayEnabled;
/// <summary>True when closing the window quits UniGetUI, which turns notifications off.</summary>
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.
Expand All @@ -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);
}

Expand Down
54 changes: 54 additions & 0 deletions src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,18 @@

<settings:CheckboxCard x:Name="DisableSystemTrayCard"
SettingName="DisableSystemTray"
Text="{t:Translate Close UniGetUI to the system tray}"
Text="{t:Translate Keep UniGetUI running in the background when its window is closed}"
StateChangedCommand="{Binding RefreshSystemTrayCommand}"
CornerRadius="8,8,0,0"/>

<settings:CheckboxCard x:Name="SystemTrayIconCard"
SettingName="HideSystemTrayIcon"
ForceInversion="True"
Text="{t:Translate Show the UniGetUI icon in the system tray}"
StateChangedCommand="{Binding RefreshSystemTrayCommand}"
CornerRadius="0"
BorderThickness="1,0,1,1"/>

<settings:ButtonCard x:Name="EditAutostartSettings"
CornerRadius="0,0,8,8"
BorderThickness="1,0,1,1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,35 @@ public Interface_P()
NavMenuModeSelector.ShowAddedItems();
NavMenuModeSelector.ValueChanged += (_, _) => 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
Margin="44,32,4,8"
automation:AutomationProperties.HeadingLevel="2"/>

<!-- Warning shown when system tray is disabled (Windows-only) -->
<TextBlock Text="{t:Translate The system tray icon must be enabled in order for notifications to work}"
<!-- Warning shown when closing the window quits UniGetUI (the tray icon itself is not needed) -->
<TextBlock Text="{t:Translate UniGetUI must keep running in the background when its window is closed for notifications to work}"
TextWrapping="Wrap"
TextAlignment="Center"
FontSize="14"
Margin="0,0,0,8"
HorizontalAlignment="Center"
Expand Down
35 changes: 35 additions & 0 deletions src/UniGetUI.Core.Settings.Tests/SettingsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ public void TestExistingSettingsMigrateToNewDirectory()
Assert.False(File.Exists(oldPath));
}

[Theory]
[InlineData(false, false, true)]
[InlineData(false, true, false)]
[InlineData(true, false, false)]
[InlineData(true, true, false)]
public void TrayIconShowsOnlyWhileRunningInTheBackgroundAndNotHidden(
bool disableSystemTray,
bool hideSystemTrayIcon,
bool expectedVisible)
{
Settings.Set(Settings.K.DisableSystemTray, disableSystemTray);
Settings.Set(Settings.K.HideSystemTrayIcon, hideSystemTrayIcon);

Assert.Equal(expectedVisible, Settings.IsSystemTrayIconVisible());

Settings.Set(Settings.K.DisableSystemTray, false);
Settings.Set(Settings.K.HideSystemTrayIcon, false);
}

[Fact]
public void HidingTheTrayIconKeepsNotificationsEnabled()
{
Settings.Set(Settings.K.DisableNotifications, false);
Settings.Set(Settings.K.DisableSystemTray, false);

Settings.Set(Settings.K.HideSystemTrayIcon, true);
Assert.False(Settings.AreNotificationsDisabled());

Settings.Set(Settings.K.DisableSystemTray, true);
Assert.True(Settings.AreNotificationsDisabled());

Settings.Set(Settings.K.DisableSystemTray, false);
Settings.Set(Settings.K.HideSystemTrayIcon, false);
}

[Theory]
[InlineData(Settings.K.Test1, true, false, false, true)]
[InlineData(Settings.K.Test2, true, false, false, false)]
Expand Down
7 changes: 7 additions & 0 deletions src/UniGetUI.Core.Settings/SettingsEngine_Extras.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ public partial class Settings
*
*/

// DisableSystemTray means closing the window quits, which has always hidden the icon too.
// HideSystemTrayIcon only hides the icon while UniGetUI keeps running in the background.
public static bool IsSystemTrayIconVisible() =>
!Get(K.DisableSystemTray) && !Get(K.HideSystemTrayIcon);

// Notifications depend on the process staying alive after the window closes, not on the
// tray icon, so hiding the icon alone leaves them enabled.
public static bool AreNotificationsDisabled() =>
Get(K.DisableSystemTray) || Get(K.DisableNotifications);

Expand Down
4 changes: 4 additions & 0 deletions src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public enum K
TransferredOldSettings,
DisableApi,
DisableSystemTray,
HideSystemTrayIcon,
ShownHiddenTrayIconNotice,
MaintainSuccessfulInstalls,
AutomaticallyUpdatePackages,
DisableAUPOnMeteredConnections,
Expand Down Expand Up @@ -175,6 +177,8 @@ public static string ResolveKey(K key)
K.TransferredOldSettings => "TransferredOldSettings",
K.DisableApi => "DisableApi",
K.DisableSystemTray => "DisableSystemTray",
K.HideSystemTrayIcon => "HideSystemTrayIcon",
K.ShownHiddenTrayIconNotice => "ShownHiddenTrayIconNotice",
K.MaintainSuccessfulInstalls => "MaintainSuccessfulInstalls",
K.AutomaticallyUpdatePackages => "AutomaticallyUpdatePackages",
K.DisableAUPOnMeteredConnections => "DisableAUPOnMeteredConnections",
Expand Down
6 changes: 5 additions & 1 deletion src/UniGetUI.Tui/Views/Pages/SettingsPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,11 @@ private void BuildInterface()
Check("Use the system UI font" + _desktopOnly, Settings.K.UseSystemUIFont);
Choice("Navigation menu" + _desktopOnly, Settings.K.NavMenuMode,
[new(CoreTools.Translate("Automatic"), "auto"), new(CoreTools.Translate("Docked"), "docked"), new(CoreTools.Translate("Overlay"), "overlay")]);
Check("Close UniGetUI to the system tray" + _desktopOnly, Settings.K.DisableSystemTray, inverted: true);
Check("Keep UniGetUI running in the background when its window is closed" + _desktopOnly, Settings.K.DisableSystemTray, inverted: true);
_form.AddCheck(CoreTools.Translate("Show the UniGetUI icon in the system tray") + _desktopOnly,
() => !Settings.Get(Settings.K.HideSystemTrayIcon),
v => Settings.Set(Settings.K.HideSystemTrayIcon, !v),
enabled: () => !Settings.Get(Settings.K.DisableSystemTray));
Check("Show package icons on package lists" + _desktopOnly, Settings.K.DisableIconsOnPackageLists, inverted: true);
Check("Show illustrations on empty package lists" + _desktopOnly, Settings.K.DisablePackageIllustrations, inverted: true);
Check("Use software rendering automatically on hosts without a GPU" + _desktopOnly, Settings.K.DisableAutoSoftwareRenderingOnGpuLessHosts, inverted: true);
Expand Down
Loading