Browse Source

Win32: fix None/BorderOnly maximized position on secondary screens (#21136)

* Win32: fix None/BorderOnly maximized position on secondary screens

* Fix WindowCustomizationsPage

* Use correct screen when minimized

* Fallback to nearest monitor
pull/21168/head
Julien Lebosquain 6 months ago
committed by GitHub
parent
commit
f960884d9d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml
  2. 104
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

3
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml

@ -7,7 +7,8 @@
Header="Window Customizations"
x:Class="ControlCatalog.Pages.WindowCustomizationsPage"
x:DataType="viewModels:MainWindowViewModel"
x:CompileBindings="True">
x:CompileBindings="True"
DataContext="{Binding $parent[Window].DataContext}">
<StackPanel>
<StackPanel

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

@ -799,39 +799,16 @@ namespace Avalonia.Win32
// A window without a caption (i.e. None and BorderOnly decorations) maximizes to the whole screen
// by default. Adjust that to the screen's working area instead.
var style = GetStyle();
if (!style.HasAllFlags(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME))
if (!style.HasAllFlags(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME) &&
Screen.ScreenFromHwnd(Hwnd, MONITOR.MONITOR_DEFAULTTONEAREST) is { } screen)
{
var screen = Screen.ScreenFromHwnd(Hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
if (screen?.WorkingArea is { } workingArea)
{
var x = workingArea.X;
var y = workingArea.Y;
var cx = workingArea.Width;
var cy = workingArea.Height;
var adjuster = CreateWindowRectAdjuster();
var borderThickness = new RECT();
var adjustedStyle = style & ~WindowStyles.WS_CAPTION;
if (style.HasAllFlags(WindowStyles.WS_BORDER))
adjustedStyle |= WindowStyles.WS_BORDER;
if (style.HasAllFlags(WindowStyles.WS_CAPTION))
adjustedStyle |= WindowStyles.WS_THICKFRAME;
adjuster.Adjust(ref borderThickness, adjustedStyle, 0);
x += borderThickness.left;
y += borderThickness.top;
cx += -borderThickness.left + borderThickness.right;
cy += -borderThickness.top + borderThickness.bottom;
mmi.ptMaxPosition.X = x;
mmi.ptMaxPosition.Y = y;
mmi.ptMaxSize.X = cx;
mmi.ptMaxSize.Y = cy;
}
var maximizedRect = GetCaptionlessMaximizedRect(style, screen.WorkingArea);
// We aren't changing ptMaxPosition because its coordinates must always target the primary screen.
// We can't do that, since the work area might not be the same for all screens.
// Instead, only set the desired max size here.
// WM_WINDOWPOSCHANGING moves the window to the correct position.
mmi.ptMaxSize.X = maximizedRect.Width;
mmi.ptMaxSize.Y = maximizedRect.Height;
}
if (_minSize.Width > 0)
@ -862,6 +839,37 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
case WindowsMessage.WM_WINDOWPOSCHANGING:
{
var pos = (WINDOWPOS*)lParam;
var style = GetStyle();
var flags = (SetWindowPosFlags)pos->flags;
// A window without a caption (i.e. None and BorderOnly decorations) maximizes to the whole screen
// by default. Adjust that to the screen's working area instead.
if (!style.HasAllFlags(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME) &&
style.HasAllFlags(WindowStyles.WS_MAXIMIZE) &&
!_isFullScreenActive &&
!flags.HasAllFlags(SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE))
{
// Prefer ScreenFromRect as it contains the new position.
// 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))
?? Screen.ScreenFromHwnd(Hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
if (screen is not null)
{
var maximizedRect = GetCaptionlessMaximizedRect(style, screen.WorkingArea);
pos->x = maximizedRect.X;
pos->y = maximizedRect.Y;
pos->cx = maximizedRect.Width;
pos->cy = maximizedRect.Height;
return IntPtr.Zero;
}
}
break;
}
case WindowsMessage.WM_DISPLAYCHANGE:
{
Screen?.OnChanged();
@ -996,6 +1004,38 @@ namespace Avalonia.Win32
return DefWindowProc(hWnd, msg, wParam, lParam);
}
/// <summary>
/// Gets the expected maximized rect for a window without a caption.
/// </summary>
private PixelRect GetCaptionlessMaximizedRect(WindowStyles style, PixelRect workingArea)
{
var x = workingArea.X;
var y = workingArea.Y;
var cx = workingArea.Width;
var cy = workingArea.Height;
var adjuster = CreateWindowRectAdjuster();
var borderThickness = new RECT();
var adjustedStyle = style & ~WindowStyles.WS_CAPTION;
if (style.HasAllFlags(WindowStyles.WS_BORDER))
adjustedStyle |= WindowStyles.WS_BORDER;
if (style.HasAllFlags(WindowStyles.WS_CAPTION))
adjustedStyle |= WindowStyles.WS_THICKFRAME;
adjuster.Adjust(ref borderThickness, adjustedStyle, 0);
x += borderThickness.left;
y += borderThickness.top;
cx += -borderThickness.left + borderThickness.right;
cy += -borderThickness.top + borderThickness.bottom;
return new PixelRect(x, y, cx, cy);
}
internal bool IsOurWindow(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)

Loading…
Cancel
Save