diff --git a/Wino.Core.Domain/Interfaces/ISystemTrayService.cs b/Wino.Core.Domain/Interfaces/ISystemTrayService.cs
deleted file mode 100644
index 7a67953c..00000000
--- a/Wino.Core.Domain/Interfaces/ISystemTrayService.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-
-namespace Wino.Core.Domain.Interfaces;
-
-public interface ISystemTrayService
-{
- ///
- /// Initializes the system tray icon.
- ///
- void Initialize();
-
- ///
- /// Shows the system tray icon.
- ///
- void Show();
-
- ///
- /// Hides the system tray icon.
- ///
- void Hide();
-
- ///
- /// Event fired when the tray icon is double-clicked.
- ///
- event EventHandler? TrayIconDoubleClicked;
-
- ///
- /// Gets whether the tray icon is currently minimized.
- ///
- bool IsMinimizedToTray { get; }
-
- ///
- /// Disposes of the system tray resources.
- ///
- void Dispose();
-}
\ No newline at end of file
diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json
index c84f20f3..b6464abb 100644
--- a/Wino.Core.Domain/Translations/en_US/resources.json
+++ b/Wino.Core.Domain/Translations/en_US/resources.json
@@ -686,6 +686,8 @@
"SystemFolderConfigDialogValidation_InboxSelected": "You can't assign Inbox folder to any other system folder.",
"SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.",
"SystemFolderConfigSetupSuccess_Title": "System Folders Setup",
+ "SystemTrayMenu_ShowWino": "Open Wino Mail",
+ "SystemTrayMenu_ExitWino": "Exit",
"TestingImapConnectionMessage": "Testing server connection...",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerDisconnectedButton_Title": "no connection",
diff --git a/Wino.Mail.WinUI/App.xaml.cs b/Wino.Mail.WinUI/App.xaml.cs
index 19ce50b5..8fff4f55 100644
--- a/Wino.Mail.WinUI/App.xaml.cs
+++ b/Wino.Mail.WinUI/App.xaml.cs
@@ -2,13 +2,13 @@
using System.Text;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Windows.AppLifecycle;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.WinUI;
using Wino.Core.WinUI.Interfaces;
using Wino.Mail.Services;
using Wino.Mail.ViewModels;
-using Wino.Mail.WinUI.Services;
using Wino.Messaging.Server;
using Wino.Services;
namespace Wino.Mail.WinUI;
@@ -36,7 +36,6 @@ public partial class App : WinoApplication, IRecipient();
services.AddTransient();
services.AddSingleton();
- services.AddSingleton();
}
private void RegisterViewModels(IServiceCollection services)
@@ -79,6 +78,8 @@ public partial class App : WinoApplication, IRecipient AppInstance.GetCurrent().GetActivatedEventArgs()?.Kind == ExtendedActivationKind.StartupTask;
+
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
// TODO: Check app relaunch mutex before loading anything.
@@ -99,19 +100,16 @@ public partial class App : WinoApplication, IRecipient();
- if (systemTrayService != null)
- {
- systemTrayService.Initialize();
- systemTrayService.Show(); // Explicitly show the tray icon
- }
-
if (MainWindow is not IWinoShellWindow shellWindow) throw new ArgumentException("MainWindow must implement IWinoShellWindow");
- shellWindow.HandleAppActivation(args);
+ bool isStartupTaskLaunch = IsStartupTaskLaunch();
- MainWindow.Activate();
+ // Do not actiavate window if launched from startup task. Keep running in the system tray.
+ if (!isStartupTaskLaunch)
+ {
+ shellWindow.HandleAppActivation(args);
+ MainWindow.Activate();
+ }
}
private void RegisterRecipients()
diff --git a/Wino.Mail.WinUI/Services/SystemTrayService.cs b/Wino.Mail.WinUI/Services/SystemTrayService.cs
deleted file mode 100644
index ca82ac8d..00000000
--- a/Wino.Mail.WinUI/Services/SystemTrayService.cs
+++ /dev/null
@@ -1,196 +0,0 @@
-using System;
-using System.Windows.Input;
-using H.NotifyIcon;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Media.Imaging;
-using Wino.Core.Domain.Interfaces;
-using Microsoft.Extensions.DependencyInjection;
-using Wino.Core.WinUI;
-
-namespace Wino.Mail.WinUI.Services;
-
-public class SystemTrayService : ISystemTrayService
-{
- private TaskbarIcon? _taskbarIcon;
- private bool _isDisposed;
- private bool _isMinimizedToTray;
-
- public bool IsMinimizedToTray => _isMinimizedToTray;
-
- public event EventHandler? TrayIconDoubleClicked;
-
- public void Initialize()
- {
- if (_taskbarIcon != null) return;
-
- try
- {
- System.Diagnostics.Debug.WriteLine("Starting system tray initialization...");
-
- // Create TaskbarIcon first
- _taskbarIcon = new TaskbarIcon();
-
- // Set basic properties first
- _taskbarIcon.ToolTipText = "Wino Mail";
-
- // Configure the taskbar icon with icon loading
- var iconUri = new Uri("ms-appx:///Assets/Wino_Icon.ico");
- var bitmapImage = new BitmapImage(iconUri);
- _taskbarIcon.IconSource = bitmapImage;
- System.Diagnostics.Debug.WriteLine("Icon source set");
-
- // Create context menu
- var contextMenu = new MenuFlyout();
-
- // Show Window menu item
- var showMenuItem = new MenuFlyoutItem
- {
- Text = "Show Wino Mail",
- Icon = new SymbolIcon(Symbol.Home)
- };
- showMenuItem.Click += ShowMenuItem_Click;
- contextMenu.Items.Add(showMenuItem);
- System.Diagnostics.Debug.WriteLine("Show menu item added");
-
- // Separator
- contextMenu.Items.Add(new MenuFlyoutSeparator());
-
- // Exit menu item
- var exitMenuItem = new MenuFlyoutItem
- {
- Text = "Exit",
- Icon = new SymbolIcon(Symbol.Cancel)
- };
- exitMenuItem.Click += ExitMenuItem_Click;
- contextMenu.Items.Add(exitMenuItem);
- System.Diagnostics.Debug.WriteLine("Exit menu item added");
-
- // Set context menu
- _taskbarIcon.ContextFlyout = contextMenu;
-
- // Handle double-click using the proper event
- _taskbarIcon.LeftClickCommand = new RelayCommand(OnTrayIconLeftClick);
-
- // Set visibility and create explicitly
- _taskbarIcon.Visibility = Visibility.Visible;
-
- // Try ForceCreate to ensure the icon is properly created in the system tray
- _taskbarIcon.ForceCreate();
- System.Diagnostics.Debug.WriteLine("System tray icon created and visible");
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Failed to initialize system tray: {ex.Message}");
- System.Diagnostics.Debug.WriteLine($"Stack trace: {ex.StackTrace}");
- }
- }
-
- private void ShowMenuItem_Click(object sender, RoutedEventArgs e)
- {
- System.Diagnostics.Debug.WriteLine("Show menu item clicked");
- TrayIconDoubleClicked?.Invoke(this, EventArgs.Empty);
- }
-
- private void ExitMenuItem_Click(object sender, RoutedEventArgs e)
- {
- System.Diagnostics.Debug.WriteLine("Exit menu item clicked");
- ExitApplication();
- }
-
- private void OnTrayIconLeftClick()
- {
- System.Diagnostics.Debug.WriteLine("Tray icon left clicked");
- TrayIconDoubleClicked?.Invoke(this, EventArgs.Empty);
- }
-
- public void Show()
- {
- if (_taskbarIcon != null)
- {
- try
- {
- _taskbarIcon.Visibility = Visibility.Visible;
- _taskbarIcon.ForceCreate(); // Ensure the icon is properly created and visible
- _isMinimizedToTray = true;
- System.Diagnostics.Debug.WriteLine("System tray icon set to visible and force created");
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Failed to show system tray icon: {ex.Message}");
- }
- }
- else
- {
- System.Diagnostics.Debug.WriteLine("TaskbarIcon is null when trying to show");
- }
- }
-
- public void Hide()
- {
- if (_taskbarIcon != null)
- {
- _taskbarIcon.Visibility = Visibility.Collapsed;
- _isMinimizedToTray = false;
- }
- }
-
- private void ExitApplication()
- {
- System.Diagnostics.Debug.WriteLine("Attempting to exit application...");
-
- try
- {
- // Clean up the tray icon first
- Dispose();
-
- // Get the main window and close it properly
- if (WinoApplication.MainWindow is ShellWindow shellWindow)
- {
- // Force close the window without minimizing to tray
- shellWindow.ForceClose();
- }
- else
- {
- // Fallback to application exit
- Application.Current.Exit();
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Error during application exit: {ex.Message}");
- // Force exit if normal exit fails
- Environment.Exit(0);
- }
- }
-
- public void Dispose()
- {
- if (_isDisposed) return;
-
- _taskbarIcon?.Dispose();
- _taskbarIcon = null;
- _isDisposed = true;
- }
-}
-
-// Simple RelayCommand implementation for the tray icon
-public class RelayCommand : ICommand
-{
- private readonly Action _execute;
- private readonly Func? _canExecute;
-
- public RelayCommand(Action execute, Func? canExecute = null)
- {
- _execute = execute ?? throw new ArgumentNullException(nameof(execute));
- _canExecute = canExecute;
- }
-
- public event EventHandler? CanExecuteChanged;
-
- public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;
-
- public void Execute(object? parameter) => _execute();
-
- public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
-}
diff --git a/Wino.Mail.WinUI/ShellWindow.xaml b/Wino.Mail.WinUI/ShellWindow.xaml
index c2f6c1ad..4819459d 100644
--- a/Wino.Mail.WinUI/ShellWindow.xaml
+++ b/Wino.Mail.WinUI/ShellWindow.xaml
@@ -4,8 +4,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:domain="using:Wino.Core.Domain"
xmlns:local="using:Wino.Mail.WinUI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:notifyicon="using:H.NotifyIcon"
xmlns:winuiex="using:WinUIEx"
Title="ShellWindow"
mc:Ignorable="d">
@@ -30,9 +32,25 @@
IsBackButtonVisible="{x:Bind StatePersistanceService.IsBackButtonVisible, Mode=OneWay}"
IsPaneToggleButtonVisible="True"
PaneToggleRequested="PaneButtonClicked" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wino.Mail.WinUI/ShellWindow.xaml.cs b/Wino.Mail.WinUI/ShellWindow.xaml.cs
index 99bbf187..1f42f70d 100644
--- a/Wino.Mail.WinUI/ShellWindow.xaml.cs
+++ b/Wino.Mail.WinUI/ShellWindow.xaml.cs
@@ -1,4 +1,6 @@
using System;
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
@@ -19,7 +21,9 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient
{
public IStatePersistanceService StatePersistanceService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception("StatePersistanceService not registered in DI container.");
public IPreferencesService PreferencesService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception("PreferencesService not registered in DI container.");
- private readonly ISystemTrayService _systemTrayService;
+
+ public ICommand ShowWinoCommand { get; set; }
+ public ICommand ExitWinoCommand { get; set; }
public ShellWindow()
{
@@ -31,22 +35,22 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient
MinHeight = 420;
ConfigureTitleBar();
- // Initialize system tray service
- _systemTrayService = WinoApplication.Current.Services.GetService() ?? throw new Exception("SystemTrayService not registered in DI container.");
- _systemTrayService.Initialize();
- _systemTrayService.TrayIconDoubleClicked += OnTrayIconDoubleClicked;
-
// Handle window closing event to minimize to tray instead of closing
Closed += OnWindowClosed;
// Use the AppWindow.Closing event to handle the close request
AppWindow.Closing += OnAppWindowClosing;
+
+ ShowWinoCommand = new RelayCommand(RestoreFromTray);
+ ExitWinoCommand = new RelayCommand(ForceClose);
+
+ SystemTrayIcon.ForceCreate();
}
private void ConfigureTitleBar()
{
AppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
-
+
// Apply initial theme colors
var themeService = WinoApplication.Current.Services.GetService();
if (themeService != null)
@@ -133,54 +137,40 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient
private void OnAppWindowClosing(object sender, Microsoft.UI.Windowing.AppWindowClosingEventArgs e)
{
- // Cancel the close and minimize to tray instead
e.Cancel = true;
MinimizeToTray();
}
private void OnWindowClosed(object sender, WindowEventArgs e)
{
- // Clean up tray icon when window is actually closed
- _systemTrayService?.Dispose();
+ SystemTrayIcon?.Dispose();
}
private void MinimizeToTray()
{
- // Hide the window and show tray icon
this.Hide();
- _systemTrayService.Show();
- }
-
- private void OnTrayIconDoubleClicked(object? sender, EventArgs e)
- {
- // Restore the window from tray
- RestoreFromTray();
+ SystemTrayIcon.ForceCreate();
}
private void RestoreFromTray()
{
- if (_systemTrayService.IsMinimizedToTray)
- {
- // Show the window and hide tray icon
- this.Show();
- this.Activate();
- _systemTrayService.Hide();
- }
+ this.Show();
+ BringToFront();
}
public void ForceClose()
{
// Unsubscribe from the closing event to avoid infinite loop
AppWindow.Closing -= OnAppWindowClosing;
-
+
// Clean up system tray
- _systemTrayService?.Dispose();
-
+ SystemTrayIcon?.Dispose();
+
UnregisterRecipients();
-
+
// Close the window
- this.Close();
-
+ Close();
+
// Exit the application
Application.Current.Exit();
}