Skip to content

Commit 8540780

Browse files
emmaussMrJul
andauthored
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]>
1 parent 8a61eca commit 8540780

File tree

4 files changed

+79
-83
lines changed

4 files changed

+79
-83
lines changed

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Diagnostics;
43
using System.Diagnostics.CodeAnalysis;
54
using System.Runtime.InteropServices;
65
using Avalonia.Automation.Peers;
76
using Avalonia.Controls;
87
using Avalonia.Input;
98
using Avalonia.Input.Raw;
10-
using Avalonia.Platform;
119
using Avalonia.Threading;
1210
using Avalonia.Win32.Automation;
1311
using Avalonia.Win32.Input;
14-
using Avalonia.Win32.Interop;
1512
using Avalonia.Win32.Interop.Automation;
1613
using static Avalonia.Win32.Interop.UnmanagedMethods;
1714

@@ -643,6 +640,8 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
643640
Resized(clientSize / RenderScaling, _resizeReason);
644641
}
645642

643+
if (IsWindowVisible(_hwnd) && !_shown)
644+
_shown = true;
646645

647646
if (stateChanged)
648647
{
@@ -661,11 +660,17 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
661660

662661
if (_isClientAreaExtended)
663662
{
664-
UpdateExtendMargins();
663+
ExtendClientArea();
665664

666665
ExtendClientAreaToDecorationsChanged?.Invoke(true);
667666
}
668667
}
668+
else if (windowState == WindowState.Maximized && _isClientAreaExtended)
669+
{
670+
ExtendClientArea();
671+
672+
ExtendClientAreaToDecorationsChanged?.Invoke(true);
673+
}
669674

670675
return IntPtr.Zero;
671676
}
@@ -676,8 +681,7 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
676681

677682
case WindowsMessage.WM_MOVE:
678683
{
679-
PositionChanged?.Invoke(new PixelPoint((short)(ToInt32(lParam) & 0xffff),
680-
(short)(ToInt32(lParam) >> 16)));
684+
PositionChanged?.Invoke(Position);
681685
return IntPtr.Zero;
682686
}
683687

src/Windows/Avalonia.Win32/WindowImpl.cs

+28-74
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,8 @@ public Size FrameSize
263263
{
264264
get
265265
{
266-
if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
267-
{
268-
GetWindowRect(_hwnd, out var rcWindow);
269-
return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
270-
}
271-
272-
DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf<RECT>());
273-
return new Size(rect.Width, rect.Height) / RenderScaling;
266+
GetWindowRect(_hwnd, out var rcWindow);
267+
return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
274268
}
275269
}
276270

@@ -510,6 +504,7 @@ private unsafe bool SetUseHostBackdropBrush(bool useHostBackdropBrush)
510504
var result = DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
511505
return result == 0;
512506
}
507+
513508
public IEnumerable<object> Surfaces
514509
=> _glSurface is null ?
515510
new object[] { Handle, _framebuffer } :
@@ -520,15 +515,10 @@ public PixelPoint Position
520515
get
521516
{
522517
GetWindowRect(_hwnd, out var rc);
523-
524-
var border = HiddenBorderSize;
525-
return new PixelPoint(rc.left + border.Width, rc.top + border.Height);
518+
return new PixelPoint(rc.left, rc.top);
526519
}
527520
set
528521
{
529-
var border = HiddenBorderSize;
530-
value = new PixelPoint(value.X - border.Width, value.Y - border.Height);
531-
532522
SetWindowPos(
533523
Handle.Handle,
534524
IntPtr.Zero,
@@ -542,23 +532,6 @@ public PixelPoint Position
542532

543533
private bool HasFullDecorations => _windowProperties.Decorations == SystemDecorations.Full;
544534

545-
private PixelSize HiddenBorderSize
546-
{
547-
get
548-
{
549-
// Windows 10 and 11 add a 7 pixel invisible border on the left/right/bottom of windows for resizing
550-
if (Win32Platform.WindowsVersion.Major < 10 || !HasFullDecorations || GetStyle().HasFlag(WindowStyles.WS_POPUP))
551-
{
552-
return PixelSize.Empty;
553-
}
554-
555-
DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var clientRect, Marshal.SizeOf<RECT>());
556-
GetWindowRect(_hwnd, out var frameRect);
557-
var borderWidth = GetSystemMetrics(SystemMetric.SM_CXBORDER);
558-
559-
return new PixelSize(clientRect.left - frameRect.left - borderWidth, 0);
560-
}
561-
}
562535

563536
public void Move(PixelPoint point) => Position = point;
564537

@@ -594,25 +567,29 @@ public void Resize(Size value, WindowResizeReason reason)
594567

595568
GetWindowPlacement(_hwnd, out var windowPlacement);
596569

597-
var clientScreenOrigin = new POINT();
598-
ClientToScreen(_hwnd, ref clientScreenOrigin);
599-
600570
var requestedClientRect = new RECT
601571
{
602-
left = clientScreenOrigin.X,
603-
right = clientScreenOrigin.X + requestedClientWidth,
604-
605-
top = clientScreenOrigin.Y,
606-
bottom = clientScreenOrigin.Y + requestedClientHeight,
572+
left = 0,
573+
top = 0,
574+
right = requestedClientWidth,
575+
bottom = requestedClientHeight
607576
};
608577

609578
var requestedWindowRect = _isClientAreaExtended ? requestedClientRect : ClientRectToWindowRect(requestedClientRect);
579+
var windowWidth = requestedWindowRect.Width;
580+
var windowHeight = requestedWindowRect.Height;
610581

611-
if (requestedWindowRect.Width == windowPlacement.NormalPosition.Width && requestedWindowRect.Height == windowPlacement.NormalPosition.Height)
582+
if (windowWidth == windowPlacement.NormalPosition.Width && windowHeight == windowPlacement.NormalPosition.Height)
612583
{
613584
return;
614585
}
615586

587+
var position = Position;
588+
requestedWindowRect.left = position.X;
589+
requestedWindowRect.top = position.Y;
590+
requestedWindowRect.right = position.X + windowWidth;
591+
requestedWindowRect.bottom = position.Y + windowHeight;
592+
616593
windowPlacement.NormalPosition = requestedWindowRect;
617594

618595
windowPlacement.ShowCmd = !_shown ? ShowWindowCommand.Hide : _lastWindowState switch
@@ -1025,14 +1002,8 @@ private void SetFullScreen(bool fullscreen)
10251002
if (fullscreen)
10261003
{
10271004
GetWindowRect(_hwnd, out var windowRect);
1028-
GetClientRect(_hwnd, out var clientRect);
1029-
1030-
clientRect.left += windowRect.left;
1031-
clientRect.right += windowRect.left;
1032-
clientRect.top += windowRect.top;
1033-
clientRect.bottom += windowRect.top;
10341005

1035-
_savedWindowInfo.WindowRect = clientRect;
1006+
_savedWindowInfo.WindowRect = windowRect;
10361007

10371008
var current = GetStyle();
10381009
var currentEx = GetExtendedStyle();
@@ -1141,16 +1112,8 @@ private void ExtendClientArea()
11411112
_isClientAreaExtended = false;
11421113
return;
11431114
}
1144-
GetClientRect(_hwnd, out var rcClient);
11451115
GetWindowRect(_hwnd, out var rcWindow);
11461116

1147-
// Inform the application of the frame change.
1148-
SetWindowPos(_hwnd,
1149-
IntPtr.Zero,
1150-
rcWindow.left, rcWindow.top,
1151-
rcClient.Width, rcClient.Height,
1152-
SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOACTIVATE);
1153-
11541117
if (_isClientAreaExtended && WindowState != WindowState.FullScreen)
11551118
{
11561119
var margins = UpdateExtendMargins();
@@ -1170,8 +1133,6 @@ private void ExtendClientArea()
11701133
_offScreenMargin = new Thickness();
11711134
_extendedMargins = new Thickness();
11721135

1173-
Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), WindowResizeReason.Layout);
1174-
11751136
unsafe
11761137
{
11771138
int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT;
@@ -1189,6 +1150,13 @@ private void ExtendClientArea()
11891150
DisableCloseButton(_hwnd);
11901151
}
11911152

1153+
// Inform the application of the frame change.
1154+
SetWindowPos(_hwnd,
1155+
IntPtr.Zero,
1156+
rcWindow.left, rcWindow.top,
1157+
0, 0,
1158+
SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOSIZE);
1159+
11921160
ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended);
11931161
}
11941162

@@ -1454,7 +1422,7 @@ private void UpdateWindowProperties(WindowProperties newProperties, bool forceCh
14541422
{
14551423
style &= ~(fullDecorationFlags | WindowStyles.WS_THICKFRAME);
14561424

1457-
if (newProperties.Decorations == SystemDecorations.BorderOnly && newProperties.WindowState != WindowState.Maximized)
1425+
if (newProperties.Decorations == SystemDecorations.BorderOnly && newProperties.WindowState != WindowState.Maximized && newProperties.IsResizable)
14581426
{
14591427
style |= WindowStyles.WS_THICKFRAME | WindowStyles.WS_BORDER;
14601428
}
@@ -1483,8 +1451,6 @@ private void UpdateWindowProperties(WindowProperties newProperties, bool forceCh
14831451

14841452
if (!_isFullScreenActive && ((oldProperties.Decorations != newProperties.Decorations) || forceChanges))
14851453
{
1486-
var style = GetStyle();
1487-
14881454
var margin = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0;
14891455

14901456
var margins = new MARGINS
@@ -1497,23 +1463,11 @@ private void UpdateWindowProperties(WindowProperties newProperties, bool forceCh
14971463

14981464
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
14991465

1500-
15011466
if (_shown || forceChanges)
15021467
{
1503-
GetClientRect(_hwnd, out var oldClientRect);
1504-
var oldClientRectOrigin = new POINT();
1505-
ClientToScreen(_hwnd, ref oldClientRectOrigin);
1506-
oldClientRect.Offset(oldClientRectOrigin);
1507-
1508-
var newRect = oldClientRect;
1509-
1510-
if (newProperties.Decorations == SystemDecorations.Full)
1511-
{
1512-
AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle());
1513-
}
1514-
1515-
SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height,
1468+
SetWindowPos(_hwnd, IntPtr.Zero, 0, 0, 0 ,0,
15161469
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
1470+
SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE |
15171471
SetWindowPosFlags.SWP_FRAMECHANGED);
15181472
}
15191473
}

tests/Avalonia.IntegrationTests.Appium/WindowDecorationsTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public WindowDecorationsTests(DefaultAppFixture fixture)
1212
{
1313
}
1414

15-
[PlatformFact(TestPlatforms.MacOS)] // TODO fix me on Windows
15+
[Fact]
1616
public void Window_Size_Should_Be_Consistent_Between_Toggles()
1717
{
1818
var window = Session.FindElementByAccessibilityId("MainWindow");

tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

+40-2
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,44 @@ public void Window_Has_Disabled_Maximize_Button_When_CanResize_Is_False(ShowWind
311311
}
312312
}
313313

314+
[Fact]
315+
public void Changing_SystemDecorations_Should_Not_Change_Frame_Size_And_Position()
316+
{
317+
using (OpenWindow(null, ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
318+
{
319+
var info = GetWindowInfo();
320+
321+
Session.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
322+
Session.FindElementByAccessibilityId("SystemDecorationsNone").SendClick();
323+
var updatedInfo = GetWindowInfo();
324+
Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
325+
Assert.Equal(info.Position, updatedInfo.Position);
326+
327+
Session.FindElementByAccessibilityId("CurrentSystemDecorations").Click();
328+
Session.FindElementByAccessibilityId("SystemDecorationsFull").SendClick();
329+
updatedInfo = GetWindowInfo();
330+
Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
331+
Assert.Equal(info.Position, updatedInfo.Position);
332+
}
333+
}
334+
335+
[Fact]
336+
public void Changing_WindowState_Should_Not_Change_Frame_Size_And_Position()
337+
{
338+
using (OpenWindow())
339+
{
340+
var info = GetWindowInfo();
341+
342+
Session.FindElementByAccessibilityId("CurrentWindowState").SendClick();
343+
Session.FindElementByAccessibilityId("WindowStateMaximized").SendClick();
344+
Session.FindElementByAccessibilityId("CurrentWindowState").SendClick();
345+
Session.FindElementByAccessibilityId("WindowStateNormal").SendClick();
346+
var updatedInfo = GetWindowInfo();
347+
Assert.Equal(info.FrameSize, updatedInfo.FrameSize);
348+
Assert.Equal(info.Position, updatedInfo.Position);
349+
}
350+
}
351+
314352
public static TheoryData<Size?, ShowWindowMode, WindowStartupLocation, bool> StartupLocationData()
315353
{
316354
var sizes = new Size?[] { null, new Size(400, 300) };
@@ -391,8 +429,8 @@ private static void AssertCloseEnough(PixelPoint expected, PixelPoint actual)
391429
}
392430

393431
private IDisposable OpenWindow(
394-
Size? size,
395-
ShowWindowMode mode,
432+
Size? size = null,
433+
ShowWindowMode mode = ShowWindowMode.NonOwned,
396434
WindowStartupLocation location = WindowStartupLocation.Manual,
397435
WindowState state = Controls.WindowState.Normal,
398436
bool canResize = true,

0 commit comments

Comments
 (0)