This commit is contained in:
TheXamlGuy
2024-01-17 17:16:50 +00:00
parent 86603a6567
commit 2f22c81384
21 changed files with 471 additions and 231 deletions
@@ -2,30 +2,30 @@
namespace Hyperbar.Windows.Controls; namespace Hyperbar.Windows.Controls;
public class DesktopFlyout : public class DesktopBar :
DependencyObject DependencyObject
{ {
public static readonly DependencyProperty ContentProperty = public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register(nameof(Content), DependencyProperty.Register(nameof(Content),
typeof(object), typeof(DesktopFlyout), typeof(object), typeof(DesktopBar),
new PropertyMetadata(null)); new PropertyMetadata(null));
public static readonly DependencyProperty PlacementProperty = public static readonly DependencyProperty PlacementProperty =
DependencyProperty.Register(nameof(Placement), DependencyProperty.Register(nameof(Placement),
typeof(DesktopFlyoutPlacement), typeof(DesktopFlyout), typeof(DesktopBarPlacemenet), typeof(DesktopBar),
new PropertyMetadata(DesktopFlyoutPlacement.Left, OnPlacementPropertyChanged)); new PropertyMetadata(DesktopBarPlacemenet.Left, OnPlacementPropertyChanged));
private readonly DesktopFlyoutHost host; private readonly DesktopBarHost host;
private readonly DesktopFlyoutPresenter presenter; private readonly DesktopBarPresenter presenter;
public DesktopFlyout() public DesktopBar()
{ {
presenter = new DesktopFlyoutPresenter presenter = new DesktopBarPresenter
{ {
Parent = this Parent = this
}; };
host = new DesktopFlyoutHost(presenter); host = new DesktopBarHost(presenter);
host.Activate(); host.Activate();
} }
@@ -35,16 +35,16 @@ public class DesktopFlyout :
set => SetValue(ContentProperty, value); set => SetValue(ContentProperty, value);
} }
public DesktopFlyoutPlacement Placement public DesktopBarPlacemenet Placement
{ {
get => (DesktopFlyoutPlacement)GetValue(PlacementProperty); get => (DesktopBarPlacemenet)GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value); set => SetValue(PlacementProperty, value);
} }
private static void OnPlacementPropertyChanged(DependencyObject dependencyObject, private static void OnPlacementPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs args) DependencyPropertyChangedEventArgs args)
{ {
if (dependencyObject is DesktopFlyout sender) if (dependencyObject is DesktopBar sender)
{ {
sender.OnPlacementPropertyChanged(); sender.OnPlacementPropertyChanged();
} }
@@ -0,0 +1,76 @@
using Microsoft.UI.Xaml;
using Hyperbar.Windows.UI;
using Windows.Foundation;
using WindowStyle = Hyperbar.Windows.Interop.WindowStyle;
using ExtendedWindowStyle = Hyperbar.Windows.Interop.ExtendedWindowStyle;
using Hyperbar.Windows.Interop;
using Microsoft.UI.Xaml.Media;
namespace Hyperbar.Windows.Controls;
internal class DesktopBarHost : Window
{
private readonly DesktopBarPresenter presenter;
private DesktopBarPlacemenet placement;
private readonly WindowSnapping windowSnapping;
public DesktopBarHost(DesktopBarPresenter presenter)
{
this.SetOpacity(0);
this.SetStyle(WindowStyle.SysMenu | WindowStyle.Visible);
this.SetStyle(ExtendedWindowStyle.NoActivate);
this.MoveAndResize(0, 0, 0, 0);
this.SetTopMost(true);
this.SetIsAvailableInSwitchers(false);
SystemBackdrop = new MicaBackdrop();
windowSnapping = WindowSnapping.Create(this.GetHandle());
this.presenter = presenter;
presenter.Loaded += OnLoaded;
Content = presenter;
}
internal void UpdatePlacement(DesktopBarPlacemenet placement)
{
this.placement = placement;
UpdatePlacement();
}
internal void UpdatePlacement()
{
presenter.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double size = Math.Min(presenter.DesiredSize.Height, presenter.DesiredSize.Width);
switch (placement)
{
case DesktopBarPlacemenet.Left:
windowSnapping.Snap(AppBarWindowPlacement.Left, (int)size);
break;
case DesktopBarPlacemenet.Top:
windowSnapping.Snap(AppBarWindowPlacement.Top, (int)size);
break;
case DesktopBarPlacemenet.Right:
windowSnapping.Snap(AppBarWindowPlacement.Right, (int)size);
break;
case DesktopBarPlacemenet.Bottom:
windowSnapping.Snap(AppBarWindowPlacement.Bottom, (int)size);
break;
default:
break;
}
presenter.UpdatePlacementState(placement);
}
private void OnLoaded(object sender,
RoutedEventArgs args)
{
UpdatePlacement();
this.SetOpacity(255);
}
}
@@ -1,6 +1,6 @@
namespace Hyperbar.Windows.Controls; namespace Hyperbar.Windows.Controls;
public enum DesktopFlyoutPlacement public enum DesktopBarPlacemenet
{ {
Left, Left,
Top, Top,
@@ -4,20 +4,20 @@ using Microsoft.UI.Xaml.Data;
namespace Hyperbar.Windows.Controls; namespace Hyperbar.Windows.Controls;
public class DesktopFlyoutPresenter : public class DesktopBarPresenter :
ContentControl ContentControl
{ {
public static readonly DependencyProperty TemplateSettingsProperty = public static readonly DependencyProperty TemplateSettingsProperty =
DependencyProperty.Register(nameof(TemplateSettings), DependencyProperty.Register(nameof(TemplateSettings),
typeof(DesktopFlyoutPresenterTemplateSettings), typeof(DesktopFlyoutPresenter), typeof(DesktopBarPresenterTemplateSettings), typeof(DesktopBarPresenter),
new PropertyMetadata(null)); new PropertyMetadata(null));
internal new DesktopFlyout? Parent; internal new DesktopBar? Parent;
public DesktopFlyoutPresenter() public DesktopBarPresenter()
{ {
DefaultStyleKey = typeof(DesktopFlyoutPresenter); DefaultStyleKey = typeof(DesktopBarPresenter);
TemplateSettings = new DesktopFlyoutPresenterTemplateSettings(); TemplateSettings = new DesktopBarPresenterTemplateSettings();
} }
protected override void OnApplyTemplate() protected override void OnApplyTemplate()
@@ -30,11 +30,11 @@ public class DesktopFlyoutPresenter :
}); });
} }
public DesktopFlyoutPresenterTemplateSettings TemplateSettings public DesktopBarPresenterTemplateSettings TemplateSettings
{ {
get => (DesktopFlyoutPresenterTemplateSettings)GetValue(TemplateSettingsProperty); get => (DesktopBarPresenterTemplateSettings)GetValue(TemplateSettingsProperty);
set => SetValue(TemplateSettingsProperty, value); set => SetValue(TemplateSettingsProperty, value);
} }
internal void UpdatePlacementState(DesktopFlyoutPlacement placement) => VisualStateManager.GoToState(this, $"{placement}Placement", true); internal void UpdatePlacementState(DesktopBarPlacemenet placement) => VisualStateManager.GoToState(this, $"{placement}Placement", true);
} }
@@ -5,32 +5,30 @@
xmlns:controls="using:Hyperbar.Windows.Controls"> xmlns:controls="using:Hyperbar.Windows.Controls">
<ResourceDictionary.ThemeDictionaries> <ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default"> <ResourceDictionary x:Key="Default">
<StaticResource x:Key="DesktopFlyoutPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" /> <StaticResource x:Key="DesktopBarPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
<StaticResource x:Key="DesktopFlyoutPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" /> <StaticResource x:Key="DesktopBarPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="DesktopFlyoutPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" /> <StaticResource x:Key="DesktopBarPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Key="Light"> <ResourceDictionary x:Key="Light">
<StaticResource x:Key="DesktopFlyoutPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" /> <StaticResource x:Key="DesktopBarPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
<StaticResource x:Key="DesktopFlyoutPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" /> <StaticResource x:Key="DesktopBarPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="DesktopFlyoutPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" /> <StaticResource x:Key="DesktopBarPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
<Thickness x:Key="DesktopFlyoutPresenterBorderThemeThickness">1</Thickness> <Thickness x:Key="DesktopBarPresenterBorderThemeThickness">0</Thickness>
<Style TargetType="controls:DesktopFlyoutPresenter"> <Style TargetType="controls:DesktopBarPresenter">
<Setter Property="Background" Value="{ThemeResource DesktopFlyoutPresenterBackground}" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{ThemeResource DesktopFlyoutPresenterForeground}" /> <Setter Property="Foreground" Value="{ThemeResource DesktopBarPresenterForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource DesktopFlyoutPresenterBorderBrush}" /> <Setter Property="BorderBrush" Value="{ThemeResource DesktopBarPresenterBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource DesktopFlyoutPresenterBorderThemeThickness}" /> <Setter Property="BorderThickness" Value="{ThemeResource DesktopBarPresenterBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{ThemeResource OverlayCornerRadius}" />
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="controls:DesktopFlyoutPresenter"> <ControlTemplate TargetType="controls:DesktopBarPresenter">
<Border x:Name="Container" Background="Transparent"> <Border x:Name="Container" Background="Transparent">
<Border <Border
x:Name="BackgroundElement" x:Name="BackgroundElement"
MinWidth="48"
MinHeight="48" MinHeight="48"
MinWidth="40"
Margin="16"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BackgroundSizing="OuterBorderEdge" BackgroundSizing="OuterBorderEdge"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
@@ -49,18 +47,10 @@
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlacementStates"> <VisualStateGroup x:Name="PlacementStates">
<VisualState x:Name="DefaultPlacement" /> <VisualState x:Name="DefaultPlacement" />
<VisualState x:Name="BottomPlacement"> <VisualState x:Name="BottomPlacement" />
<VisualState x:Name="TopPlacement" />
</VisualState> <VisualState x:Name="LeftPlacement" />
<VisualState x:Name="TopPlacement"> <VisualState x:Name="RightPlacement" />
</VisualState>
<VisualState x:Name="LeftPlacement">
</VisualState>
<VisualState x:Name="RightPlacement">
</VisualState>
</VisualStateGroup> </VisualStateGroup>
</VisualStateManager.VisualStateGroups> </VisualStateManager.VisualStateGroups>
</Border> </Border>
@@ -2,26 +2,26 @@
namespace Hyperbar.Windows.Controls; namespace Hyperbar.Windows.Controls;
public class DesktopFlyoutPresenterTemplateSettings : DependencyObject public class DesktopBarPresenterTemplateSettings : DependencyObject
{ {
public static readonly DependencyProperty HeightProperty = public static readonly DependencyProperty HeightProperty =
DependencyProperty.Register(nameof(Height), DependencyProperty.Register(nameof(Height),
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings), typeof(double), typeof(DesktopBarPresenterTemplateSettings),
new PropertyMetadata(0d)); new PropertyMetadata(0d));
public static readonly DependencyProperty NegativeHeightProperty = public static readonly DependencyProperty NegativeHeightProperty =
DependencyProperty.Register(nameof(NegativeHeight), DependencyProperty.Register(nameof(NegativeHeight),
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings), typeof(double), typeof(DesktopBarPresenterTemplateSettings),
new PropertyMetadata(0d)); new PropertyMetadata(0d));
public static readonly DependencyProperty NegativeWidthProperty = public static readonly DependencyProperty NegativeWidthProperty =
DependencyProperty.Register(nameof(NegativeWidth), DependencyProperty.Register(nameof(NegativeWidth),
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings), typeof(double), typeof(DesktopBarPresenterTemplateSettings),
new PropertyMetadata(0d)); new PropertyMetadata(0d));
public static readonly DependencyProperty WidthProperty = public static readonly DependencyProperty WidthProperty =
DependencyProperty.Register(nameof(Width), DependencyProperty.Register(nameof(Width),
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings), typeof(double), typeof(DesktopBarPresenterTemplateSettings),
new PropertyMetadata(0d)); new PropertyMetadata(0d));
public double Height public double Height
@@ -1,104 +0,0 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Hyperbar.Windows.UI;
using Windows.Foundation;
using WinUIEx;
using WindowStyle = Hyperbar.Windows.Interop.WindowStyle;
using ExtendedWindowStyle = Hyperbar.Windows.Interop.ExtendedWindowStyle;
namespace Hyperbar.Windows.Controls;
internal class DesktopFlyoutHost : Window
{
private readonly DesktopFlyoutPresenter presenter;
private bool loaded;
private DesktopFlyoutPlacement placement;
public DesktopFlyoutHost(DesktopFlyoutPresenter presenter)
{
SystemBackdrop = new TransparentTintBackdrop();
this.SetOpacity(0);
this.Snap(WindowPlacement.Top, 0, 0);
this.SetStyle(WindowStyle.SysMenu | WindowStyle.Visible);
this.SetStyle(ExtendedWindowStyle.NoActivate);
this.SetTopMost(true);
this.SetIsAvailableInSwitchers(false);
Border root = new();
root.Loaded += OnLoaded;
Content = root;
this.presenter = presenter;
}
internal void UpdatePlacement(DesktopFlyoutPlacement placement)
{
this.placement = placement;
// Not ready
if (!loaded)
{
return;
}
presenter.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double height = presenter.DesiredSize.Height;
double width = presenter.DesiredSize.Width;
switch (placement)
{
case DesktopFlyoutPlacement.Left:
this.Snap(WindowPlacement.Left, height, width);
break;
case DesktopFlyoutPlacement.Top:
this.Snap(WindowPlacement.Top, width, height);
break;
case DesktopFlyoutPlacement.Right:
this.Snap(WindowPlacement.Right, height, width);
break;
case DesktopFlyoutPlacement.Bottom:
this.Snap(WindowPlacement.Bottom, width, height);
break;
default:
break;
}
presenter.TemplateSettings.SetValue(DesktopFlyoutPresenterTemplateSettings.HeightProperty, height);
presenter.TemplateSettings.SetValue(DesktopFlyoutPresenterTemplateSettings.WidthProperty, width);
presenter.TemplateSettings.SetValue(DesktopFlyoutPresenterTemplateSettings.NegativeHeightProperty, -height);
presenter.TemplateSettings.SetValue(DesktopFlyoutPresenterTemplateSettings.NegativeWidthProperty, -width);
presenter.UpdatePlacementState(placement);
}
private void OnChildSizeChanged(object sender,
SizeChangedEventArgs args) => UpdatePlacement(placement);
private void OnLoaded(object sender,
RoutedEventArgs args)
{
this.SetOpacity(255);
if (Content is Border border)
{
border.Child = presenter;
double height = presenter.DesiredSize.Height;
double width = presenter.DesiredSize.Width;
presenter.SizeChanged += OnChildSizeChanged;
}
loaded = true;
UpdatePlacement(placement);
}
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Hyperbar.Windows.Controls/DesktopFlyout/DesktopFlyoutPresenter.xaml" /> <ResourceDictionary Source="ms-appx:///Hyperbar.Windows.Controls/DesktopBar/DesktopBarPresenter.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
@@ -0,0 +1,9 @@
namespace Hyperbar.Windows.Interop;
public enum AppBarWindowPlacement
{
Left,
Top,
Right,
Bottom
}
+5 -52
View File
@@ -1,5 +1,4 @@
using System; using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi; using Windows.Win32.Graphics.Gdi;
@@ -30,8 +29,10 @@ public static class HwndExtensions
SET_WINDOW_POS_FLAGS.SWP_NOZORDER); SET_WINDOW_POS_FLAGS.SWP_NOZORDER);
} }
public static uint GetDpiForWindow(IntPtr hwnd) => PInvoke.GetDpiForWindow(new HWND(hwnd));
public static void SetWindowOpacity(this IntPtr hWnd, public static void SetWindowOpacity(this IntPtr hWnd,
byte value) byte value)
{ {
HWND hWND = new(hWnd); HWND hWND = new(hWnd);
WindowStyles windowLong = (WindowStyles)PInvoke.GetWindowLong(hWND, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE); WindowStyles windowLong = (WindowStyles)PInvoke.GetWindowLong(hWND, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
@@ -45,6 +46,7 @@ public static class HwndExtensions
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
} }
} }
public static void SetWindowStyle(this IntPtr hwnd, public static void SetWindowStyle(this IntPtr hwnd,
WindowStyle newStyle) WindowStyle newStyle)
{ {
@@ -56,53 +58,4 @@ public static class HwndExtensions
PInvoke.SetWindowPos(new HWND(hwnd), new HWND(0), 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_DRAWFRAME | SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOOWNERZORDER | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER); PInvoke.SetWindowPos(new HWND(hwnd), new HWND(0), 0, 0, 0, 0, SET_WINDOW_POS_FLAGS.SWP_DRAWFRAME | SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOOWNERZORDER | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER);
} }
public static void SnapWindow(this IntPtr hwnd,
int placement,
double? width = null,
double? height = null)
{
HMONITOR hwndDesktop = PInvoke.MonitorFromWindow(new(hwnd), MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
MONITORINFO info = new()
{
cbSize = 40
};
PInvoke.GetMonitorInfo(hwndDesktop, ref info);
uint dpi = PInvoke.GetDpiForWindow(new HWND(hwnd));
PInvoke.GetWindowRect(new HWND(hwnd), out RECT windowRect);
double scalingFactor = dpi / 96d;
int actualWidth = width.HasValue ? (int)(width * scalingFactor) : windowRect.right - windowRect.left;
int actualHeight = height.HasValue ? (int)(height * scalingFactor) : windowRect.bottom - windowRect.top;
int left = 0;
int top = 0;
switch (placement)
{
case 0:
left = 0;
top = (info.rcWork.bottom + info.rcWork.top) / 2 - actualHeight / 2;
break;
case 1:
left = (info.rcWork.left + info.rcWork.right) / 2 - actualWidth / 2;
top = 0;
break;
case 2:
left = info.rcWork.left + info.rcWork.right - actualWidth;
top = (info.rcWork.bottom + info.rcWork.top) / 2 - actualHeight / 2;
break;
case 3:
left = (info.rcWork.left + info.rcWork.right) / 2 - actualWidth / 2;
top = info.rcWork.bottom + info.rcWork.top - actualHeight;
break;
}
PInvoke.SetWindowPos(new HWND(hwnd), new HWND(), left, top, actualWidth, actualHeight, 0);
}
} }
+2 -1
View File
@@ -56,4 +56,5 @@ CreateSolidBrush
FillRect FillRect
GetDC GetDC
SendInput SendInput
MapVirtualKey MapVirtualKey
MoveWindow
+111
View File
@@ -0,0 +1,111 @@
using System.Runtime.InteropServices;
using Windows.Foundation;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using Windows.Win32.Graphics.Gdi;
namespace Hyperbar.Windows.Interop;
public class Screen
{
private const int CCHDEVICENAME = 32;
private const int PRIMARY_MONITOR = unchecked((int)0xBAADF00D);
private static readonly bool multiMonitorSupport;
private readonly IntPtr monitorHandle;
static Screen()
{
multiMonitorSupport = PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CMONITORS) != 0;
}
private Screen(IntPtr monitorHandle)
{
if (!multiMonitorSupport || monitorHandle == PRIMARY_MONITOR)
{
Bounds = SystemInformationHelper.VirtualScreen;
Primary = true;
DeviceName = "DISPLAY";
}
else
{
MonitorData monitorData = GetMonitorData(monitorHandle);
Bounds = new Rect(monitorData.MonitorRect.left, monitorData.MonitorRect.top, monitorData.MonitorRect.right - monitorData.MonitorRect.left, monitorData.MonitorRect.bottom - monitorData.MonitorRect.top);
Primary = (monitorData.Flags & (int)MonitorFlag.MONITOR_DEFAULTTOPRIMARY) != 0;
DeviceName = monitorData.DeviceName;
}
this.monitorHandle = monitorHandle;
}
private enum MonitorFlag : uint
{
MONITOR_DEFAULTTONULL = 0,
MONITOR_DEFAULTTOPRIMARY = 1,
MONITOR_DEFAULTTONEAREST = 2
}
public Rect Bounds { get; }
public string DeviceName { get; }
public bool Primary { get; }
public Rect WorkingArea => GetWorkingArea();
public static Screen FromHandle(IntPtr handle)
{
return multiMonitorSupport ? new Screen(PInvoke.MonitorFromWindow((HWND)handle, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST)) : new Screen((IntPtr)PRIMARY_MONITOR);
}
public override bool Equals(object? obj)
{
if (obj is not Screen monitor) return false;
return monitorHandle == monitor.monitorHandle;
}
public override int GetHashCode()
{
return (int)monitorHandle;
}
[DllImport("user32.dll", EntryPoint = "GetMonitorInfo", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetMonitorInfoEx(IntPtr hMonitor, ref MonitorData lpmi);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
private MonitorData GetMonitorData(IntPtr monitorHandle)
{
MonitorData monitorData = new();
monitorData.Size = Marshal.SizeOf(monitorData);
GetMonitorInfoEx(monitorHandle, ref monitorData);
return monitorData;
}
private Rect GetWorkingArea()
{
if (!multiMonitorSupport || monitorHandle == PRIMARY_MONITOR)
{
return SystemInformationHelper.WorkingArea;
}
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);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct MonitorData
{
public int Size;
public RECT MonitorRect;
public RECT WorkAreaRect;
public uint Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string DeviceName;
}
}
@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
using Windows.Foundation;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
namespace Hyperbar.Windows.Interop;
internal static class SystemInformationHelper
{
private const int SPI_GETWORKAREA = 48;
public static Rect VirtualScreen => GetVirtualScreen();
public static Rect WorkingArea => GetWorkingArea();
private static Rect GetVirtualScreen()
{
Size size = new(PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSCREEN),
PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSCREEN));
return new Rect(0, 0, size.Width, size.Height);
}
private static Rect GetWorkingArea()
{
RECT rect = new();
SystemParametersInfo(SPI_GETWORKAREA, 0, ref rect, 0);
return new Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
}
@@ -0,0 +1,46 @@
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
namespace Hyperbar.Windows.Interop;
public class WindowMessageListener :
IDisposable
{
private readonly nint hwnd;
private SUBCLASSPROC? callback;
protected WindowMessageListener(IntPtr hwnd)
{
this.hwnd = hwnd;
Set();
}
~WindowMessageListener()
{
Dispose();
}
public static WindowMessageListener Create(IntPtr hwnd) => new(hwnd);
public void Dispose() => Remove();
private void Remove()
{
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,
nuint uIdSubclass,
nuint dwRefData)
{
return PInvoke.DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
}
+110
View File
@@ -0,0 +1,110 @@
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
namespace Hyperbar.Windows.Interop;
public class WindowSnapping
{
private readonly nint hwnd;
private uint callback;
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();
appBarData.cbSize = (uint)Marshal.SizeOf(appBarData);
appBarData.hWnd = new HWND(hwnd);
callback = PInvoke.RegisterWindowMessage("AppBarMessage64");
appBarData.uCallbackMessage = callback;
_ = PInvoke.SHAppBarMessage(0, ref appBarData);
}
else
{
APPBARDATA32 appBarData = new();
appBarData.cbSize = (uint)Marshal.SizeOf(appBarData);
appBarData.hWnd = new HWND(hwnd);
callback = PInvoke.RegisterWindowMessage("AppBarMessage32");
appBarData.uCallbackMessage = callback;
_ = PInvoke.SHAppBarMessage(0, ref appBarData);
}
}
}
@@ -1,17 +1,31 @@
using Hyperbar.Windows.Interop; using Hyperbar.Windows.Interop;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Windows.Graphics;
using WinRT.Interop; using WinRT.Interop;
namespace Hyperbar.Windows.UI; namespace Hyperbar.Windows.UI;
public static class WindowExtensions public static class WindowExtensions
{ {
public static WindowMessageListener CreateMessageListener(this Window window) =>
WindowMessageListener.Create(window.GetHandle());
public static IntPtr GetHandle(this Window window) => public static IntPtr GetHandle(this Window window) =>
window is not null ? WindowNative.GetWindowHandle(window) : default; window is not null ? WindowNative.GetWindowHandle(window) : default;
public static void MoveAndResize(this Window window,
double x,
double y,
double width,
double height)
{
float num = HwndExtensions.GetDpiForWindow(window.GetHandle()) / 96f;
window.AppWindow.MoveAndResize(new RectInt32((int)x, (int)y, (int)(width * (double)num), (int)(height * (double)num)));
}
public static void SetIsAvailableInSwitchers(this Window window, public static void SetIsAvailableInSwitchers(this Window window,
bool value) => window.AppWindow.IsShownInSwitchers = value; bool value) => window.AppWindow.IsShownInSwitchers = value;
public static void SetOpacity(this Window window, public static void SetOpacity(this Window window,
byte value) => window.GetHandle().SetWindowOpacity(value); byte value) => window.GetHandle().SetWindowOpacity(value);
@@ -31,9 +45,4 @@ public static class WindowExtensions
presenter.IsAlwaysOnTop = value; presenter.IsAlwaysOnTop = value;
} }
} }
public static void Snap(this Window window,
WindowPlacement placement,
double? width = null,
double? height = null) => window.GetHandle().SnapWindow((int)placement, width, height);
} }
+1 -1
View File
@@ -46,7 +46,7 @@ public partial class App :
services.AddTransient<IInitializer, AppInitializer>(); services.AddTransient<IInitializer, AppInitializer>();
services.AddTransient<ITemplateFactory, TemplateFactory>(); services.AddTransient<ITemplateFactory, TemplateFactory>();
services.AddSingleton<DesktopFlyout>(); services.AddSingleton<DesktopBar>();
services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>(); services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>();
@@ -4,5 +4,5 @@ namespace Hyperbar.Windows;
public class AppConfiguration public class AppConfiguration
{ {
public DesktopFlyoutPlacement Placement { get; set; } public DesktopBarPlacemenet Placement { get; set; }
} }
@@ -5,7 +5,7 @@ namespace Hyperbar.Windows;
public class AppInitializer([FromKeyedServices(nameof(WidgetBarViewModel))] WidgetBarView view, public class AppInitializer([FromKeyedServices(nameof(WidgetBarViewModel))] WidgetBarView view,
[FromKeyedServices(nameof(WidgetBarViewModel))] WidgetBarViewModel viewModel, [FromKeyedServices(nameof(WidgetBarViewModel))] WidgetBarViewModel viewModel,
DesktopFlyout desktopFlyout, DesktopBar desktopFlyout,
AppConfiguration configuration) : AppConfiguration configuration) :
IInitializer IInitializer
{ {
@@ -2,7 +2,7 @@
namespace Hyperbar.Windows.Primary; namespace Hyperbar.Windows.Primary;
public class AppConfigurationChangedHandler(DesktopFlyout desktopFlyout, public class AppConfigurationChangedHandler(DesktopBar desktopFlyout,
AppConfiguration configuration) : AppConfiguration configuration) :
INotificationHandler<ConfigurationChanged<AppConfiguration>> INotificationHandler<ConfigurationChanged<AppConfiguration>>
{ {
@@ -22,13 +22,18 @@
FontSize="16"> FontSize="16">
<SplitButton.Flyout> <SplitButton.Flyout>
<Flyout ShouldConstrainToRootBounds="False"> <Flyout ShouldConstrainToRootBounds="False">
<ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding}"> <Border
<ItemsControl.ItemTemplate> Width="300"
<DataTemplate> Height="300"
<MenuFlyoutItem Text="{Binding Text}" /> Background="red">
</DataTemplate> <ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding Mode=TwoWay}">
</ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
</ItemsControl> <DataTemplate>
<MenuFlyoutItem Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Flyout> </Flyout>
</SplitButton.Flyout> </SplitButton.Flyout>
</SplitButton> </SplitButton>