From 7a4a2992627367716037beed6b6915ef335d7acf Mon Sep 17 00:00:00 2001 From: Dan Clark Date: Sat, 2 Nov 2024 21:06:58 +0000 Subject: [PATCH] Moved TaskbarButtonMonitor --- Toolkit.Windows/ITaskbarButtonMonitor.cs | 7 + Toolkit.Windows/PointerMonitor.cs | 3 +- Toolkit.Windows/Screen.cs | 4 +- Toolkit.Windows/SystemInformationHelper.cs | 2 +- Toolkit.Windows/Taskbar.cs | 8 +- .../TaskbarButtonCreatedEventArgs.cs | 3 + Toolkit.Windows/TaskbarButtonMonitor.cs | 158 ++++++++++++++++++ .../TaskbarButtonRemovedEventArgs.cs | 3 + .../TaskbarButtonUpdatedEventArgs.cs | 3 + Toolkit.Windows/Toolkit.Windows.csproj | 58 ++++--- Toolkit.Windows/WindowHelper.cs | 4 +- Toolkit.Windows/WndProcMonitor.cs | 2 +- 12 files changed, 224 insertions(+), 31 deletions(-) create mode 100644 Toolkit.Windows/ITaskbarButtonMonitor.cs create mode 100644 Toolkit.Windows/TaskbarButtonCreatedEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarButtonMonitor.cs create mode 100644 Toolkit.Windows/TaskbarButtonRemovedEventArgs.cs create mode 100644 Toolkit.Windows/TaskbarButtonUpdatedEventArgs.cs diff --git a/Toolkit.Windows/ITaskbarButtonMonitor.cs b/Toolkit.Windows/ITaskbarButtonMonitor.cs new file mode 100644 index 0000000..6c623a2 --- /dev/null +++ b/Toolkit.Windows/ITaskbarButtonMonitor.cs @@ -0,0 +1,7 @@ +using Toolkit.Foundation; + +namespace Toolkit.Windows; + +public interface ITaskbarButtonMonitor : + IInitialization, + IDisposable; diff --git a/Toolkit.Windows/PointerMonitor.cs b/Toolkit.Windows/PointerMonitor.cs index b2ec022..bc3e405 100644 --- a/Toolkit.Windows/PointerMonitor.cs +++ b/Toolkit.Windows/PointerMonitor.cs @@ -50,8 +50,7 @@ public class PointerMonitor(IPublisher publisher) : { if (nCode >= 0) { - - if (TryGetPointerLocation(out var location)) + if (TryGetPointerLocation(out PointerLocation? location)) { switch ((uint)wParam.Value) { diff --git a/Toolkit.Windows/Screen.cs b/Toolkit.Windows/Screen.cs index 39cc5f4..81c1723 100644 --- a/Toolkit.Windows/Screen.cs +++ b/Toolkit.Windows/Screen.cs @@ -29,7 +29,7 @@ public class Screen } else { - var monitorData = GetMonitorData(monitorHandle); + MonitorData monitorData = GetMonitorData(monitorHandle); Bounds = new Rect(monitorData.MonitorRect.left, monitorData.MonitorRect.top, monitorData.MonitorRect.right - monitorData.MonitorRect.left, @@ -95,7 +95,7 @@ public class Screen return SystemInformationHelper.WorkingArea; } - var monitorData = GetMonitorData(_monitorHandle); + MonitorData monitorData = GetMonitorData(_monitorHandle); return new Rect(monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.top, monitorData.WorkAreaRect.right - monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.bottom - monitorData.WorkAreaRect.top); } diff --git a/Toolkit.Windows/SystemInformationHelper.cs b/Toolkit.Windows/SystemInformationHelper.cs index 60dd73c..d347e67 100644 --- a/Toolkit.Windows/SystemInformationHelper.cs +++ b/Toolkit.Windows/SystemInformationHelper.cs @@ -22,7 +22,7 @@ internal static class SystemInformationHelper private static Rect GetWorkingArea() { - var rect = new RECT(); + RECT rect = new RECT(); SystemParametersInfo(SPI_GETWORKAREA, 0, ref rect, 0); return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); diff --git a/Toolkit.Windows/Taskbar.cs b/Toolkit.Windows/Taskbar.cs index d2d3c0a..64d6740 100644 --- a/Toolkit.Windows/Taskbar.cs +++ b/Toolkit.Windows/Taskbar.cs @@ -23,13 +23,13 @@ public class Taskbar(ISubscriber subscriber, public TaskbarState GetCurrentState() { - var handle = GetHandle(); - var state = new TaskbarState + nint handle = GetHandle(); + TaskbarState state = new TaskbarState { Screen = Screen.FromHandle(handle) }; - var appBarData = PInvoke.GetAppBarData(handle); + PInvoke.AppBarData appBarData = PInvoke.GetAppBarData(handle); PInvoke.GetAppBarPosition(ref appBarData); state.Rect = appBarData.rect.ToRect(); @@ -65,7 +65,7 @@ public class Taskbar(ISubscriber subscriber, public Task Handle(PointerMovedEventArgs args) { nint taskbarHandle = GetHandle(); - if (WindowHelper.TryGetBounds(taskbarHandle, out var rect)) + if (WindowHelper.TryGetBounds(taskbarHandle, out Rect? rect)) { if (args.Location.IsWithinBounds(rect)) { diff --git a/Toolkit.Windows/TaskbarButtonCreatedEventArgs.cs b/Toolkit.Windows/TaskbarButtonCreatedEventArgs.cs new file mode 100644 index 0000000..4923348 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonCreatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonCreatedEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarButtonMonitor.cs b/Toolkit.Windows/TaskbarButtonMonitor.cs new file mode 100644 index 0000000..7f3ab69 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonMonitor.cs @@ -0,0 +1,158 @@ +using System.Diagnostics; +using Toolkit.Foundation; +using UIAutomationClient; + +namespace Toolkit.Windows; + +public class TaskbarButtonMonitor : + ITaskbarButtonMonitor +{ + private readonly IDispatcherTimer dispatcherTimer; + private readonly IDispatcherTimerFactory dispatcherTimerFactory; + private readonly IDisposer disposer; + private readonly IPublisher publisher; + private readonly IServiceFactory serviceFactory; + private readonly Dictionary taskbarButtons = []; + private readonly ITaskbarList taskbarList; + private Rect? taskbarRectCache; + private IUIAutomationCondition? taskListCondition; + private IUIAutomationElement? taskListElement; + private IntPtr taskListHandle; + + public TaskbarButtonMonitor(ITaskbarList taskbarList, + IPublisher publisher, + IDispatcherTimerFactory dispatcherTimerFactory, + IServiceFactory serviceFactory, + IDisposer disposer) + { + this.taskbarList = taskbarList; + this.publisher = publisher; + this.dispatcherTimerFactory = dispatcherTimerFactory; + this.serviceFactory = serviceFactory; + this.disposer = disposer; + + disposer.Add(this, dispatcherTimer = dispatcherTimerFactory.Create(OnDispatcher, + TimeSpan.FromMilliseconds(500))); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + disposer.Dispose(this); + } + + public void Initialize() + { + CUIAutomation clientUIAutomation = new(); + taskListCondition = clientUIAutomation.CreateTrueCondition(); + + taskListHandle = taskbarList.GetHandle(); + taskListElement = clientUIAutomation.ElementFromHandle(taskListHandle); + + if (WindowHelper.TryGetBounds(taskListHandle, out Rect? rect)) + { + taskbarRectCache = rect; + } + + dispatcherTimer.Start(); + UpdateTaskbarButtons(); + } + + private bool CheckDirtyTaskbarRegion() + { + if (WindowHelper.TryGetBounds(taskListHandle, out Rect? rect)) + { + if (taskbarRectCache?.Width != rect.Width || + taskbarRectCache?.Height != rect.Height) + { + taskbarRectCache = rect; + return true; + } + } + + return false; + } + + private Dictionary FindTaskbarButtons() + { + IUIAutomationElementArray? taskElements = taskListElement?.FindAll(TreeScope.TreeScope_Descendants | + TreeScope.TreeScope_Children, taskListCondition); + + Dictionary buttons = []; + if (taskElements is not null) + { + for (int index = 0; index <= taskElements.Length - 1; index++) + { + IUIAutomationElement taskUIElement = taskElements.GetElement(index); + string name = taskUIElement.CurrentName; + tagRECT rect = taskUIElement.CurrentBoundingRectangle; + + buttons.Add(name, rect); + } + } + + return buttons; + } + + private void OnDispatcher() + { + dispatcherTimer.Stop(); + + if (CheckDirtyTaskbarRegion()) + { + UpdateTaskbarButtons(); + } + + dispatcherTimer.Start(); + } + + private void UpdateTaskbarButtons() + { + if (taskListElement is null) + { + return; + } + + Dictionary buttons = FindTaskbarButtons(); + + foreach (KeyValuePair buttonToRemove in taskbarButtons + .Where(taskbarButton => !buttons.ContainsKey(taskbarButton.Key))) + { + string key = buttonToRemove.Key; + TaskbarButton button = buttonToRemove.Value; + + Debug.WriteLine($"{key} button removed"); + + taskbarButtons.Remove(key); + publisher.Publish(new TaskbarButtonRemovedEventArgs(button)); + + button.Dispose(); + } + + foreach (KeyValuePair button in buttons) + { + string name = button.Key; + tagRECT bounds = button.Value; + + Rect rect = new(bounds.left, + bounds.top, + bounds.right - bounds.left, + bounds.bottom - bounds.top); + + if (taskbarButtons.TryGetValue(name, out TaskbarButton? taskbarButton)) + { + Debug.WriteLine($"{name} button updated"); + + taskbarButtons[name].Rect = rect; + publisher.Publish(new TaskbarButtonUpdatedEventArgs(taskbarButtons[name])); + } + else + { + Debug.WriteLine($"{name} button added"); + + taskbarButtons.Add(name, serviceFactory.Create(name, rect)); + publisher.Publish(new TaskbarButtonCreatedEventArgs(taskbarButtons[name])); + } + } + } +} diff --git a/Toolkit.Windows/TaskbarButtonRemovedEventArgs.cs b/Toolkit.Windows/TaskbarButtonRemovedEventArgs.cs new file mode 100644 index 0000000..5af75bf --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonRemovedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonRemovedEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/TaskbarButtonUpdatedEventArgs.cs b/Toolkit.Windows/TaskbarButtonUpdatedEventArgs.cs new file mode 100644 index 0000000..d130948 --- /dev/null +++ b/Toolkit.Windows/TaskbarButtonUpdatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Windows; + +public record TaskbarButtonUpdatedEventArgs(TaskbarButton Button); diff --git a/Toolkit.Windows/Toolkit.Windows.csproj b/Toolkit.Windows/Toolkit.Windows.csproj index b784cf4..fb0ad59 100644 --- a/Toolkit.Windows/Toolkit.Windows.csproj +++ b/Toolkit.Windows/Toolkit.Windows.csproj @@ -1,26 +1,46 @@  - - net9.0-windows10.0.19041.0 - enable - enable - True - True - AnyCPU;x64;x86 - 10.0.19041.41 + + net9.0-windows10.0.19041.0 + enable + enable + True + True + AnyCPU;x64;x86 + 10.0.19041.41 + - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - - - + + + tlbimp + 0 + 1 + 930299ce-9965-4dec-b0f4-a54848d4b667 + 0 + false + true + + + 0 + 1 + 944de083-8fb8-45cf-bcb7-c477acb2f897 + 0 + tlbimp + false + true + + diff --git a/Toolkit.Windows/WindowHelper.cs b/Toolkit.Windows/WindowHelper.cs index af20301..abaff17 100644 --- a/Toolkit.Windows/WindowHelper.cs +++ b/Toolkit.Windows/WindowHelper.cs @@ -9,7 +9,7 @@ public class WindowHelper { public static void BringToForeground(HWND handle) { - if (TryGetBoundsUnsafe(handle, out var bounds)) + if (TryGetBoundsUnsafe(handle, out RECT bounds)) { PInvoke.SetWindowPos(handle, new HWND(), bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE); @@ -28,7 +28,7 @@ public class WindowHelper public static bool TryGetBounds(IntPtr handle, [MaybeNullWhen(false)] out Rect rect) { - if (TryGetBoundsUnsafe(handle, out var unsafeRect)) + if (TryGetBoundsUnsafe(handle, out RECT unsafeRect)) { rect = new Rect(unsafeRect.left, unsafeRect.top, unsafeRect.right - unsafeRect.left, unsafeRect.bottom - unsafeRect.top); diff --git a/Toolkit.Windows/WndProcMonitor.cs b/Toolkit.Windows/WndProcMonitor.cs index c3b69af..8fd5780 100644 --- a/Toolkit.Windows/WndProcMonitor.cs +++ b/Toolkit.Windows/WndProcMonitor.cs @@ -21,7 +21,7 @@ public class WndProcMonitor(IPublisher publisher) : private unsafe void InitializeWndProc() { - var windowName = Guid.NewGuid().ToString(); + string windowName = Guid.NewGuid().ToString(); handler = Wndproc; WNDCLASSW wndProcWindow;