Keep window on the same display in SetClientSize()

It was unexpected that changing the window client size could change the
display the window was on and, even worse, result in further size
changes because changing the display could change the DPI scaling used.
And, worst of all, if the window wxEVT_DPI_CHANGED handler called
SetClientSize(), possibly indirectly such as via wxSizer::Fit(), this
could result in an infinite recursion when moving the window to a high
definition display located to the left of the standard definition one
(the problem didn't happen for the reciprocal display arrangement
because increasing the window size couldn't change the window display
in that case).

So ensure that the window remains on the same display it is on now, by
keeping its center position unchanged, instead of keeping the position
of its top left corner as we did before.
This commit is contained in:
Vadim Zeitlin 2023-05-08 22:47:56 +01:00
parent 438da9c99a
commit 072d581e87

View file

@ -2277,6 +2277,10 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
const int widthWin = rectWin.right - rectWin.left,
heightWin = rectWin.bottom - rectWin.top;
wxRect proposedRect(rectWin.left, rectWin.top,
width + widthWin - rectClient.right,
height + heightWin - rectClient.bottom);
if ( IsTopLevel() )
{
// toplevel window's coordinates are mirrored if the TLW is a child of another
@ -2288,8 +2292,33 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
if ( tlwParent && (::GetWindowLong(tlwParent, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0 )
{
const int diffWidth = width - (rectClient.right - rectClient.left);
rectWin.left -= diffWidth;
rectWin.right -= diffWidth;
proposedRect.x -= diffWidth;
}
// Another complication with TLWs is that changing their size may
// change the monitor they are on, even without changing their
// position. This is unexpected and especially so if the new
// monitor uses a different DPI scaling and so moving the window to
// it changes its size -- which may result in an infinite recursion
// if the window calls SetClientSize() when DPI changes.
//
// So ensure that the window stays on the same monitor, adjusting
// its position if necessary.
const int currentDisplay =
wxDisplay::GetFromWindow(static_cast<const wxWindow*>(this));
if ( currentDisplay != wxNOT_FOUND &&
wxDisplay::GetFromRect(proposedRect) != currentDisplay )
{
// It's not obvious how to determine the smallest modification
// of the window position sufficient for keeping it on the
// current display, so keep things simple and preserve the
// position of its center horizontally.
proposedRect.x += widthWin/2 - proposedRect.width/2;
if ( wxDisplay::GetFromRect(proposedRect) != currentDisplay )
{
// And if this isn't sufficient, then vertically too.
proposedRect.y += heightWin/2 - proposedRect.height/2;
}
}
}
else
@ -2300,6 +2329,8 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
if ( parent )
{
::ScreenToClient(GetHwndOf(parent), (POINT *)&rectWin);
proposedRect.x = rectWin.left;
proposedRect.y = rectWin.top;
}
}
@ -2307,9 +2338,9 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
// and not defer it here as otherwise the value returned by
// GetClient/WindowRect() wouldn't change as the window wouldn't be
// really resized
MSWMoveWindowToAnyPosition(GetHwnd(), rectWin.left, rectWin.top,
width + widthWin - rectClient.right,
height + heightWin - rectClient.bottom, true);
MSWMoveWindowToAnyPosition(GetHwnd(), proposedRect.x, proposedRect.y,
proposedRect.width, proposedRect.height,
true);
}
}