Moved TaskbarButtonMonitor
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
using Toolkit.Foundation;
|
||||||
|
|
||||||
|
namespace Toolkit.Windows;
|
||||||
|
|
||||||
|
public interface ITaskbarButtonMonitor :
|
||||||
|
IInitialization,
|
||||||
|
IDisposable;
|
||||||
@@ -50,8 +50,7 @@ public class PointerMonitor(IPublisher publisher) :
|
|||||||
{
|
{
|
||||||
if (nCode >= 0)
|
if (nCode >= 0)
|
||||||
{
|
{
|
||||||
|
if (TryGetPointerLocation(out PointerLocation? location))
|
||||||
if (TryGetPointerLocation(out var location))
|
|
||||||
{
|
{
|
||||||
switch ((uint)wParam.Value)
|
switch ((uint)wParam.Value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class Screen
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var monitorData = GetMonitorData(monitorHandle);
|
MonitorData monitorData = GetMonitorData(monitorHandle);
|
||||||
|
|
||||||
Bounds = new Rect(monitorData.MonitorRect.left, monitorData.MonitorRect.top,
|
Bounds = new Rect(monitorData.MonitorRect.left, monitorData.MonitorRect.top,
|
||||||
monitorData.MonitorRect.right - monitorData.MonitorRect.left,
|
monitorData.MonitorRect.right - monitorData.MonitorRect.left,
|
||||||
@@ -95,7 +95,7 @@ public class Screen
|
|||||||
return SystemInformationHelper.WorkingArea;
|
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);
|
return new Rect(monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.top, monitorData.WorkAreaRect.right - monitorData.WorkAreaRect.left, monitorData.WorkAreaRect.bottom - monitorData.WorkAreaRect.top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal static class SystemInformationHelper
|
|||||||
|
|
||||||
private static Rect GetWorkingArea()
|
private static Rect GetWorkingArea()
|
||||||
{
|
{
|
||||||
var rect = new RECT();
|
RECT rect = new RECT();
|
||||||
|
|
||||||
SystemParametersInfo(SPI_GETWORKAREA, 0, ref rect, 0);
|
SystemParametersInfo(SPI_GETWORKAREA, 0, ref rect, 0);
|
||||||
return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ public class Taskbar(ISubscriber subscriber,
|
|||||||
|
|
||||||
public TaskbarState GetCurrentState()
|
public TaskbarState GetCurrentState()
|
||||||
{
|
{
|
||||||
var handle = GetHandle();
|
nint handle = GetHandle();
|
||||||
var state = new TaskbarState
|
TaskbarState state = new TaskbarState
|
||||||
{
|
{
|
||||||
Screen = Screen.FromHandle(handle)
|
Screen = Screen.FromHandle(handle)
|
||||||
};
|
};
|
||||||
|
|
||||||
var appBarData = PInvoke.GetAppBarData(handle);
|
PInvoke.AppBarData appBarData = PInvoke.GetAppBarData(handle);
|
||||||
PInvoke.GetAppBarPosition(ref appBarData);
|
PInvoke.GetAppBarPosition(ref appBarData);
|
||||||
|
|
||||||
state.Rect = appBarData.rect.ToRect();
|
state.Rect = appBarData.rect.ToRect();
|
||||||
@@ -65,7 +65,7 @@ public class Taskbar(ISubscriber subscriber,
|
|||||||
public Task Handle(PointerMovedEventArgs args)
|
public Task Handle(PointerMovedEventArgs args)
|
||||||
{
|
{
|
||||||
nint taskbarHandle = GetHandle();
|
nint taskbarHandle = GetHandle();
|
||||||
if (WindowHelper.TryGetBounds(taskbarHandle, out var rect))
|
if (WindowHelper.TryGetBounds(taskbarHandle, out Rect? rect))
|
||||||
{
|
{
|
||||||
if (args.Location.IsWithinBounds(rect))
|
if (args.Location.IsWithinBounds(rect))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Toolkit.Windows;
|
||||||
|
|
||||||
|
public record TaskbarButtonCreatedEventArgs(TaskbarButton Button);
|
||||||
@@ -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<string, TaskbarButton> 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<string, tagRECT> FindTaskbarButtons()
|
||||||
|
{
|
||||||
|
IUIAutomationElementArray? taskElements = taskListElement?.FindAll(TreeScope.TreeScope_Descendants |
|
||||||
|
TreeScope.TreeScope_Children, taskListCondition);
|
||||||
|
|
||||||
|
Dictionary<string, tagRECT> 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<string, tagRECT> buttons = FindTaskbarButtons();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, TaskbarButton> 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<string, tagRECT> 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<TaskbarButton>(name, rect));
|
||||||
|
publisher.Publish(new TaskbarButtonCreatedEventArgs(taskbarButtons[name]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Toolkit.Windows;
|
||||||
|
|
||||||
|
public record TaskbarButtonRemovedEventArgs(TaskbarButton Button);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace Toolkit.Windows;
|
||||||
|
|
||||||
|
public record TaskbarButtonUpdatedEventArgs(TaskbarButton Button);
|
||||||
@@ -1,26 +1,46 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<ResolveComReferenceSilent>True</ResolveComReferenceSilent>
|
<ResolveComReferenceSilent>True</ResolveComReferenceSilent>
|
||||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||||
<WindowsSdkPackageVersion>10.0.19041.41</WindowsSdkPackageVersion>
|
<WindowsSdkPackageVersion>10.0.19041.41</WindowsSdkPackageVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
</PropertyGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
|
||||||
<PrivateAssets>all</PrivateAssets>
|
</ItemGroup>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
|
<COMReference Include="UIA">
|
||||||
</ItemGroup>
|
<WrapperTool>tlbimp</WrapperTool>
|
||||||
|
<VersionMinor>0</VersionMinor>
|
||||||
|
<VersionMajor>1</VersionMajor>
|
||||||
|
<Guid>930299ce-9965-4dec-b0f4-a54848d4b667</Guid>
|
||||||
|
<Lcid>0</Lcid>
|
||||||
|
<Isolated>false</Isolated>
|
||||||
|
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||||
|
</COMReference>
|
||||||
|
<COMReference Include="UIAutomationClient">
|
||||||
|
<VersionMinor>0</VersionMinor>
|
||||||
|
<VersionMajor>1</VersionMajor>
|
||||||
|
<Guid>944de083-8fb8-45cf-bcb7-c477acb2f897</Guid>
|
||||||
|
<Lcid>0</Lcid>
|
||||||
|
<WrapperTool>tlbimp</WrapperTool>
|
||||||
|
<Isolated>false</Isolated>
|
||||||
|
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||||
|
</COMReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class WindowHelper
|
|||||||
{
|
{
|
||||||
public static void BringToForeground(HWND handle)
|
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 -
|
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);
|
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,
|
public static bool TryGetBounds(IntPtr handle,
|
||||||
[MaybeNullWhen(false)] out Rect rect)
|
[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);
|
rect = new Rect(unsafeRect.left, unsafeRect.top, unsafeRect.right - unsafeRect.left, unsafeRect.bottom - unsafeRect.top);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class WndProcMonitor(IPublisher publisher) :
|
|||||||
|
|
||||||
private unsafe void InitializeWndProc()
|
private unsafe void InitializeWndProc()
|
||||||
{
|
{
|
||||||
var windowName = Guid.NewGuid().ToString();
|
string windowName = Guid.NewGuid().ToString();
|
||||||
handler = Wndproc;
|
handler = Wndproc;
|
||||||
|
|
||||||
WNDCLASSW wndProcWindow;
|
WNDCLASSW wndProcWindow;
|
||||||
|
|||||||
Reference in New Issue
Block a user