Browse Source

Win32: fix BorderOnly maximized position on secondary screens in more cases (#21229)

* Update IntegrationTests.Win32 to test all screens

* Win32: use GetWindowPlacement to determine the screen to maximize to
pull/21189/merge
Julien Lebosquain 2 weeks ago
committed by GitHub
parent
commit
dd9217ee9d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  2. 35
      tests/Avalonia.IntegrationTests.Win32/ExtendClientAreaWindowTests.cs
  3. 34
      tests/Avalonia.IntegrationTests.Win32/StandardWindowTests.cs
  4. 5
      tests/Avalonia.IntegrationTests.Win32/UnmanagedMethods.cs
  5. 8
      tests/Avalonia.IntegrationTests.Win32/WindowExtensions.cs

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

@ -852,9 +852,11 @@ namespace Avalonia.Win32
!_isFullScreenActive &&
!flags.HasAllFlags(SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE))
{
// Prefer ScreenFromRect as it contains the new position.
GetWindowPlacement(Hwnd, out var placement);
// Prefer ScreenFromRect with the window's restored bounds.
// If the window was minimized, ScreenFromHwnd won't return the correct monitor at this point.
var screen = Screen.ScreenFromRect(new PixelRect(pos->x, pos->y, pos->cx, pos->cy))
var screen = Screen.ScreenFromRect(placement.NormalPosition.ToPixelRect())
?? Screen.ScreenFromHwnd(Hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
if (screen is not null)

35
tests/Avalonia.IntegrationTests.Win32/ExtendClientAreaWindowTests.cs

@ -3,17 +3,17 @@ using System.Linq;
using System.Threading.Tasks;
using Avalonia.Automation;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.VisualTree;
using Xunit;
using static Avalonia.IntegrationTests.Win32.UnmanagedMethods;
namespace Avalonia.IntegrationTests.Win32;
public abstract class ExtendClientAreaWindowTests : IDisposable
{
private const double ClientWidth = 200;
private const double ClientHeight = 200;
private const int ClientWidth = 200;
private const int ClientHeight = 200;
private Window? _window;
@ -28,10 +28,13 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
protected abstract WindowDecorations Decorations { get; }
public static MatrixTheoryData<bool, WindowState> States
=> new([true, false], Enum.GetValues<WindowState>());
public static MatrixTheoryData<int, WindowState, bool> States
=> new(
Enumerable.Range(0, GetSystemMetrics(SM_CMONITORS)),
Enum.GetValues<WindowState>(),
[true, false]);
private async Task InitWindowAsync(WindowState state, bool canResize)
private async Task InitWindowAsync(int screenIndex, WindowState state, bool canResize)
{
Assert.Null(_window);
@ -44,7 +47,6 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
Width = ClientWidth,
Height = ClientHeight,
WindowStartupLocation = WindowStartupLocation.Manual,
Position = new PixelPoint(50, 50),
Content = new Border
{
Background = Brushes.DodgerBlue,
@ -53,6 +55,9 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
}
};
var screenCenter = _window.Screens.All[screenIndex].Bounds.Center;
_window.Position = new PixelPoint(screenCenter.X - ClientWidth / 2, screenCenter.Y - ClientHeight / 2);
_window.Show();
await Window.WhenLoadedAsync();
@ -60,9 +65,9 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
[Theory]
[MemberData(nameof(States))]
public async Task Normal_State_Respects_Client_Size(bool canResize, WindowState initialState)
public async Task Normal_State_Respects_Client_Size(int screenIndex, WindowState initialState, bool canResize)
{
await InitWindowAsync(initialState, canResize);
await InitWindowAsync(screenIndex, initialState, canResize);
if (initialState != WindowState.Normal)
Window.WindowState = WindowState.Normal;
@ -79,16 +84,16 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
[Theory]
[MemberData(nameof(States))]
public async Task Maximized_State_Fills_Screen_Working_Area(bool canResize, WindowState initialState)
public async Task Maximized_State_Fills_Screen_Working_Area(int screenIndex, WindowState initialState, bool canResize)
{
await InitWindowAsync(initialState, canResize);
await InitWindowAsync(screenIndex, initialState, canResize);
if (initialState != WindowState.Maximized)
Window.WindowState = WindowState.Maximized;
// The client size should match the screen working area
var clientSize = Window.GetWin32ClientSize();
var screenWorkingArea = Window.GetScreen().WorkingArea;
var screenWorkingArea = Window.GetScreenAtIndex(screenIndex).WorkingArea;
Assert.Equal(screenWorkingArea.Size, clientSize);
VerifyMaximizedState();
@ -98,16 +103,16 @@ public abstract class ExtendClientAreaWindowTests : IDisposable
[Theory]
[MemberData(nameof(States))]
public async Task FullScreen_State_Fills_Screen(bool canResize, WindowState initialState)
public async Task FullScreen_State_Fills_Screen(int screenIndex, WindowState initialState, bool canResize)
{
await InitWindowAsync(initialState, canResize);
await InitWindowAsync(screenIndex, initialState, canResize);
if (initialState != WindowState.FullScreen)
Window.WindowState = WindowState.FullScreen;
// The client size should match the screen bounds
var clientSize = Window.GetWin32ClientSize();
var screenBounds = Window.GetScreen().Bounds;
var screenBounds = Window.GetScreenAtIndex(screenIndex).Bounds;
Assert.Equal(screenBounds.Width, clientSize.Width);
Assert.Equal(screenBounds.Height, clientSize.Height);

34
tests/Avalonia.IntegrationTests.Win32/StandardWindowTests.cs

@ -2,19 +2,16 @@
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Chrome;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
using Xunit;
using static Avalonia.IntegrationTests.Win32.UnmanagedMethods;
namespace Avalonia.IntegrationTests.Win32;
public abstract class StandardWindowTests : IDisposable
{
private const double ClientWidth = 200;
private const double ClientHeight = 200;
private const int ClientWidth = 200;
private const int ClientHeight = 200;
private Window? _window;
@ -31,10 +28,13 @@ public abstract class StandardWindowTests : IDisposable
protected abstract bool HasCaption { get; }
public static MatrixTheoryData<bool, WindowState> States
=> new([true, false], Enum.GetValues<WindowState>());
public static MatrixTheoryData<int, WindowState, bool> States
=> new(
Enumerable.Range(0, GetSystemMetrics(SM_CMONITORS)),
Enum.GetValues<WindowState>(),
[true, false]);
private async Task InitWindowAsync(WindowState state, bool canResize)
private async Task InitWindowAsync(int screenIndex, WindowState state, bool canResize)
{
Assert.Null(_window);
@ -47,7 +47,6 @@ public abstract class StandardWindowTests : IDisposable
Width = ClientWidth,
Height = ClientHeight,
WindowStartupLocation = WindowStartupLocation.Manual,
Position = new PixelPoint(50, 50),
Content = new Border
{
Background = Brushes.DodgerBlue,
@ -56,6 +55,9 @@ public abstract class StandardWindowTests : IDisposable
}
};
var screenCenter = _window.Screens.All[screenIndex].Bounds.Center;
_window.Position = new PixelPoint(screenCenter.X - ClientWidth / 2, screenCenter.Y - ClientHeight / 2);
_window.Show();
await Window.WhenLoadedAsync();
@ -63,16 +65,16 @@ public abstract class StandardWindowTests : IDisposable
[Theory]
[MemberData(nameof(States))]
public async Task Maximized_State_Fills_Screen_Working_Area(bool canResize, WindowState initialState)
public async Task Maximized_State_Fills_Screen_Working_Area(int screenIndex, WindowState initialState, bool canResize)
{
await InitWindowAsync(initialState, canResize);
await InitWindowAsync(screenIndex, initialState, canResize);
if (initialState != WindowState.Maximized)
Window.WindowState = WindowState.Maximized;
// The client size should match the screen working area
var clientSize = Window.GetWin32ClientSize();
var screenWorkingArea = Window.GetScreen().WorkingArea;
var screenWorkingArea = Window.GetScreenAtIndex(screenIndex).WorkingArea;
if (HasCaption)
{
@ -85,16 +87,16 @@ public abstract class StandardWindowTests : IDisposable
[Theory]
[MemberData(nameof(States))]
public async Task FullScreen_State_Fills_Screen(bool canResize, WindowState initialState)
public async Task FullScreen_State_Fills_Screen(int screenIndex, WindowState initialState, bool canResize)
{
await InitWindowAsync(initialState, canResize);
await InitWindowAsync(screenIndex, initialState, canResize);
if (initialState != WindowState.FullScreen)
Window.WindowState = WindowState.FullScreen;
// The client size should match the screen bounds
var clientSize = Window.GetWin32ClientSize();
var screenBounds = Window.GetScreen().Bounds;
var screenBounds = Window.GetScreenAtIndex(screenIndex).Bounds;
Assert.Equal(screenBounds.Width, clientSize.Width);
Assert.Equal(screenBounds.Height, clientSize.Height);

5
tests/Avalonia.IntegrationTests.Win32/UnmanagedMethods.cs

@ -13,6 +13,9 @@ internal static partial class UnmanagedMethods
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[LibraryImport("user32.dll")]
public static partial int GetSystemMetrics(int nIndex);
public struct RECT
{
public int left;
@ -20,4 +23,6 @@ internal static partial class UnmanagedMethods
public int right;
public int bottom;
}
public const int SM_CMONITORS = 80;
}

8
tests/Avalonia.IntegrationTests.Win32/WindowExtensions.cs

@ -27,12 +27,8 @@ internal static class WindowExtensions
}
}
public static Screen GetScreen(this Window window)
{
var screen = window.Screens.ScreenFromWindow(window);
Assert.NotNull(screen);
return screen;
}
public static Screen GetScreenAtIndex(this Window window, int index)
=> window.Screens.All[index];
public static PixelSize GetWin32ClientSize(this Window window)
{

Loading…
Cancel
Save