LenovoLegionToolkit
275 строк · 7.3 Кб
1using System;2using System.Drawing;3using System.Runtime.InteropServices;4using System.Threading;5using System.Threading.Tasks;6using System.Windows;7using System.Windows.Controls;8using System.Windows.Controls.Primitives;9using System.Windows.Forms;10using System.Windows.Interop;11using LenovoLegionToolkit.Lib.Utils;12using Windows.Win32;13using Windows.Win32.Foundation;14using Windows.Win32.UI.Shell;15using Windows.Win32.UI.WindowsAndMessaging;16using Wpf.Ui.Controls;17
18namespace LenovoLegionToolkit.WPF.Utils;19
20public class NotifyIcon : NativeWindow, IDisposable21{
22private const uint TRAY_MESSAGE_ID = PInvoke.WM_USER + 1069;23
24private static readonly uint TaskbarCreatedMessage = PInvoke.RegisterWindowMessage("TaskbarCreated");25
26private static uint _nextId;27
28private readonly object _lock = new();29private readonly uint _id = ++_nextId;30
31private bool _added;32private CancellationTokenSource? _showToolTipCancellationTokenSource;33
34private bool _visible;35public bool Visible36{37set38{39_visible = value;40UpdateIcon();41}42}43
44private Icon? _icon;45public Icon? Icon46{47set48{49_icon = value;50UpdateIcon();51
52}53}54
55private string? _text;56public string? Text57{58set59{60_text = value;61UpdateIcon();62}63}64
65private UiWindow? _currentToolTipWindow;66
67private Func<Task<UiWindow>>? _toolTipWindow;68public Func<Task<UiWindow>>? ToolTipWindow69{70set71{72_toolTipWindow = value;73UpdateIcon();74}75}76
77public ContextMenu? ContextMenu { get; set; }78
79public event EventHandler? OnClick;80
81public NotifyIcon()82{83UpdateIcon();84}85
86protected override void WndProc(ref Message m)87{88switch ((uint)m.Msg)89{90case TRAY_MESSAGE_ID:91switch ((uint)m.LParam & 0xFFFF)92{93case PInvoke.NIN_POPUPOPEN:94if (Log.Instance.IsTraceEnabled)95Log.Instance.Trace($"NIN_POPUPOPEN");96ShowToolTip();97break;98case PInvoke.NIN_POPUPCLOSE:99if (Log.Instance.IsTraceEnabled)100Log.Instance.Trace($"NIN_POPUPCLOSE");101HideToolTip();102break;103case PInvoke.WM_LBUTTONUP:104if (Log.Instance.IsTraceEnabled)105Log.Instance.Trace($"WM_LBUTTONUP");106HideToolTip();107HideContextMenu();108OnClick?.Invoke(this, EventArgs.Empty);109break;110case PInvoke.WM_RBUTTONUP:111if (Log.Instance.IsTraceEnabled)112Log.Instance.Trace($"WM_RBUTTONUP");113HideToolTip();114ShowContextMenu();115break;116}117break;118case PInvoke.WM_DESTROY:119_visible = false;120UpdateIcon();121break;122default:123if (m.Msg == TaskbarCreatedMessage && _visible)124{125_visible = true;126_added = false;127UpdateIcon();128}129
130DefWndProc(ref m);131break;132}133}134
135private async void ShowToolTip()136{137if (_toolTipWindow is null)138return;139
140_showToolTipCancellationTokenSource?.Cancel();141_showToolTipCancellationTokenSource = new();142
143var token = _showToolTipCancellationTokenSource.Token;144
145try146{147await Task.Delay(TimeSpan.FromMilliseconds(500), token);148
149if (ContextMenu is not null && ContextMenu.IsOpen)150return;151
152_currentToolTipWindow?.Close();153_currentToolTipWindow = await _toolTipWindow();154
155token.ThrowIfCancellationRequested();156
157_currentToolTipWindow?.Show();158}159catch (OperationCanceledException)160{161_currentToolTipWindow?.Close();162_currentToolTipWindow = null;163}164catch (Exception ex)165{166_currentToolTipWindow?.Close();167_currentToolTipWindow = null;168
169if (Log.Instance.IsTraceEnabled)170Log.Instance.Trace($"Failed to show tooltip.", ex);171}172}173
174private void HideToolTip()175{176if (_toolTipWindow is null)177return;178
179_showToolTipCancellationTokenSource?.Cancel();180
181_currentToolTipWindow?.Hide();182_currentToolTipWindow = null;183}184
185private void ShowContextMenu()186{187if (ContextMenu is null)188return;189
190ContextMenu.Placement = PlacementMode.Mouse;191ContextMenu.PlacementRectangle = Rect.Empty;192ContextMenu.PlacementTarget = null;193ContextMenu.IsOpen = true;194
195if (PresentationSource.FromVisual(ContextMenu) is HwndSource source && source.Handle != IntPtr.Zero)196PInvoke.SetForegroundWindow(new HWND(source.Handle));197}198
199private void HideContextMenu()200{201if (ContextMenu is null || !ContextMenu.IsOpen)202return;203
204ContextMenu.IsOpen = false;205}206
207private void UpdateIcon()208{209lock (_lock)210{211var data = new NOTIFYICONDATAW212{213cbSize = (uint)Marshal.SizeOf<NOTIFYICONDATAW>(),214uID = _id,215uCallbackMessage = TRAY_MESSAGE_ID,216uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_TIP,217szTip = " "218};219
220if (_visible && Handle == IntPtr.Zero)221CreateHandle(new CreateParams());222
223data.hWnd = new HWND(Handle);224
225if (_icon is not null)226{227data.uFlags |= NOTIFY_ICON_DATA_FLAGS.NIF_ICON;228data.hIcon = new HICON(_icon.Handle);229}230
231if (_text is not null && _toolTipWindow is null)232{233data.uFlags |= NOTIFY_ICON_DATA_FLAGS.NIF_SHOWTIP;234data.szTip = _text;235}236
237switch (_visible, _added)238{239case (true, false):240PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, data);241data.Anonymous = new() { uVersion = 4 };242PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_SETVERSION, data);243_added = true;244break;245case (true, true):246PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_MODIFY, data);247break;248case (false, true):249PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, data);250_added = false;251break;252}253}254}255
256public void Dispose()257{258GC.SuppressFinalize(this);259
260HideContextMenu();261HideToolTip();262
263_visible = false;264UpdateIcon();265
266_icon?.Dispose();267
268_icon = null;269_text = null;270_toolTipWindow = null;271ContextMenu = null;272
273ReleaseHandle();274}275}
276