diff --git a/src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs b/src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs index 2e363e647d6..9ca897a948d 100644 --- a/src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/IWin32OptionsTopLevelImpl.cs @@ -21,5 +21,10 @@ public interface IWin32OptionsTopLevelImpl : ITopLevelImpl /// Gets or sets a custom callback for the window's WndProc /// public CustomWndProcHookCallback? WndProcHookCallback { get; set; } + + /// + /// Sets hints that configure the shape of window corners. + /// + void SetWindowCornerHints(Win32WindowCornerHints hints); } } diff --git a/src/Avalonia.Controls/Platform/Win32CornerHints.cs b/src/Avalonia.Controls/Platform/Win32CornerHints.cs new file mode 100644 index 00000000000..cb197cf8f6e --- /dev/null +++ b/src/Avalonia.Controls/Platform/Win32CornerHints.cs @@ -0,0 +1,30 @@ +using System; + +namespace Avalonia.Platform; + +/// +/// Specifies hints for window corner appearance. +/// +[Flags] +public enum Win32WindowCornerHints +{ + /// + /// Default Avalonia behavior. + /// + NoHint, + + /// + /// The platform's default corner style. + /// + PlatformDefault, + + /// + /// Rounded corners for the window. + /// + Rounded, + + /// + /// Prevents corners from being rounded. + /// + NotRounded +} diff --git a/src/Avalonia.Controls/Platform/Win32Properties.cs b/src/Avalonia.Controls/Platform/Win32Properties.cs index 13b1333ee64..badb69b0702 100644 --- a/src/Avalonia.Controls/Platform/Win32Properties.cs +++ b/src/Avalonia.Controls/Platform/Win32Properties.cs @@ -14,8 +14,14 @@ namespace Avalonia.Controls /// /// Set of Win32 specific properties and events that allow deeper customization of the application per platform. /// - public static class Win32Properties + public class Win32Properties { + public static readonly AttachedProperty WindowCornerHintProperty = + AvaloniaProperty.RegisterAttached("WindowCornerHint"); + + public static void SetWindowCornerHint(Window obj, Win32WindowCornerHints value) => obj.SetValue(WindowCornerHintProperty, value); + public static Win32WindowCornerHints GetWindowCornerHint(Window obj) => obj.GetValue(WindowCornerHintProperty); + 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); @@ -70,5 +76,16 @@ public static void RemoveWndProcHookCallback(TopLevel topLevel, CustomWndProcHoo toplevelImpl.WndProcHookCallback -= callback; } } + + static Win32Properties() + { + WindowCornerHintProperty.Changed.AddClassHandler((window, e) => + { + if (window.PlatformImpl is IWin32OptionsTopLevelImpl toplevelImpl) + { + toplevelImpl.SetWindowCornerHints(e.GetNewValue()); + } + }); + } } } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a9bd0c41966..3c9e972c258 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -100,6 +100,7 @@ internal partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindow private POINT _maxTrackSize; private WindowImpl? _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; + private Win32WindowCornerHints _cornerHints; private bool _isCloseRequested; private bool _shown; private bool _hiddenWindowIsParent; @@ -1133,6 +1134,30 @@ private MARGINS UpdateExtendMargins() return margins; } + private static DwmWindowCornerPreference HintsToCornerPreference(Win32WindowCornerHints cornerHints, DwmWindowCornerPreference noHintDefault) + { + return cornerHints switch + { + Win32WindowCornerHints.NoHint => noHintDefault, + Win32WindowCornerHints.PlatformDefault => DwmWindowCornerPreference.DWMWCP_DEFAULT, + Win32WindowCornerHints.Rounded => DwmWindowCornerPreference.DWMWCP_ROUND, + Win32WindowCornerHints.NotRounded => DwmWindowCornerPreference.DWMWCP_DONOTROUND, + _ => throw new ArgumentOutOfRangeException(nameof(cornerHints), cornerHints, null) + }; + } + + private void UpdateWindowCornerPreference() + { + int cornerPreference = (int)HintsToCornerPreference(_cornerHints, + _isClientAreaExtended && WindowState != WindowState.FullScreen ? + DwmWindowCornerPreference.DWMWCP_ROUND : + DwmWindowCornerPreference.DWMWCP_DEFAULT); + unsafe + { + DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int)); + } + } + private void ExtendClientArea() { if (!_shown) @@ -1151,12 +1176,6 @@ private void ExtendClientArea() { var margins = UpdateExtendMargins(); DwmExtendFrameIntoClientArea(_hwnd, ref margins); - - unsafe - { - int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_ROUND; - DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int)); - } } else { @@ -1165,14 +1184,10 @@ private void ExtendClientArea() _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); - - unsafe - { - int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT; - DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int)); - } } + UpdateWindowCornerPreference(); + if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome))) { @@ -1592,6 +1607,14 @@ public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) ExtendClientArea(); } + /// + public void SetWindowCornerHints(Win32WindowCornerHints hints) + { + _cornerHints = hints; + + UpdateWindowCornerPreference(); + } + /// public void GetWindowsZOrder(Span windows, Span zOrder) {