Kaynağa Gözat

Win32 - Fix Window Frame Size and Position issues. (#16608)

* add tests

* win32 - ensure frame size and position doesn't change for client operations

* remove forced position setting

---------

Co-authored-by: Julien Lebosquain <[email protected]>
Emmanuel Hansen 1 yıl önce
ebeveyn
işleme
854078067d

+ 10 - 6
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@@ -1,17 +1,14 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
 using Avalonia.Automation.Peers;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
-using Avalonia.Platform;
 using Avalonia.Threading;
 using Avalonia.Win32.Automation;
 using Avalonia.Win32.Input;
-using Avalonia.Win32.Interop;
 using Avalonia.Win32.Interop.Automation;
 using static Avalonia.Win32.Interop.UnmanagedMethods;
 
@@ -643,6 +640,8 @@ namespace Avalonia.Win32
                             Resized(clientSize / RenderScaling, _resizeReason);
                         }
 
+                        if (IsWindowVisible(_hwnd) && !_shown)
+                            _shown = true;
 
                         if (stateChanged)
                         {
@@ -661,11 +660,17 @@ namespace Avalonia.Win32
 
                             if (_isClientAreaExtended)
                             {
-                                UpdateExtendMargins();
+                                ExtendClientArea();
 
                                 ExtendClientAreaToDecorationsChanged?.Invoke(true);
                             }
                         }
+                        else if (windowState == WindowState.Maximized && _isClientAreaExtended)
+                        {
+                            ExtendClientArea();
+
+                            ExtendClientAreaToDecorationsChanged?.Invoke(true);
+                        }
 
                         return IntPtr.Zero;
                     }
@@ -676,8 +681,7 @@ namespace Avalonia.Win32
 
                 case WindowsMessage.WM_MOVE:
                     {
-                        PositionChanged?.Invoke(new PixelPoint((short)(ToInt32(lParam) & 0xffff),
-                            (short)(ToInt32(lParam) >> 16)));
+                        PositionChanged?.Invoke(Position);
                         return IntPtr.Zero;
                     }
 

+ 28 - 74
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -263,14 +263,8 @@ namespace Avalonia.Win32
         {
             get
             {
-                if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
-                {
-                    GetWindowRect(_hwnd, out var rcWindow);
-                    return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
-                }
-
-                DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf<RECT>());
-                return new Size(rect.Width, rect.Height) / RenderScaling;
+                GetWindowRect(_hwnd, out var rcWindow);
+                return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
             }
         }
 
@@ -510,6 +504,7 @@ namespace Avalonia.Win32
             var result = DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
             return result == 0;
         }
+
         public IEnumerable<object> Surfaces
             => _glSurface is null ?
                 new object[] { Handle, _framebuffer } :
@@ -520,15 +515,10 @@ namespace Avalonia.Win32
             get
             {
                 GetWindowRect(_hwnd, out var rc);
-
-                var border = HiddenBorderSize;
-                return new PixelPoint(rc.left + border.Width, rc.top + border.Height);
+                return new PixelPoint(rc.left, rc.top);
             }
             set
             {
-                var border = HiddenBorderSize;
-                value = new PixelPoint(value.X - border.Width, value.Y - border.Height);
-
                 SetWindowPos(
                     Handle.Handle,
                     IntPtr.Zero,
@@ -542,23 +532,6 @@ namespace Avalonia.Win32
 
         private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full;
 
-        private PixelSize HiddenBorderSize
-        {
-            get
-            {
-                // Windows 10 and 11 add a 7 pixel invisible border on the left/right/bottom of windows for resizing
-                if (Win32Platform.WindowsVersion.Major < 10 || !HasFullDecorations || GetStyle().HasFlag(WindowStyles.WS_POPUP))
-                {
-                    return PixelSize.Empty;
-                }
-
-                DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var clientRect, Marshal.SizeOf<RECT>());
-                GetWindowRect(_hwnd, out var frameRect);
-                var borderWidth = GetSystemMetrics(SystemMetric.SM_CXBORDER);
-
-                return new PixelSize(clientRect.left - frameRect.left - borderWidth, 0);
-            }
-        }
 
         public void Move(PixelPoint point) => Position = point;
 
@@ -594,25 +567,29 @@ namespace Avalonia.Win32
 
             GetWindowPlacement(_hwnd, out var windowPlacement);
 
-            var clientScreenOrigin = new POINT();
-            ClientToScreen(_hwnd, ref clientScreenOrigin);
-
             var requestedClientRect = new RECT
             {
-                left = clientScreenOrigin.X,
-                right = clientScreenOrigin.X + requestedClientWidth,
-
-                top = clientScreenOrigin.Y,
-                bottom = clientScreenOrigin.Y + requestedClientHeight,
+                left = 0,
+                top = 0,
+                right = requestedClientWidth,
+                bottom = requestedClientHeight
             };
 
             var requestedWindowRect = _isClientAreaExtended ? requestedClientRect : ClientRectToWindowRect(requestedClientRect);
+            var windowWidth = requestedWindowRect.Width;
+            var windowHeight = requestedWindowRect.Height;
 
-            if (requestedWindowRect.Width == windowPlacement.NormalPosition.Width && requestedWindowRect.Height == windowPlacement.NormalPosition.Height)
+            if (windowWidth == windowPlacement.NormalPosition.Width && windowHeight == windowPlacement.NormalPosition.Height)
             {
                 return;
             }
 
+            var position = Position;
+            requestedWindowRect.left = position.X;
+            requestedWindowRect.top = position.Y;
+            requestedWindowRect.right = position.X + windowWidth;
+            requestedWindowRect.bottom = position.Y + windowHeight;
+
             windowPlacement.NormalPosition = requestedWindowRect;
 
             windowPlacement.ShowCmd = !_shown ? ShowWindowCommand.Hide : _lastWindowState switch
@@ -1025,14 +1002,8 @@ namespace Avalonia.Win32
             if (fullscreen)
             {
                 GetWindowRect(_hwnd, out var windowRect);
-                GetClientRect(_hwnd, out var clientRect);
-
-                clientRect.left += windowRect.left;
-                clientRect.right += windowRect.left;
-                clientRect.top += windowRect.top;
-                clientRect.bottom += windowRect.top;
 
-                _savedWindowInfo.WindowRect = clientRect;
+                _savedWindowInfo.WindowRect = windowRect;
 
                 var current = GetStyle();
                 var currentEx = GetExtendedStyle();
@@ -1141,16 +1112,8 @@ namespace Avalonia.Win32
                 _isClientAreaExtended = false;
                 return;
             }
-            GetClientRect(_hwnd, out var rcClient);
             GetWindowRect(_hwnd, out var rcWindow);
 
-            // Inform the application of the frame change.
-            SetWindowPos(_hwnd,
-                IntPtr.Zero,
-                rcWindow.left, rcWindow.top,
-                rcClient.Width, rcClient.Height,
-                SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOACTIVATE);
-
             if (_isClientAreaExtended && WindowState != WindowState.FullScreen)
             {
                 var margins = UpdateExtendMargins();
@@ -1170,8 +1133,6 @@ namespace Avalonia.Win32
                 _offScreenMargin = new Thickness();
                 _extendedMargins = new Thickness();
 
-                Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), WindowResizeReason.Layout);
-
                 unsafe
                 {
                     int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT;
@@ -1189,6 +1150,13 @@ namespace Avalonia.Win32
                 DisableCloseButton(_hwnd);
             }
 
+            // Inform the application of the frame change.
+            SetWindowPos(_hwnd,
+                IntPtr.Zero,
+                rcWindow.left, rcWindow.top,
+                0, 0,
+                SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOSIZE);
+
             ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended);
         }
 
@@ -1454,7 +1422,7 @@ namespace Avalonia.Win32
                 {
                     style &= ~(fullDecorationFlags | WindowStyles.WS_THICKFRAME);
 
-                    if (newProperties.Decorations == SystemDecorations.BorderOnly && newProperties.WindowState != WindowState.Maximized)
+                    if (newProperties.Decorations == SystemDecorations.BorderOnly && newProperties.WindowState != WindowState.Maximized && newProperties.IsResizable)
                     {
                         style |= WindowStyles.WS_THICKFRAME | WindowStyles.WS_BORDER;
                     }
@@ -1483,8 +1451,6 @@ namespace Avalonia.Win32
 
             if (!_isFullScreenActive && ((oldProperties.Decorations != newProperties.Decorations) || forceChanges))
             {
-                var style = GetStyle();
-
                 var margin = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0;
 
                 var margins = new MARGINS
@@ -1497,23 +1463,11 @@ namespace Avalonia.Win32
 
                 DwmExtendFrameIntoClientArea(_hwnd, ref margins);
 
-
                 if (_shown || forceChanges)
                 {
-                    GetClientRect(_hwnd, out var oldClientRect);
-                    var oldClientRectOrigin = new POINT();
-                    ClientToScreen(_hwnd, ref oldClientRectOrigin);
-                    oldClientRect.Offset(oldClientRectOrigin);
-
-                    var newRect = oldClientRect;
-
-                    if (newProperties.Decorations == SystemDecorations.Full)
-                    {
-                        AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle());
-                    }
-
-                    SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
+                    SetWindowPos(_hwnd, IntPtr.Zero, 0, 0, 0 ,0,
                         SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
+                        SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE |
                         SetWindowPosFlags.SWP_FRAMECHANGED);
                 }
             }

+ 1 - 1
tests/Avalonia.IntegrationTests.Appium/WindowDecorationsTests.cs

@@ -12,7 +12,7 @@ public class WindowDecorationsTests : TestBase, IDisposable
     {
     }
 
-    [PlatformFact(TestPlatforms.MacOS)] // TODO fix me on Windows
+    [Fact]
     public void Window_Size_Should_Be_Consistent_Between_Toggles()
     {
         var window = Session.FindElementByAccessibilityId("MainWindow");

+ 40 - 2
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@@ -311,6 +311,44 @@ namespace Avalonia.IntegrationTests.Appium
             }
         }
 
+        [Fact]
+        public void Changing_SystemDecorations_Should_Not_Change_Frame_Size_And_Position()
+        {
+            using (OpenWindow(null, ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
+            {
+                var info = GetWindowInfo();
+
+                Session.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
+                Session.FindElementByAccessibilityId("SystemDecorationsNone").SendClick();
+                var updatedInfo = GetWindowInfo();
+                Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
+                Assert.Equal(info.Position, updatedInfo.Position);
+
+                Session.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
+                Session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
+                updatedInfo = GetWindowInfo();
+                Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
+                Assert.Equal(info.Position, updatedInfo.Position);
+            }
+        }
+
+        [Fact]
+        public void Changing_WindowState_Should_Not_Change_Frame_Size_And_Position()
+        {
+            using (OpenWindow())
+            {
+                var info = GetWindowInfo();
+
+                Session.FindElementByAccessibilityId("CurrentWindowState").SendClick();
+                Session.FindElementByAccessibilityId("WindowStateMaximized").SendClick();
+                Session.FindElementByAccessibilityId("CurrentWindowState").SendClick();
+                Session.FindElementByAccessibilityId("WindowStateNormal").SendClick();
+                var updatedInfo = GetWindowInfo();
+                Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
+                Assert.Equal(info.Position, updatedInfo.Position);
+            }
+        }
+
         public static TheoryData<Size?, ShowWindowMode, WindowStartupLocation, bool> StartupLocationData()
         {
             var sizes = new Size?[] { null, new Size(400, 300) };
@@ -391,8 +429,8 @@ namespace Avalonia.IntegrationTests.Appium
         }
 
         private IDisposable OpenWindow(
-            Size? size,
-            ShowWindowMode mode,
+            Size? size = null,
+            ShowWindowMode mode = ShowWindowMode.NonOwned,
             WindowStartupLocation location = WindowStartupLocation.Manual,
             WindowState state = Controls.WindowState.Normal,
             bool canResize = true,