diff --git a/Hyperbar.Windows.Controls/DesktopBar/DesktopBarHost.cs b/Hyperbar.Windows.Controls/DesktopBar/DesktopBarHost.cs index d26c5b0..b5508e7 100644 --- a/Hyperbar.Windows.Controls/DesktopBar/DesktopBarHost.cs +++ b/Hyperbar.Windows.Controls/DesktopBar/DesktopBarHost.cs @@ -11,8 +11,8 @@ namespace Hyperbar.Windows.Controls; internal class DesktopBarHost : Window { private readonly DesktopBarPresenter presenter; - private DesktopBarPlacemenet placement; private readonly WindowSnapping windowSnapping; + private DesktopBarPlacemenet placement; public DesktopBarHost(DesktopBarPresenter presenter) { @@ -29,6 +29,8 @@ internal class DesktopBarHost : Window this.presenter = presenter; presenter.Loaded += OnLoaded; Content = presenter; + + Closed += OnClosed; } internal void UpdatePlacement(DesktopBarPlacemenet placement) @@ -45,19 +47,19 @@ internal class DesktopBarHost : Window switch (placement) { case DesktopBarPlacemenet.Left: - windowSnapping.Snap(AppBarWindowPlacement.Left, (int)size); + windowSnapping.Snap(WindowSnappingPlacement.Left, (int)size); break; case DesktopBarPlacemenet.Top: - windowSnapping.Snap(AppBarWindowPlacement.Top, (int)size); + windowSnapping.Snap(WindowSnappingPlacement.Top, (int)size); break; case DesktopBarPlacemenet.Right: - windowSnapping.Snap(AppBarWindowPlacement.Right, (int)size); + windowSnapping.Snap(WindowSnappingPlacement.Right, (int)size); break; case DesktopBarPlacemenet.Bottom: - windowSnapping.Snap(AppBarWindowPlacement.Bottom, (int)size); + windowSnapping.Snap(WindowSnappingPlacement.Bottom, (int)size); break; default: @@ -67,6 +69,10 @@ internal class DesktopBarHost : Window presenter.UpdatePlacementState(placement); } + private void OnClosed(object sender, WindowEventArgs args) + { + windowSnapping.Dispose(); + } private void OnLoaded(object sender, RoutedEventArgs args) { diff --git a/Hyperbar.Windows.Interop/WindowMessageListener.cs b/Hyperbar.Windows.Interop/WindowMessageListener.cs index b9d5845..f37b1a5 100644 --- a/Hyperbar.Windows.Interop/WindowMessageListener.cs +++ b/Hyperbar.Windows.Interop/WindowMessageListener.cs @@ -12,7 +12,9 @@ public class WindowMessageListener : protected WindowMessageListener(IntPtr hwnd) { this.hwnd = hwnd; - Set(); + + callback = new SUBCLASSPROC(WindowProc); + PInvoke.SetWindowSubclass(new HWND(hwnd), callback, 101, 0); } ~WindowMessageListener() @@ -22,19 +24,14 @@ public class WindowMessageListener : public static WindowMessageListener Create(IntPtr hwnd) => new(hwnd); - public void Dispose() => Remove(); - - private void Remove() + public void Dispose() { + GC.SuppressFinalize(this); + PInvoke.RemoveWindowSubclass(new HWND(hwnd), callback, 101); callback = null; } - private void Set() - { - callback = new SUBCLASSPROC(WindowProc); - PInvoke.SetWindowSubclass(new HWND(hwnd), callback, 101, 0); - } private LRESULT WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam, diff --git a/Hyperbar.Windows.Interop/WindowSnapping.cs b/Hyperbar.Windows.Interop/WindowSnapping.cs index 4415de3..5f247b1 100644 --- a/Hyperbar.Windows.Interop/WindowSnapping.cs +++ b/Hyperbar.Windows.Interop/WindowSnapping.cs @@ -4,87 +4,17 @@ using Windows.Win32.Foundation; namespace Hyperbar.Windows.Interop; -public class WindowSnapping +public class WindowSnapping : + IDisposable { + private readonly uint callback; private readonly nint hwnd; - private uint callback; + private WindowSnappingPlacement placement; public WindowSnapping(IntPtr hwnd) { this.hwnd = hwnd; - InitializeAppBarWindow(); - } - private enum AppBarMsg : int - { - ABM_NEW = 0, - ABM_REMOVE, - ABM_QUERYPOS, - ABM_SETPOS, - ABM_GETSTATE, - ABM_GETTASKBARPOS, - ABM_ACTIVATE, - ABM_GETAUTOHIDEBAR, - ABM_SETAUTOHIDEBAR, - ABM_WINDOWPOSCHANGED, - ABM_SETSTATE - } - public static WindowSnapping Create(IntPtr hwnd) - { - return new WindowSnapping(hwnd); - } - - public void Snap(AppBarWindowPlacement placement, int size) - { - uint dpi = PInvoke.GetDpiForWindow(new HWND(hwnd)); - - double scalingFactor = dpi / 96d; - int actualSize = (int)(size * scalingFactor); - - Screen screen = Screen.FromHandle(hwnd); - - APPBARDATA32 appBarData = new(); - appBarData.cbSize = (uint)Marshal.SizeOf(appBarData); - appBarData.hWnd = new HWND(hwnd); - appBarData.uEdge = (uint)placement; - appBarData.rc = new RECT - { - left = (int)screen.Bounds.Left, - top = (int)screen.Bounds.Top, - right = (int)screen.Bounds.Right, - bottom = (int)screen.Bounds.Bottom - }; - - PInvoke.SHAppBarMessage((int)AppBarMsg.ABM_QUERYPOS, ref appBarData); - - switch (placement) - { - case AppBarWindowPlacement.Top: - appBarData.rc.bottom = appBarData.rc.top + actualSize; - break; - case AppBarWindowPlacement.Bottom: - appBarData.rc.top = appBarData.rc.bottom - actualSize; - break; - case AppBarWindowPlacement.Left: - appBarData.rc.right = appBarData.rc.left + actualSize; - break; - case AppBarWindowPlacement.Right: - appBarData.rc.left = appBarData.rc.right - actualSize; - break; - default: throw new NotSupportedException(); - } - - PInvoke.SHAppBarMessage((int)AppBarMsg.ABM_SETPOS, ref appBarData); - - PInvoke.SetWindowPos(new HWND(hwnd), new HWND(), - appBarData.rc.left, - appBarData.rc.top, - appBarData.rc.right - appBarData.rc.left, - appBarData.rc.bottom - appBarData.rc.top, 0); - } - - private void InitializeAppBarWindow() - { if (Environment.Is64BitProcess) { APPBARDATA64 appBarData = new(); @@ -107,4 +37,136 @@ public class WindowSnapping _ = PInvoke.SHAppBarMessage(0, ref appBarData); } } + + ~WindowSnapping() + { + Dispose(); + } + + public static WindowSnapping Create(IntPtr hwnd) => new(hwnd); + + public void Dispose() + { + GC.SuppressFinalize(this); + Remove(); + } + + public void Snap(WindowSnappingPlacement placement, + int size) + { + this.placement = placement; + uint dpi = PInvoke.GetDpiForWindow(new HWND(hwnd)); + + double scalingFactor = dpi / 96d; + int actualSize = (int)(size * scalingFactor); + + if (Environment.Is64BitProcess) + { + APPBARDATA64 appBarData = GetAppBarData64(); + appBarData.rc = GetScreen(); + + PInvoke.SHAppBarMessage(2, ref appBarData); + + switch (placement) + { + case WindowSnappingPlacement.Top: + appBarData.rc.bottom = appBarData.rc.top + actualSize; + break; + case WindowSnappingPlacement.Bottom: + appBarData.rc.top = appBarData.rc.bottom - actualSize; + break; + case WindowSnappingPlacement.Left: + appBarData.rc.right = appBarData.rc.left + actualSize; + break; + case WindowSnappingPlacement.Right: + appBarData.rc.left = appBarData.rc.right - actualSize; + break; + default: throw new NotSupportedException(); + } + + PInvoke.SHAppBarMessage(3, ref appBarData); + PInvoke.SetWindowPos(new HWND(hwnd), new HWND(), + appBarData.rc.left, + appBarData.rc.top, + appBarData.rc.right - appBarData.rc.left, + appBarData.rc.bottom - appBarData.rc.top, 0); + } + else + { + APPBARDATA32 appBarData = GetAppBarData32(); + appBarData.rc = GetScreen(); + + PInvoke.SHAppBarMessage(2, ref appBarData); + + switch (placement) + { + case WindowSnappingPlacement.Top: + appBarData.rc.bottom = appBarData.rc.top + actualSize; + break; + case WindowSnappingPlacement.Bottom: + appBarData.rc.top = appBarData.rc.bottom - actualSize; + break; + case WindowSnappingPlacement.Left: + appBarData.rc.right = appBarData.rc.left + actualSize; + break; + case WindowSnappingPlacement.Right: + appBarData.rc.left = appBarData.rc.right - actualSize; + break; + default: throw new NotSupportedException(); + } + + PInvoke.SHAppBarMessage(3, ref appBarData); + PInvoke.SetWindowPos(new HWND(hwnd), new HWND(), + appBarData.rc.left, + appBarData.rc.top, + appBarData.rc.right - appBarData.rc.left, + appBarData.rc.bottom - appBarData.rc.top, 0); + } + } + + private APPBARDATA32 GetAppBarData32() + { + APPBARDATA32 appBarData = new(); + appBarData.cbSize = (uint)Marshal.SizeOf(appBarData); + appBarData.hWnd = new HWND(hwnd); + appBarData.uEdge = (uint)placement; + + return appBarData; + } + + private APPBARDATA64 GetAppBarData64() + { + APPBARDATA64 appBarData = new(); + appBarData.cbSize = (uint)Marshal.SizeOf(appBarData); + appBarData.hWnd = new HWND(hwnd); + appBarData.uEdge = (uint)placement; + + return appBarData; + } + + private RECT GetScreen() + { + Screen screen = Screen.FromHandle(hwnd); + return new RECT + { + left = (int)screen.Bounds.Left, + top = (int)screen.Bounds.Top, + right = (int)screen.Bounds.Right, + bottom = (int)screen.Bounds.Bottom + }; + } + + private void Remove() + { + if (Environment.Is64BitProcess) + { + APPBARDATA64 appBarData = GetAppBarData64(); + PInvoke.SHAppBarMessage(2, ref appBarData); + } + else + { + APPBARDATA64 appBarData = GetAppBarData64(); + PInvoke.SHAppBarMessage(1, ref appBarData); + } + } } diff --git a/Hyperbar.Windows.Interop/AppBarWindowPlacement.cs b/Hyperbar.Windows.Interop/WindowSnappingPlacement.cs similarity index 70% rename from Hyperbar.Windows.Interop/AppBarWindowPlacement.cs rename to Hyperbar.Windows.Interop/WindowSnappingPlacement.cs index 7303505..72651f9 100644 --- a/Hyperbar.Windows.Interop/AppBarWindowPlacement.cs +++ b/Hyperbar.Windows.Interop/WindowSnappingPlacement.cs @@ -1,6 +1,6 @@ namespace Hyperbar.Windows.Interop; -public enum AppBarWindowPlacement +public enum WindowSnappingPlacement { Left, Top, diff --git a/Hyperbar.Windows.Primary/PrimaryCommandConfiguration.cs b/Hyperbar.Windows.Primary/PrimaryCommandConfiguration.cs index c8540cf..ce9816b 100644 --- a/Hyperbar.Windows.Primary/PrimaryCommandConfiguration.cs +++ b/Hyperbar.Windows.Primary/PrimaryCommandConfiguration.cs @@ -12,5 +12,5 @@ public class PrimaryCommandConfiguration public required string Text { get; set; } - public List? Commands { get; set; } = []; + public List Commands { get; set; } = []; } \ No newline at end of file diff --git a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs index cba4127..a15eae4 100644 --- a/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs +++ b/Hyperbar.Windows.Primary/PrimaryWidgetConfigurationHandler.cs @@ -9,15 +9,14 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator, public async ValueTask Handle(ConfigurationChanged notification, CancellationToken cancellationToken) { - foreach (KeyValuePair item in cache) - { - if (configuration.FirstOrDefault(x => x.Id == item.Key) == null) - { - await mediator.PublishAsync(new Removed(item.Value), - cancellationToken); + HashSet configurationIds = new(configuration.SelectMany(item => new[] { item } + .Concat(item.Commands).Select(x => x.Id))); - cache.Remove(item.Key); - } + foreach (KeyValuePair item in cache.Where(x => !configurationIds.Contains(x.Key))) + { + await mediator.PublishAsync(new Removed(item.Value), + cancellationToken); + cache.Remove(item.Key); } foreach (PrimaryCommandConfiguration item in configuration) diff --git a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs b/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs index 957e5a8..f7d03d8 100644 --- a/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs +++ b/Hyperbar.Windows.Primary/WidgetComponentViewModelFactory.cs @@ -25,7 +25,6 @@ public class WidgetComponentViewModelFactory(IServiceFactory service, if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations) { List childViewModels = []; - foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations) { WidgetComponentViewModel? childViewModel = null; diff --git a/Hyperbar.Windows.UI/Extensions/WindowPlacement.cs b/Hyperbar.Windows.UI/Extensions/WindowPlacement.cs deleted file mode 100644 index f7bc061..0000000 --- a/Hyperbar.Windows.UI/Extensions/WindowPlacement.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Hyperbar.Windows.UI; - -public enum WindowPlacement -{ - Left = 0, - Top = 1, - Right = 2, - Bottom = 3, -}