WIP
This commit is contained in:
+12
-12
@@ -2,30 +2,30 @@
|
||||
|
||||
namespace Hyperbar.Windows.Controls;
|
||||
|
||||
public class DesktopFlyout :
|
||||
public class DesktopBar :
|
||||
DependencyObject
|
||||
{
|
||||
public static readonly DependencyProperty ContentProperty =
|
||||
DependencyProperty.Register(nameof(Content),
|
||||
typeof(object), typeof(DesktopFlyout),
|
||||
typeof(object), typeof(DesktopBar),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty PlacementProperty =
|
||||
DependencyProperty.Register(nameof(Placement),
|
||||
typeof(DesktopFlyoutPlacement), typeof(DesktopFlyout),
|
||||
new PropertyMetadata(DesktopFlyoutPlacement.Left, OnPlacementPropertyChanged));
|
||||
typeof(DesktopBarPlacemenet), typeof(DesktopBar),
|
||||
new PropertyMetadata(DesktopBarPlacemenet.Left, OnPlacementPropertyChanged));
|
||||
|
||||
private readonly DesktopFlyoutHost host;
|
||||
private readonly DesktopFlyoutPresenter presenter;
|
||||
private readonly DesktopBarHost host;
|
||||
private readonly DesktopBarPresenter presenter;
|
||||
|
||||
public DesktopFlyout()
|
||||
public DesktopBar()
|
||||
{
|
||||
presenter = new DesktopFlyoutPresenter
|
||||
presenter = new DesktopBarPresenter
|
||||
{
|
||||
Parent = this
|
||||
};
|
||||
|
||||
host = new DesktopFlyoutHost(presenter);
|
||||
host = new DesktopBarHost(presenter);
|
||||
host.Activate();
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ public class DesktopFlyout :
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public DesktopFlyoutPlacement Placement
|
||||
public DesktopBarPlacemenet Placement
|
||||
{
|
||||
get => (DesktopFlyoutPlacement)GetValue(PlacementProperty);
|
||||
get => (DesktopBarPlacemenet)GetValue(PlacementProperty);
|
||||
set => SetValue(PlacementProperty, value);
|
||||
}
|
||||
|
||||
private static void OnPlacementPropertyChanged(DependencyObject dependencyObject,
|
||||
DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (dependencyObject is DesktopFlyout sender)
|
||||
if (dependencyObject is DesktopBar sender)
|
||||
{
|
||||
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
-1
@@ -1,6 +1,6 @@
|
||||
namespace Hyperbar.Windows.Controls;
|
||||
|
||||
public enum DesktopFlyoutPlacement
|
||||
public enum DesktopBarPlacemenet
|
||||
{
|
||||
Left,
|
||||
Top,
|
||||
+9
-9
@@ -4,20 +4,20 @@ using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Hyperbar.Windows.Controls;
|
||||
|
||||
public class DesktopFlyoutPresenter :
|
||||
public class DesktopBarPresenter :
|
||||
ContentControl
|
||||
{
|
||||
public static readonly DependencyProperty TemplateSettingsProperty =
|
||||
DependencyProperty.Register(nameof(TemplateSettings),
|
||||
typeof(DesktopFlyoutPresenterTemplateSettings), typeof(DesktopFlyoutPresenter),
|
||||
typeof(DesktopBarPresenterTemplateSettings), typeof(DesktopBarPresenter),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
internal new DesktopFlyout? Parent;
|
||||
internal new DesktopBar? Parent;
|
||||
|
||||
public DesktopFlyoutPresenter()
|
||||
public DesktopBarPresenter()
|
||||
{
|
||||
DefaultStyleKey = typeof(DesktopFlyoutPresenter);
|
||||
TemplateSettings = new DesktopFlyoutPresenterTemplateSettings();
|
||||
DefaultStyleKey = typeof(DesktopBarPresenter);
|
||||
TemplateSettings = new DesktopBarPresenterTemplateSettings();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
internal void UpdatePlacementState(DesktopFlyoutPlacement placement) => VisualStateManager.GoToState(this, $"{placement}Placement", true);
|
||||
internal void UpdatePlacementState(DesktopBarPlacemenet placement) => VisualStateManager.GoToState(this, $"{placement}Placement", true);
|
||||
}
|
||||
+18
-28
@@ -5,32 +5,30 @@
|
||||
xmlns:controls="using:Hyperbar.Windows.Controls">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="DesktopFlyoutPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterBackground" ResourceKey="AcrylicInAppFillColorDefaultBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterForeground" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="DesktopBarPresenterBorderBrush" ResourceKey="ControlStrokeColorDefaultBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
<Thickness x:Key="DesktopFlyoutPresenterBorderThemeThickness">1</Thickness>
|
||||
<Style TargetType="controls:DesktopFlyoutPresenter">
|
||||
<Setter Property="Background" Value="{ThemeResource DesktopFlyoutPresenterBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource DesktopFlyoutPresenterForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource DesktopFlyoutPresenterBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource DesktopFlyoutPresenterBorderThemeThickness}" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource OverlayCornerRadius}" />
|
||||
<Thickness x:Key="DesktopBarPresenterBorderThemeThickness">0</Thickness>
|
||||
<Style TargetType="controls:DesktopBarPresenter">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource DesktopBarPresenterForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource DesktopBarPresenterBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource DesktopBarPresenterBorderThemeThickness}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:DesktopFlyoutPresenter">
|
||||
<ControlTemplate TargetType="controls:DesktopBarPresenter">
|
||||
<Border x:Name="Container" Background="Transparent">
|
||||
<Border
|
||||
x:Name="BackgroundElement"
|
||||
MinWidth="48"
|
||||
MinHeight="48"
|
||||
MinWidth="40"
|
||||
Margin="16"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="OuterBorderEdge"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
@@ -49,18 +47,10 @@
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="PlacementStates">
|
||||
<VisualState x:Name="DefaultPlacement" />
|
||||
<VisualState x:Name="BottomPlacement">
|
||||
|
||||
</VisualState>
|
||||
<VisualState x:Name="TopPlacement">
|
||||
|
||||
</VisualState>
|
||||
<VisualState x:Name="LeftPlacement">
|
||||
|
||||
</VisualState>
|
||||
<VisualState x:Name="RightPlacement">
|
||||
|
||||
</VisualState>
|
||||
<VisualState x:Name="BottomPlacement" />
|
||||
<VisualState x:Name="TopPlacement" />
|
||||
<VisualState x:Name="LeftPlacement" />
|
||||
<VisualState x:Name="RightPlacement" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
+5
-5
@@ -2,26 +2,26 @@
|
||||
|
||||
namespace Hyperbar.Windows.Controls;
|
||||
|
||||
public class DesktopFlyoutPresenterTemplateSettings : DependencyObject
|
||||
public class DesktopBarPresenterTemplateSettings : DependencyObject
|
||||
{
|
||||
public static readonly DependencyProperty HeightProperty =
|
||||
DependencyProperty.Register(nameof(Height),
|
||||
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings),
|
||||
typeof(double), typeof(DesktopBarPresenterTemplateSettings),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
public static readonly DependencyProperty NegativeHeightProperty =
|
||||
DependencyProperty.Register(nameof(NegativeHeight),
|
||||
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings),
|
||||
typeof(double), typeof(DesktopBarPresenterTemplateSettings),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
public static readonly DependencyProperty NegativeWidthProperty =
|
||||
DependencyProperty.Register(nameof(NegativeWidth),
|
||||
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings),
|
||||
typeof(double), typeof(DesktopBarPresenterTemplateSettings),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
public static readonly DependencyProperty WidthProperty =
|
||||
DependencyProperty.Register(nameof(Width),
|
||||
typeof(double), typeof(DesktopFlyoutPresenterTemplateSettings),
|
||||
typeof(double), typeof(DesktopBarPresenterTemplateSettings),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
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" ?>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<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>
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Hyperbar.Windows.Interop;
|
||||
|
||||
public enum AppBarWindowPlacement
|
||||
{
|
||||
Left,
|
||||
Top,
|
||||
Right,
|
||||
Bottom
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
@@ -30,8 +29,10 @@ public static class HwndExtensions
|
||||
SET_WINDOW_POS_FLAGS.SWP_NOZORDER);
|
||||
}
|
||||
|
||||
public static uint GetDpiForWindow(IntPtr hwnd) => PInvoke.GetDpiForWindow(new HWND(hwnd));
|
||||
|
||||
public static void SetWindowOpacity(this IntPtr hWnd,
|
||||
byte value)
|
||||
byte value)
|
||||
{
|
||||
HWND hWND = new(hWnd);
|
||||
WindowStyles windowLong = (WindowStyles)PInvoke.GetWindowLong(hWND, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
@@ -45,6 +46,7 @@ public static class HwndExtensions
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetWindowStyle(this IntPtr hwnd,
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,5 @@ CreateSolidBrush
|
||||
FillRect
|
||||
GetDC
|
||||
SendInput
|
||||
MapVirtualKey
|
||||
MapVirtualKey
|
||||
MoveWindow
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Hyperbar.Windows.UI;
|
||||
|
||||
public static class WindowExtensions
|
||||
{
|
||||
public static WindowMessageListener CreateMessageListener(this Window window) =>
|
||||
WindowMessageListener.Create(window.GetHandle());
|
||||
|
||||
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,
|
||||
bool value) => window.AppWindow.IsShownInSwitchers = value;
|
||||
bool value) => window.AppWindow.IsShownInSwitchers = value;
|
||||
|
||||
public static void SetOpacity(this Window window,
|
||||
byte value) => window.GetHandle().SetWindowOpacity(value);
|
||||
@@ -31,9 +45,4 @@ public static class WindowExtensions
|
||||
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);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public partial class App :
|
||||
services.AddTransient<IInitializer, AppInitializer>();
|
||||
services.AddTransient<ITemplateFactory, TemplateFactory>();
|
||||
|
||||
services.AddSingleton<DesktopFlyout>();
|
||||
services.AddSingleton<DesktopBar>();
|
||||
|
||||
services.AddContentTemplate<WidgetBarViewModel, WidgetBarView>();
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ namespace Hyperbar.Windows;
|
||||
|
||||
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,
|
||||
[FromKeyedServices(nameof(WidgetBarViewModel))] WidgetBarViewModel viewModel,
|
||||
DesktopFlyout desktopFlyout,
|
||||
DesktopBar desktopFlyout,
|
||||
AppConfiguration configuration) :
|
||||
IInitializer
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Hyperbar.Windows.Primary;
|
||||
|
||||
public class AppConfigurationChangedHandler(DesktopFlyout desktopFlyout,
|
||||
public class AppConfigurationChangedHandler(DesktopBar desktopFlyout,
|
||||
AppConfiguration configuration) :
|
||||
INotificationHandler<ConfigurationChanged<AppConfiguration>>
|
||||
{
|
||||
|
||||
@@ -22,13 +22,18 @@
|
||||
FontSize="16">
|
||||
<SplitButton.Flyout>
|
||||
<Flyout ShouldConstrainToRootBounds="False">
|
||||
<ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<MenuFlyoutItem Text="{Binding Text}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Border
|
||||
Width="300"
|
||||
Height="300"
|
||||
Background="red">
|
||||
<ItemsControl Margin="-16,-13,-16,-15" ItemsSource="{Binding Mode=TwoWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<MenuFlyoutItem Text="{Binding Text}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</Flyout>
|
||||
</SplitButton.Flyout>
|
||||
</SplitButton>
|
||||
|
||||
Reference in New Issue
Block a user