Browse Source

Implement Win32Properties.WindowCornerPreference (#21615)

* Implement Win32Properties.WindowCornerPreference

* Add corner preference to ControlCatalog

* Update API suppressions
pull/21624/head
Julien Lebosquain 3 months ago
committed by GitHub
parent
commit
b2c8170f95
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      api/Avalonia.nupkg.xml
  2. 1
      samples/ControlCatalog/MainWindow.xaml
  3. 13
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml
  4. 10
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  5. 13
      src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs
  6. 95
      src/Avalonia.Controls/Platform/Win32Properties.cs
  7. 35
      src/Windows/Avalonia.Win32/WindowImpl.cs

12
api/Avalonia.nupkg.xml

@ -97,4 +97,16 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.OpenGL.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Platform.IWin32OptionsTopLevelImpl.SetWindowCornerPreference(Avalonia.Controls.Win32Properties.WindowCornerPreference)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Platform.IWin32OptionsTopLevelImpl.SetWindowCornerPreference(Avalonia.Controls.Win32Properties.WindowCornerPreference)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
</Suppressions>

1
samples/ControlCatalog/MainWindow.xaml

@ -12,6 +12,7 @@
CanResize="{Binding CanResize}"
CanMinimize="{Binding CanMinimize}"
CanMaximize="{Binding CanMaximize}"
Win32Properties.WindowCornerPreference="{Binding Win32WindowCornerPreference}"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"
x:DataType="vm:MainWindowViewModel">
<NativeMenu.Menu>

13
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml

@ -42,6 +42,19 @@
IsChecked="{Binding CanMaximize}"
IsEnabled="{Binding CanResize}" />
<DockPanel>
<Label Content="Corner Preference (Windows 11+): "
Margin="4"
DockPanel.Dock="Left" />
<ComboBox ItemsSource="{Binding Win32WindowCornerPreferences}"
SelectedItem="{Binding Win32WindowCornerPreference, Mode=TwoWay}"
HorizontalAlignment="Left"
MinWidth="220" />
</DockPanel>
</StackPanel>
<StackPanel Spacing="10" Margin="25" IsEnabled="{OnFormFactor false, Mobile=true}">

10
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -122,7 +122,6 @@ namespace ControlCatalog.ViewModels
set { RaiseAndSetIfChanged(ref _selectedDecorationIndex, value); }
}
public MiniCommand AboutCommand { get; }
public MiniCommand ExitCommand { get; }
@ -138,5 +137,14 @@ namespace ControlCatalog.ViewModels
get => _validatedDateExample;
set => RaiseAndSetIfChanged(ref _validatedDateExample, value);
}
public Win32Properties.WindowCornerPreference[] Win32WindowCornerPreferences { get; } =
Enum.GetValues<Win32Properties.WindowCornerPreference>();
public Win32Properties.WindowCornerPreference Win32WindowCornerPreference
{
get;
set { RaiseAndSetIfChanged(ref field, value); }
}
}
}

13
src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Metadata;
using Avalonia.Metadata;
using Avalonia.Platform;
using static Avalonia.Controls.Win32Properties;
@ -21,5 +16,11 @@ namespace Avalonia.Controls.Platform
/// Gets or sets a custom callback for the window's WndProc
/// </summary>
public CustomWndProcHookCallback? WndProcHookCallback { get; set; }
/// <summary>
/// Sets a window corner preference for the window.
/// </summary>
/// <param name="preference">The value to set.</param>
public void SetWindowCornerPreference(WindowCornerPreference preference);
}
}

95
src/Avalonia.Controls/Platform/Win32Properties.cs

@ -17,9 +17,38 @@ namespace Avalonia.Controls
/// </summary>
public static class Win32Properties
{
/// <summary>
/// Defines the <c>NonClientHitTestResult</c> attached property.
/// </summary>
public static readonly AttachedProperty<Win32HitTestValue> NonClientHitTestResultProperty =
AvaloniaProperty.RegisterAttached<Visual, Win32HitTestValue>(
"NonClientHitTestResult",
typeof(Win32Properties),
inherits: true,
defaultValue: Win32HitTestValue.Client);
/// <summary>
/// Defines the <c>WindowCornerPreferenceProperty</c> attached property.
/// </summary>
public static readonly AttachedProperty<WindowCornerPreference> WindowCornerPreferenceProperty =
AvaloniaProperty.RegisterAttached<Window, WindowCornerPreference>(
"WindowCornerPreference",
typeof(Win32Properties),
defaultValue: WindowCornerPreference.Default,
validate: ValidateWindowCornerPreference);
public delegate (uint style, uint exStyle) CustomWindowStylesCallback(uint style, uint exStyle);
public delegate IntPtr CustomWndProcHookCallback(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool handled);
static Win32Properties()
{
WindowCornerPreferenceProperty.Changed.AddClassHandler<Window>((window, e) =>
{
if (window.PlatformImpl is IWin32OptionsTopLevelImpl toplevelImpl)
toplevelImpl.SetWindowCornerPreference(e.GetNewValue<WindowCornerPreference>());
});
}
/// <summary>
/// Adds a callback to set the window's style.
/// </summary>
@ -72,16 +101,48 @@ namespace Avalonia.Controls
}
}
public static readonly AttachedProperty<Win32HitTestValue> NonClientHitTestResultProperty =
AvaloniaProperty.RegisterAttached<Visual, Win32HitTestValue>(
"NonClientHitTestResult",
typeof(Win32Properties),
inherits: true,
defaultValue: Win32HitTestValue.Client);
/// <summary>
/// Sets the value of the <c>NonClientHitTestResult</c> attached property on the specified visual.
/// </summary>
/// <param name="obj">The visual.</param>
/// <param name="value">The value to set.</param>
public static void SetNonClientHitTestResult(Visual obj, Win32HitTestValue value)
=> obj.SetValue(NonClientHitTestResultProperty, value);
/// <summary>
/// Gets the value of the <c>NonClientHitTestResult</c> attached property on the specified visual.
/// </summary>
/// <param name="obj">The visual.</param>
/// <returns>The hit test value for <paramref name="obj"/>.</returns>
public static Win32HitTestValue GetNonClientHitTestResult(Visual obj)
=> obj.GetValue(NonClientHitTestResultProperty);
/// <summary>
/// Sets the value of the <c>WindowCornerPreference</c> attached property on the specified window.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="value">The value to set.</param>
/// <remarks>
/// This value is supported starting with Windows 11 Build 22000.
/// It is ignored on earlier Windows versions.
/// </remarks>
public static void SetWindowCornerPreference(Window window, WindowCornerPreference value)
=> window.SetValue(WindowCornerPreferenceProperty, value);
/// <summary>
/// Gets the value of the <c>WindowCornerPreference</c> attached property on the specified window.
/// </summary>
/// <param name="window">The window.</param>
/// <returns>The window corner preference for <paramref name="window"/>.</returns>
public static WindowCornerPreference GetWindowCornerPreference(Window window)
=> window.GetValue(WindowCornerPreferenceProperty);
public static void SetNonClientHitTestResult(Visual obj, Win32HitTestValue value) => obj.SetValue(NonClientHitTestResultProperty, value);
public static Win32HitTestValue GetNonClientHitTestResult(Visual obj) => obj.GetValue(NonClientHitTestResultProperty);
private static bool ValidateWindowCornerPreference(WindowCornerPreference preference)
=> preference is >= WindowCornerPreference.Default and <= WindowCornerPreference.RoundSmall;
/// <summary>
/// Represents a hit test value for a visual.
/// </summary>
public enum Win32HitTestValue
{
Nowhere = 0,
@ -99,5 +160,23 @@ namespace Avalonia.Controls
BottomRight = 17,
Close = 20,
}
/// <summary>
/// Represents the rounded corner preference for a window.
/// </summary>
public enum WindowCornerPreference
{
/// <summary>Let the system decide when to round window corners.</summary>
Default = 0,
/// <summary>Never round window corners.</summary>
DoNotRound = 1,
/// <summary>Round the corners, if appropriate.</summary>
Round = 2,
/// <summary>Round the corners if appropriate, with a small radius.</summary>
RoundSmall = 3
}
}
}

35
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -108,6 +108,7 @@ namespace Avalonia.Win32
internal bool _ignoreWmChar;
private WindowTransparencyLevel _transparencyLevel;
private readonly WindowTransparencyLevel _defaultTransparencyLevel;
private WindowCornerPreference _cornerPreference;
private const int MaxPointerHistorySize = 512;
private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new();
@ -1197,6 +1198,18 @@ namespace Avalonia.Win32
return margins;
}
private void UpdateWindowCornerPreference()
{
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000))
return;
unsafe
{
var preference = (int)_cornerPreference;
DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(int));
}
}
private void ExtendClientArea()
{
if (!_shown)
@ -1219,10 +1232,6 @@ namespace Avalonia.Win32
// Make sure that Windows still paints the non-client area so we get native borders and shadows.
SetNCRenderingPolicy(DwmNCRenderingPolicy.DWMNCRP_ENABLED);
// On Windows 11 21H2 and later, corners are configurable.
if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000))
SetWindowCornerPreference(DwmWindowCornerPreference.DWMWCP_ROUND);
}
else
{
@ -1231,16 +1240,7 @@ namespace Avalonia.Win32
_extendedMargins = new Thickness();
if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000))
SetWindowCornerPreference(DwmWindowCornerPreference.DWMWCP_DEFAULT);
SetNCRenderingPolicy(DwmNCRenderingPolicy.DWMNCRP_USEWINDOWSTYLE);
unsafe
{
int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT;
DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int));
}
}
if (!_isClientAreaExtended)
@ -1262,9 +1262,6 @@ namespace Avalonia.Win32
ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended);
}
private unsafe void SetWindowCornerPreference(DwmWindowCornerPreference value)
=> DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(int));
private unsafe void SetNCRenderingPolicy(DwmNCRenderingPolicy value)
=> DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_NCRENDERING_POLICY, &value, sizeof(int));
@ -1615,6 +1612,12 @@ namespace Avalonia.Win32
ExtendClientArea();
}
public void SetWindowCornerPreference(WindowCornerPreference preference)
{
_cornerPreference = preference;
UpdateWindowCornerPreference();
}
/// <inheritdoc/>
public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended;

Loading…
Cancel
Save