Make system tray icon optional

This commit is contained in:
Burak Kaan Köse
2026-04-19 10:47:42 +02:00
parent 9cc6b03f61
commit bfbc3d40b3
4 changed files with 80 additions and 15 deletions
@@ -62,6 +62,11 @@ public interface IPreferencesService : INotifyPropertyChanged
/// </summary>
bool IsStoreUpdateNotificationsEnabled { get; set; }
/// <summary>
/// Setting: Whether the system tray icon should be created while the shell is available.
/// </summary>
bool IsSystemTrayIconEnabled { get; set; }
/// <summary>
/// Setting: Whether the Wino account profile button in the shell title bar should be hidden.
/// </summary>
+63 -16
View File
@@ -137,6 +137,11 @@ public partial class App : WinoApplication,
?? windowManager.GetWindow(WinoWindowKind.Shell)
?? windowManager.GetWindow(WinoWindowKind.Welcome);
if (window is IWinoShellWindow)
{
DisposeTrayIcon();
}
InitializeNavigationDispatcher();
}
@@ -159,6 +164,37 @@ public partial class App : WinoApplication,
_trayIcon.Create();
}
private void DisposeTrayIcon()
{
_trayIcon?.Dispose();
_trayIcon = null;
}
private void EnsurePreferenceChangedSubscription()
{
if (_preferencesService == null)
return;
_preferencesService.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
}
private bool ShouldCreateTrayIcon()
=> _hasConfiguredAccounts &&
HasShellWindow() &&
(_preferencesService?.IsSystemTrayIconEnabled ?? true);
private void UpdateTrayIconState(bool allowCreation)
{
if (!allowCreation || !ShouldCreateTrayIcon())
{
DisposeTrayIcon();
return;
}
EnsureTrayIconCreated();
}
private IReadOnlyList<NativeTrayIcon.NativeTrayMenuItem> BuildTrayMenu()
{
List<NativeTrayIcon.NativeTrayMenuItem> items =
@@ -268,6 +304,7 @@ public partial class App : WinoApplication,
if (windowManager.GetWindow(WinoWindowKind.Shell) is not ShellWindow shellWindow)
return;
DisposeTrayIcon();
windowManager.HideWindow(shellWindow);
if (ReferenceEquals(MainWindow, shellWindow))
{
@@ -289,6 +326,8 @@ public partial class App : WinoApplication,
{
await NewThemeService.ApplyThemeToActiveWindowAsync();
}
UpdateTrayIconState(window is IWinoShellWindow);
}
private Task ExitApplicationAsync()
@@ -303,8 +342,7 @@ public partial class App : WinoApplication,
return;
_isExiting = true;
_trayIcon?.Dispose();
_trayIcon = null;
DisposeTrayIcon();
Services.GetRequiredService<IWinoWindowManager>().CloseAllWindows();
Application.Current.Exit();
@@ -476,12 +514,10 @@ public partial class App : WinoApplication,
return;
EnsureWindowManagerConfigured();
EnsureTrayIconCreated();
EnsurePreferenceChangedSubscription();
if (_hasConfiguredAccounts)
{
_preferencesService!.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
RestartAutoSynchronizationLoop();
}
@@ -942,11 +978,16 @@ public partial class App : WinoApplication,
if (isStartupTaskLaunch)
{
UpdateTrayIconState(allowCreation: true);
LogActivation("Launched by startup task. Window created but hidden (system tray only).");
return;
}
MainWindow?.Activate();
if (MainWindow is WindowEx window)
{
await ActivateWindowAsync(window, applyThemeToWindow: false);
}
LogActivation("Window created and activated.");
}
@@ -1186,8 +1227,10 @@ public partial class App : WinoApplication,
// Initialize theme service after window is created.
await NewThemeService.InitializeAsync();
if (MainWindow != null)
Services.GetRequiredService<IWinoWindowManager>().ActivateWindow(MainWindow);
if (MainWindow is WindowEx window)
{
await ActivateWindowAsync(window, applyThemeToWindow: false);
}
LogActivation("Window created and activated.");
}
@@ -1398,6 +1441,7 @@ public partial class App : WinoApplication,
public void Receive(AccountCreatedMessage message)
{
_hasConfiguredAccounts = true;
EnsurePreferenceChangedSubscription();
var windowManager = Services.GetRequiredService<IWinoWindowManager>();
@@ -1436,11 +1480,7 @@ public partial class App : WinoApplication,
MainWindow?.DispatcherQueue?.TryEnqueue(async () =>
{
if (_preferencesService != null)
{
_preferencesService.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
}
EnsurePreferenceChangedSubscription();
CreateWindow(
null,
@@ -1484,6 +1524,7 @@ public partial class App : WinoApplication,
// All accounts removed — go back to welcome wizard from step 1
Services.GetRequiredService<WelcomeWizardContext>().Reset();
StopAutoSynchronizationLoop();
UpdateTrayIconState(allowCreation: false);
CloseShellWindowIfPresent();
CreateWelcomeWindow();
if (MainWindow != null)
@@ -1577,10 +1618,16 @@ public partial class App : WinoApplication,
private void PreferencesServiceChanged(object? sender, string propertyName)
{
if (propertyName != nameof(IPreferencesService.EmailSyncIntervalMinutes))
return;
if (propertyName == nameof(IPreferencesService.EmailSyncIntervalMinutes))
{
RestartAutoSynchronizationLoop();
return;
}
if (propertyName == nameof(IPreferencesService.IsSystemTrayIconEnabled))
{
UpdateTrayIconState(allowCreation: true);
}
}
private void RestartAutoSynchronizationLoop()
@@ -387,6 +387,12 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
set => SetPropertyAndSave(nameof(IsStoreUpdateNotificationsEnabled), value);
}
public bool IsSystemTrayIconEnabled
{
get => _configurationService.Get(nameof(IsSystemTrayIconEnabled), true);
set => SetPropertyAndSave(nameof(IsSystemTrayIconEnabled), value);
}
public bool IsWinoAccountButtonHidden
{
get => _configurationService.Get(nameof(IsWinoAccountButtonHidden), false);
@@ -63,6 +63,13 @@
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description}" Header="{x:Bind domain:Translator.SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title}">
<ToggleSwitch IsOn="{x:Bind ViewModel.PreferencesService.IsSystemTrayIconEnabled, Mode=TwoWay}" />
<controls:SettingsCard.HeaderIcon>
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE7F4;" />
</controls:SettingsCard.HeaderIcon>
</controls:SettingsCard>
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsAppPreferences_StoreUpdateNotifications_Description}" Header="{x:Bind domain:Translator.SettingsAppPreferences_StoreUpdateNotifications_Title}">
<ToggleSwitch IsOn="{x:Bind ViewModel.PreferencesService.IsStoreUpdateNotificationsEnabled, Mode=TwoWay}" />