Fixed bug where items are destroyed during configuration update
This commit is contained in:
@@ -11,8 +11,8 @@ namespace Hyperbar.Windows.Controls;
|
|||||||
internal class DesktopBarHost : Window
|
internal class DesktopBarHost : Window
|
||||||
{
|
{
|
||||||
private readonly DesktopBarPresenter presenter;
|
private readonly DesktopBarPresenter presenter;
|
||||||
private DesktopBarPlacemenet placement;
|
|
||||||
private readonly WindowSnapping windowSnapping;
|
private readonly WindowSnapping windowSnapping;
|
||||||
|
private DesktopBarPlacemenet placement;
|
||||||
|
|
||||||
public DesktopBarHost(DesktopBarPresenter presenter)
|
public DesktopBarHost(DesktopBarPresenter presenter)
|
||||||
{
|
{
|
||||||
@@ -29,6 +29,8 @@ internal class DesktopBarHost : Window
|
|||||||
this.presenter = presenter;
|
this.presenter = presenter;
|
||||||
presenter.Loaded += OnLoaded;
|
presenter.Loaded += OnLoaded;
|
||||||
Content = presenter;
|
Content = presenter;
|
||||||
|
|
||||||
|
Closed += OnClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdatePlacement(DesktopBarPlacemenet placement)
|
internal void UpdatePlacement(DesktopBarPlacemenet placement)
|
||||||
@@ -45,19 +47,19 @@ internal class DesktopBarHost : Window
|
|||||||
switch (placement)
|
switch (placement)
|
||||||
{
|
{
|
||||||
case DesktopBarPlacemenet.Left:
|
case DesktopBarPlacemenet.Left:
|
||||||
windowSnapping.Snap(AppBarWindowPlacement.Left, (int)size);
|
windowSnapping.Snap(WindowSnappingPlacement.Left, (int)size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DesktopBarPlacemenet.Top:
|
case DesktopBarPlacemenet.Top:
|
||||||
windowSnapping.Snap(AppBarWindowPlacement.Top, (int)size);
|
windowSnapping.Snap(WindowSnappingPlacement.Top, (int)size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DesktopBarPlacemenet.Right:
|
case DesktopBarPlacemenet.Right:
|
||||||
windowSnapping.Snap(AppBarWindowPlacement.Right, (int)size);
|
windowSnapping.Snap(WindowSnappingPlacement.Right, (int)size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DesktopBarPlacemenet.Bottom:
|
case DesktopBarPlacemenet.Bottom:
|
||||||
windowSnapping.Snap(AppBarWindowPlacement.Bottom, (int)size);
|
windowSnapping.Snap(WindowSnappingPlacement.Bottom, (int)size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -67,6 +69,10 @@ internal class DesktopBarHost : Window
|
|||||||
presenter.UpdatePlacementState(placement);
|
presenter.UpdatePlacementState(placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClosed(object sender, WindowEventArgs args)
|
||||||
|
{
|
||||||
|
windowSnapping.Dispose();
|
||||||
|
}
|
||||||
private void OnLoaded(object sender,
|
private void OnLoaded(object sender,
|
||||||
RoutedEventArgs args)
|
RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ public class WindowMessageListener :
|
|||||||
protected WindowMessageListener(IntPtr hwnd)
|
protected WindowMessageListener(IntPtr hwnd)
|
||||||
{
|
{
|
||||||
this.hwnd = hwnd;
|
this.hwnd = hwnd;
|
||||||
Set();
|
|
||||||
|
callback = new SUBCLASSPROC(WindowProc);
|
||||||
|
PInvoke.SetWindowSubclass(new HWND(hwnd), callback, 101, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
~WindowMessageListener()
|
~WindowMessageListener()
|
||||||
@@ -22,19 +24,14 @@ public class WindowMessageListener :
|
|||||||
|
|
||||||
public static WindowMessageListener Create(IntPtr hwnd) => new(hwnd);
|
public static WindowMessageListener Create(IntPtr hwnd) => new(hwnd);
|
||||||
|
|
||||||
public void Dispose() => Remove();
|
public void Dispose()
|
||||||
|
|
||||||
private void Remove()
|
|
||||||
{
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
PInvoke.RemoveWindowSubclass(new HWND(hwnd), callback, 101);
|
PInvoke.RemoveWindowSubclass(new HWND(hwnd), callback, 101);
|
||||||
callback = null;
|
callback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Set()
|
|
||||||
{
|
|
||||||
callback = new SUBCLASSPROC(WindowProc);
|
|
||||||
PInvoke.SetWindowSubclass(new HWND(hwnd), callback, 101, 0);
|
|
||||||
}
|
|
||||||
private LRESULT WindowProc(HWND hWnd, uint uMsg,
|
private LRESULT WindowProc(HWND hWnd, uint uMsg,
|
||||||
WPARAM wParam,
|
WPARAM wParam,
|
||||||
LPARAM lParam,
|
LPARAM lParam,
|
||||||
|
|||||||
@@ -4,87 +4,17 @@ using Windows.Win32.Foundation;
|
|||||||
|
|
||||||
namespace Hyperbar.Windows.Interop;
|
namespace Hyperbar.Windows.Interop;
|
||||||
|
|
||||||
public class WindowSnapping
|
public class WindowSnapping :
|
||||||
|
IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly uint callback;
|
||||||
private readonly nint hwnd;
|
private readonly nint hwnd;
|
||||||
private uint callback;
|
private WindowSnappingPlacement placement;
|
||||||
|
|
||||||
public WindowSnapping(IntPtr hwnd)
|
public WindowSnapping(IntPtr hwnd)
|
||||||
{
|
{
|
||||||
this.hwnd = 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)
|
if (Environment.Is64BitProcess)
|
||||||
{
|
{
|
||||||
APPBARDATA64 appBarData = new();
|
APPBARDATA64 appBarData = new();
|
||||||
@@ -107,4 +37,136 @@ public class WindowSnapping
|
|||||||
_ = PInvoke.SHAppBarMessage(0, ref appBarData);
|
_ = 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
-1
@@ -1,6 +1,6 @@
|
|||||||
namespace Hyperbar.Windows.Interop;
|
namespace Hyperbar.Windows.Interop;
|
||||||
|
|
||||||
public enum AppBarWindowPlacement
|
public enum WindowSnappingPlacement
|
||||||
{
|
{
|
||||||
Left,
|
Left,
|
||||||
Top,
|
Top,
|
||||||
@@ -12,5 +12,5 @@ public class PrimaryCommandConfiguration
|
|||||||
|
|
||||||
public required string Text { get; set; }
|
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,
|
public async ValueTask Handle(ConfigurationChanged<PrimaryWidgetConfiguration> notification,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<Guid, IWidgetComponentViewModel> item in cache)
|
HashSet<Guid> configurationIds = new(configuration.SelectMany(item => new[] { item }
|
||||||
{
|
.Concat(item.Commands).Select(x => x.Id)));
|
||||||
if (configuration.FirstOrDefault(x => x.Id == item.Key) == null)
|
|
||||||
{
|
|
||||||
await mediator.PublishAsync(new Removed<IWidgetComponentViewModel>(item.Value),
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
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)
|
foreach (PrimaryCommandConfiguration item in configuration)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ public class WidgetComponentViewModelFactory(IServiceFactory service,
|
|||||||
if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations)
|
if (processCommandConfiguration.Commands is { Count: > 0 } childCommandConfigurations)
|
||||||
{
|
{
|
||||||
List<IWidgetComponentViewModel> childViewModels = [];
|
List<IWidgetComponentViewModel> childViewModels = [];
|
||||||
|
|
||||||
foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations)
|
foreach (PrimaryCommandConfiguration childCommandConfiguration in childCommandConfigurations)
|
||||||
{
|
{
|
||||||
WidgetComponentViewModel? childViewModel = null;
|
WidgetComponentViewModel? childViewModel = null;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Hyperbar.Windows.UI;
|
|
||||||
|
|
||||||
public enum WindowPlacement
|
|
||||||
{
|
|
||||||
Left = 0,
|
|
||||||
Top = 1,
|
|
||||||
Right = 2,
|
|
||||||
Bottom = 3,
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user