-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTrayIconHost.cs
More file actions
149 lines (130 loc) · 4.9 KB
/
Copy pathTrayIconHost.cs
File metadata and controls
149 lines (130 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using MsbIcon = MsBox.Avalonia.Enums.Icon;
namespace Babelive;
/// <summary>
/// Owns the system-tray <see cref="TrayIcon"/> and its context menu so
/// the user can:
/// <list type="bullet">
/// <item>Toggle translation on/off</item>
/// <item>Show / hide the lyric overlay</item>
/// <item>Open the settings window</item>
/// <item>Exit the application</item>
/// </list>
/// Owns nothing audio-related itself — just a controller around the two
/// windows.
/// </summary>
public sealed class TrayIconHost : IDisposable
{
private readonly TrayIcon _icon;
private readonly MainWindow _settings;
private readonly LyricWindow _lyrics;
private readonly NativeMenuItem _toggleItem;
private readonly NativeMenuItem _showLyricsItem;
private bool _disposed;
public TrayIconHost(MainWindow settings, LyricWindow lyrics)
{
_settings = settings;
_lyrics = lyrics;
_icon = new TrayIcon
{
Icon = AppIcon.Build(),
ToolTipText = "Babelive",
IsVisible = true,
};
var menu = new NativeMenu();
_toggleItem = new NativeMenuItem("Start translation");
_toggleItem.Click += async (_, _) =>
{
try { await _settings.ToggleRunningAsync(); }
catch (Exception ex) { await ShowError("Toggle failed", ex); }
};
menu.Items.Add(_toggleItem);
menu.Items.Add(new NativeMenuItemSeparator());
_showLyricsItem = new NativeMenuItem("Show lyric overlay")
{
// ToggleType enum location varies between Avalonia versions and
// native menus on Windows don't always render the checkmark
// reliably — we track state via IsChecked and update the menu
// text in the click handler so the user sees the toggle effect.
IsChecked = true,
};
_showLyricsItem.Click += (_, _) =>
{
// Avalonia's NativeMenuItem doesn't auto-flip IsChecked for
// CheckBox toggle type the way WinForms did — do it manually.
_showLyricsItem.IsChecked = !_showLyricsItem.IsChecked;
if (_showLyricsItem.IsChecked)
{
if (!_lyrics.IsVisible) _lyrics.Show();
_lyrics.Activate();
}
else _lyrics.Hide();
};
menu.Items.Add(_showLyricsItem);
var settingsItem = new NativeMenuItem("Settings…");
settingsItem.Click += (_, _) => OpenSettings();
menu.Items.Add(settingsItem);
menu.Items.Add(new NativeMenuItemSeparator());
var exitItem = new NativeMenuItem("Exit");
exitItem.Click += (_, _) =>
{
if (Application.Current?.ApplicationLifetime
is IClassicDesktopStyleApplicationLifetime desktop)
desktop.Shutdown();
};
menu.Items.Add(exitItem);
_icon.Menu = menu;
// Left-click on the icon opens the settings window (Avalonia
// exposes a single Clicked event — there's no separate
// DoubleClick — so single-click-to-open is the only sensible UX).
_icon.Clicked += (_, _) => OpenSettings();
// Keep the toggle item label in sync with running state
_settings.OnRunningChanged += UpdateToggleLabel;
UpdateToggleLabel();
// Keep "Show lyric overlay" check in sync if the user closes the
// overlay via its ✕ button (which Hides rather than Closes).
// Avalonia exposes IsVisible as an AvaloniaProperty; subscribe to
// changes via the property-changed observable.
_lyrics.PropertyChanged += (_, e) =>
{
if (e.Property == Window.IsVisibleProperty
&& _showLyricsItem.IsChecked != _lyrics.IsVisible)
_showLyricsItem.IsChecked = _lyrics.IsVisible;
};
}
private void OpenSettings()
{
Dispatcher.UIThread.Post(() =>
{
if (!_settings.IsVisible) _settings.Show();
if (_settings.WindowState == WindowState.Minimized)
_settings.WindowState = WindowState.Normal;
_settings.Activate();
});
}
private void UpdateToggleLabel()
{
Dispatcher.UIThread.Post(() =>
{
_toggleItem.Header = _settings.IsRunning ? "Stop translation" : "Start translation";
});
}
private static async Task ShowError(string title, Exception ex)
{
await MessageBoxManager.GetMessageBoxStandard(
title, ex.Message, ButtonEnum.Ok, MsbIcon.Warning).ShowAsync();
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_settings.OnRunningChanged -= UpdateToggleLabel;
_icon.IsVisible = false;
_icon.Dispose();
}
}