diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index cf31d30332..9c4808a41c 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -94,5 +94,11 @@ namespace Avalonia.Platform /// /// void SetMinMaxSize(Size minSize, Size maxSize); + + bool ExtendClientAreaToDecorationsHint { get; set; } + + Action ExtendClientAreaToDecorationsChanged { get; set; } + + Thickness ExtendedMargins { get; } } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ff7cc41e3b..04b48d7f9f 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -71,6 +71,8 @@ namespace Avalonia.Controls { private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>(); + private bool _isExtendedIntoWindowDecorations; + /// /// Defines the property. /// @@ -87,6 +89,29 @@ namespace Avalonia.Controls o => o.HasSystemDecorations, (o, v) => o.HasSystemDecorations = v); + /// + /// Defines the property. + /// + public static readonly StyledProperty ExtendClientAreaToDecorationsHintProperty = + AvaloniaProperty.Register(nameof(ExtendClientAreaToDecorationsHint), false); + + + /// + /// Defines the property. + /// + public static readonly DirectProperty IsExtendedIntoWindowDecorationsProperty = + AvaloniaProperty.RegisterDirect(nameof(IsExtendedIntoWindowDecorations), + o => o.IsExtendedIntoWindowDecorations, + unsetValue: false); + + /// + /// Defines the property. + /// + public static readonly DirectProperty WindowDecorationMarginsProperty = + AvaloniaProperty.RegisterDirect(nameof(WindowDecorationMargins), + o => o.WindowDecorationMargins); + + /// /// Defines the property. /// @@ -164,6 +189,9 @@ namespace Avalonia.Controls WindowStateProperty.Changed.AddClassHandler( (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); + ExtendClientAreaToDecorationsHintProperty.Changed.AddClassHandler( + (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.ExtendClientAreaToDecorationsHint = (bool)e.NewValue; }); + MinWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size((double)e.NewValue, w.MinHeight), new Size(w.MaxWidth, w.MaxHeight))); MinHeightProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight))); MaxWidthProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight))); @@ -189,6 +217,7 @@ namespace Avalonia.Controls impl.GotInputWhenDisabled = OnGotInputWhenDisabled; impl.WindowStateChanged = HandleWindowStateChanged; _maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size); + impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged; this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); @@ -237,6 +266,32 @@ namespace Avalonia.Controls } } + /// + /// Gets or sets if the ClientArea is Extended into the Window Decorations (chrome or border). + /// + public bool ExtendClientAreaToDecorationsHint + { + get { return GetValue(ExtendClientAreaToDecorationsHintProperty); } + set { SetValue(ExtendClientAreaToDecorationsHintProperty, value); } + } + + /// + /// Gets if the ClientArea is Extended into the Window Decorations. + /// + public bool IsExtendedIntoWindowDecorations + { + get => _isExtendedIntoWindowDecorations; + private set => SetAndRaise(IsExtendedIntoWindowDecorationsProperty, ref _isExtendedIntoWindowDecorations, value); + } + + private Thickness _windowDecorationMargins; + + public Thickness WindowDecorationMargins + { + get => _windowDecorationMargins; + private set => SetAndRaise(WindowDecorationMarginsProperty, ref _windowDecorationMargins, value); + } + /// /// Sets the system decorations (title bar, border, etc) /// @@ -435,6 +490,13 @@ namespace Avalonia.Controls } } + protected virtual void ExtendClientAreaToDecorationsChanged(bool isExtended) + { + IsExtendedIntoWindowDecorations = isExtended; + + WindowDecorationMargins = PlatformImpl.ExtendedMargins; + } + /// /// Hides the window but does not close it. /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index cd64af60e2..441a79531a 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -84,6 +84,12 @@ namespace Avalonia.DesignerSupport.Remote public IScreenImpl Screen { get; } = new ScreenStub(); public Action GotInputWhenDisabled { get; set; } + + public bool ExtendClientAreaToDecorationsHint { get; set; } + + public Action ExtendClientAreaToDecorationsChanged { get; set; } + + public Thickness ExtendedMargins { get; } = new Thickness(); public void Activate() { diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index b001bc1b76..f962382575 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -39,6 +39,12 @@ namespace Avalonia.DesignerSupport.Remote public Action TransparencyLevelChanged { get; set; } + public bool ExtendClientAreaToDecorationsHint { get; set; } + + public Action ExtendClientAreaToDecorationsChanged { get; set; } + + public Thickness ExtendedMargins { get; } = new Thickness(); + public WindowStub(IWindowImpl parent = null) { if (parent != null) diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index e91445000a..3495c4b392 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -98,6 +98,12 @@ namespace Avalonia.Native public Action WindowStateChanged { get; set; } + public bool ExtendClientAreaToDecorationsHint { get; set; } + + public Action ExtendClientAreaToDecorationsChanged { get; set; } + + public Thickness ExtendedMargins { get; } = new Thickness(); + public void ShowTaskbarIcon(bool value) { // NO OP On OSX diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 90064cb28d..ac9048adad 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -311,6 +311,12 @@ namespace Avalonia.X11 set => _transparencyHelper.TransparencyLevelChanged = value; } + public bool ExtendClientAreaToDecorationsHint { get; set; } + + public Action ExtendClientAreaToDecorationsChanged { get; set; } + + public Thickness ExtendedMargins { get; } = new Thickness(); + public Action Closed { get; set; } public Action PositionChanged { get; set; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs index dedef2ded3..3ce34250fa 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs @@ -14,11 +14,6 @@ namespace Avalonia.Win32 { public partial class WindowImpl { - private int LEFTEXTENDWIDTH = 4; - private int RIGHTEXTENDWIDTH = 4; - private int BOTTOMEXTENDWIDTH = 4; - private int TOPEXTENDWIDTH = 100; - // Hit test the frame for resizing and moving. HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { @@ -32,28 +27,40 @@ namespace Avalonia.Win32 RECT rcFrame = new RECT(); AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0); + RECT border_thickness = new RECT(); + if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME)) + { + AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); + border_thickness.left *= -1; + border_thickness.top *= -1; + } + else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) + { + border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; + } + // Determine if the hit test is for resizing. Default middle (1,1). ushort uRow = 1; ushort uCol = 1; bool fOnResizeBorder = false; // Determine if the point is at the top or bottom of the window. - if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + TOPEXTENDWIDTH) + if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + border_thickness.top) { fOnResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top)); uRow = 0; } - else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - BOTTOMEXTENDWIDTH) + else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - border_thickness.bottom) { uRow = 2; } // Determine if the point is at the left or right of the window. - if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + LEFTEXTENDWIDTH) + if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + border_thickness.left) { uCol = 0; // left side } - else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - RIGHTEXTENDWIDTH) + else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - border_thickness.right) { uCol = 2; // right side } @@ -66,8 +73,6 @@ namespace Avalonia.Win32 new []{ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT }, }; - System.Diagnostics.Debug.WriteLine(hitTests[uRow][uCol]); - return hitTests[uRow][uCol]; } @@ -84,35 +89,37 @@ namespace Avalonia.Win32 { case WindowsMessage.WM_ACTIVATE: { - if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME)) - { - AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); - border_thickness.left *= -1; - border_thickness.top *= -1; - } - else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) - { - border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; - } + if (!_isClientAreaExtended) + { + if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME)) + { + AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); + border_thickness.left *= -1; + border_thickness.top *= -1; + } + else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) + { + border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; + } - LEFTEXTENDWIDTH = border_thickness.left; - RIGHTEXTENDWIDTH = border_thickness.right; - BOTTOMEXTENDWIDTH = border_thickness.bottom; - TOPEXTENDWIDTH = border_thickness.top; + // Extend the frame into the client area. + margins.cxLeftWidth = border_thickness.left; + margins.cxRightWidth = border_thickness.right; + margins.cyBottomHeight = border_thickness.bottom; + margins.cyTopHeight = border_thickness.top; - // Extend the frame into the client area. - margins.cxLeftWidth = border_thickness.left; - margins.cxRightWidth = border_thickness.right; - margins.cyBottomHeight = border_thickness.bottom; - margins.cyTopHeight = border_thickness.top; + var hr = DwmExtendFrameIntoClientArea(hWnd, ref margins); - var hr = DwmExtendFrameIntoClientArea(hWnd, ref margins); + //if (hr < 0) + { + // Handle the error. + } - //if (hr < 0) - { - // Handle the error. - } + _isClientAreaExtended = true; + _extendedMargins = new Thickness(margins.cxLeftWidth / Scaling, margins.cyTopHeight / Scaling, margins.cxRightWidth / Scaling, margins.cyBottomHeight / Scaling); + ExtendClientAreaToDecorationsChanged?.Invoke(true); + } lRet = IntPtr.Zero; callDwp = true; break;