Fixed bug where items are destroyed during configuration update

This commit is contained in:
TheXamlGuy
2024-01-17 18:58:57 +00:00
parent 2f22c81384
commit 67b0456cbd
8 changed files with 162 additions and 108 deletions
@@ -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)
{
@@ -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,
+136 -74
View File
@@ -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);
}
}
}
@@ -1,6 +1,6 @@
namespace Hyperbar.Windows.Interop;
public enum AppBarWindowPlacement
public enum WindowSnappingPlacement
{
Left,
Top,
@@ -12,5 +12,5 @@ public class PrimaryCommandConfiguration
public required string Text { get; set; }
public List<PrimaryCommandConfiguration>? Commands { get; set; } = [];
public List<PrimaryCommandConfiguration> Commands { get; set; } = [];
}
@@ -9,15 +9,14 @@ public class PrimaryWidgetConfigurationHandler(IMediator mediator,
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
CancellationToken cancellationToken)
{
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> item in cache)
{
if (configuration.FirstOrDefault(x => x.Id == item.Key) == null)
{
await mediator.PublishAsync(new Removed<IWidgetComponentViewModel>(item.Value),
cancellationToken);
HashSet<Guid> configurationIds = new(configuration.SelectMany(item => new[] { item }
.Concat(item.Commands).Select(x => x.Id)));
cache.Remove(item.Key);
}
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> item in cache.Where(x => !configurationIds.Contains(x.Key)))
{
await mediator.PublishAsync(new Removed<IWidgetComponentViewModel>(item.Value),
cancellationToken);
cache.Remove(item.Key);
}
foreach (PrimaryCommandConfiguration item in configuration)
@@ -25,7 +25,6 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations)
{
List<IWidgetComponentViewModel> childViewModels = [];
foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations)
{
WidgetComponentViewModel? childViewModel = null;
@@ -1,9 +0,0 @@
namespace Hyperbar.Windows.UI;
public enum WindowPlacement
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3,
}