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> /// </summary>
bool IsStoreUpdateNotificationsEnabled { get; set; } 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> /// <summary>
/// Setting: Whether the Wino account profile button in the shell title bar should be hidden. /// Setting: Whether the Wino account profile button in the shell title bar should be hidden.
/// </summary> /// </summary>
+63 -16
View File
@@ -137,6 +137,11 @@ public partial class App : WinoApplication,
?? windowManager.GetWindow(WinoWindowKind.Shell) ?? windowManager.GetWindow(WinoWindowKind.Shell)
?? windowManager.GetWindow(WinoWindowKind.Welcome); ?? windowManager.GetWindow(WinoWindowKind.Welcome);
if (window is IWinoShellWindow)
{
DisposeTrayIcon();
}
InitializeNavigationDispatcher(); InitializeNavigationDispatcher();
} }
@@ -159,6 +164,37 @@ public partial class App : WinoApplication,
_trayIcon.Create(); _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() private IReadOnlyList<NativeTrayIcon.NativeTrayMenuItem> BuildTrayMenu()
{ {
List<NativeTrayIcon.NativeTrayMenuItem> items = List<NativeTrayIcon.NativeTrayMenuItem> items =
@@ -268,6 +304,7 @@ public partial class App : WinoApplication,
if (windowManager.GetWindow(WinoWindowKind.Shell) is not ShellWindow shellWindow) if (windowManager.GetWindow(WinoWindowKind.Shell) is not ShellWindow shellWindow)
return; return;
DisposeTrayIcon();
windowManager.HideWindow(shellWindow); windowManager.HideWindow(shellWindow);
if (ReferenceEquals(MainWindow, shellWindow)) if (ReferenceEquals(MainWindow, shellWindow))
{ {
@@ -289,6 +326,8 @@ public partial class App : WinoApplication,
{ {
await NewThemeService.ApplyThemeToActiveWindowAsync(); await NewThemeService.ApplyThemeToActiveWindowAsync();
} }
UpdateTrayIconState(window is IWinoShellWindow);
} }
private Task ExitApplicationAsync() private Task ExitApplicationAsync()
@@ -303,8 +342,7 @@ public partial class App : WinoApplication,
return; return;
_isExiting = true; _isExiting = true;
_trayIcon?.Dispose(); DisposeTrayIcon();
_trayIcon = null;
Services.GetRequiredService<IWinoWindowManager>().CloseAllWindows(); Services.GetRequiredService<IWinoWindowManager>().CloseAllWindows();
Application.Current.Exit(); Application.Current.Exit();
@@ -476,12 +514,10 @@ public partial class App : WinoApplication,
return; return;
EnsureWindowManagerConfigured(); EnsureWindowManagerConfigured();
EnsureTrayIconCreated(); EnsurePreferenceChangedSubscription();
if (_hasConfiguredAccounts) if (_hasConfiguredAccounts)
{ {
_preferencesService!.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
RestartAutoSynchronizationLoop(); RestartAutoSynchronizationLoop();
} }
@@ -942,11 +978,16 @@ public partial class App : WinoApplication,
if (isStartupTaskLaunch) if (isStartupTaskLaunch)
{ {
UpdateTrayIconState(allowCreation: true);
LogActivation("Launched by startup task. Window created but hidden (system tray only)."); LogActivation("Launched by startup task. Window created but hidden (system tray only).");
return; return;
} }
MainWindow?.Activate(); if (MainWindow is WindowEx window)
{
await ActivateWindowAsync(window, applyThemeToWindow: false);
}
LogActivation("Window created and activated."); LogActivation("Window created and activated.");
} }
@@ -1186,8 +1227,10 @@ public partial class App : WinoApplication,
// Initialize theme service after window is created. // Initialize theme service after window is created.
await NewThemeService.InitializeAsync(); await NewThemeService.InitializeAsync();
if (MainWindow != null) if (MainWindow is WindowEx window)
Services.GetRequiredService<IWinoWindowManager>().ActivateWindow(MainWindow); {
await ActivateWindowAsync(window, applyThemeToWindow: false);
}
LogActivation("Window created and activated."); LogActivation("Window created and activated.");
} }
@@ -1398,6 +1441,7 @@ public partial class App : WinoApplication,
public void Receive(AccountCreatedMessage message) public void Receive(AccountCreatedMessage message)
{ {
_hasConfiguredAccounts = true; _hasConfiguredAccounts = true;
EnsurePreferenceChangedSubscription();
var windowManager = Services.GetRequiredService<IWinoWindowManager>(); var windowManager = Services.GetRequiredService<IWinoWindowManager>();
@@ -1436,11 +1480,7 @@ public partial class App : WinoApplication,
MainWindow?.DispatcherQueue?.TryEnqueue(async () => MainWindow?.DispatcherQueue?.TryEnqueue(async () =>
{ {
if (_preferencesService != null) EnsurePreferenceChangedSubscription();
{
_preferencesService.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
}
CreateWindow( CreateWindow(
null, null,
@@ -1484,6 +1524,7 @@ public partial class App : WinoApplication,
// All accounts removed — go back to welcome wizard from step 1 // All accounts removed — go back to welcome wizard from step 1
Services.GetRequiredService<WelcomeWizardContext>().Reset(); Services.GetRequiredService<WelcomeWizardContext>().Reset();
StopAutoSynchronizationLoop(); StopAutoSynchronizationLoop();
UpdateTrayIconState(allowCreation: false);
CloseShellWindowIfPresent(); CloseShellWindowIfPresent();
CreateWelcomeWindow(); CreateWelcomeWindow();
if (MainWindow != null) if (MainWindow != null)
@@ -1577,10 +1618,16 @@ public partial class App : WinoApplication,
private void PreferencesServiceChanged(object? sender, string propertyName) private void PreferencesServiceChanged(object? sender, string propertyName)
{ {
if (propertyName != nameof(IPreferencesService.EmailSyncIntervalMinutes)) if (propertyName == nameof(IPreferencesService.EmailSyncIntervalMinutes))
return; {
RestartAutoSynchronizationLoop(); RestartAutoSynchronizationLoop();
return;
}
if (propertyName == nameof(IPreferencesService.IsSystemTrayIconEnabled))
{
UpdateTrayIconState(allowCreation: true);
}
} }
private void RestartAutoSynchronizationLoop() private void RestartAutoSynchronizationLoop()
@@ -387,6 +387,12 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
set => SetPropertyAndSave(nameof(IsStoreUpdateNotificationsEnabled), value); set => SetPropertyAndSave(nameof(IsStoreUpdateNotificationsEnabled), value);
} }
public bool IsSystemTrayIconEnabled
{
get => _configurationService.Get(nameof(IsSystemTrayIconEnabled), true);
set => SetPropertyAndSave(nameof(IsSystemTrayIconEnabled), value);
}
public bool IsWinoAccountButtonHidden public bool IsWinoAccountButtonHidden
{ {
get => _configurationService.Get(nameof(IsWinoAccountButtonHidden), false); get => _configurationService.Get(nameof(IsWinoAccountButtonHidden), false);
@@ -63,6 +63,13 @@
</controls:SettingsCard.HeaderIcon> </controls:SettingsCard.HeaderIcon>
</controls:SettingsCard> </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}"> <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}" /> <ToggleSwitch IsOn="{x:Bind ViewModel.PreferencesService.IsStoreUpdateNotificationsEnabled, Mode=TwoWay}" />