Merge branch 'keep-on-same-display'
Keep window on the same display when its client size is changed. See #23533.
This commit is contained in:
commit
5f5e5c27dd
10 changed files with 161 additions and 77 deletions
2
.github/workflows/code_checks.yml
vendored
2
.github/workflows/code_checks.yml
vendored
|
|
@ -88,7 +88,7 @@ jobs:
|
|||
git fetch --depth=1 origin master
|
||||
if git diff origin/master \
|
||||
':**.h' ':**.cpp' \
|
||||
| grep -E '^\+.*(wxOVERRIDE|wxNOEXCEPT|[^"_@]NULL[^"_])'; then
|
||||
| grep -E '^\+.*(wxOVERRIDE|wxNOEXCEPT|[^"_@A-Za-z0-9]NULL[^"_A-Za-z0-9])'; then
|
||||
echo "::error ::Please use C++11 equivalents of the deprecated macros in the new code."
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ public:
|
|||
// it doesn't belong to any display
|
||||
static int GetFromPoint(const wxPoint& pt);
|
||||
|
||||
// find the display which has the biggest intersection with the given
|
||||
// rectangle or wxNOT_FOUND if the rectangle doesn't intersect any display
|
||||
static int GetFromRect(const wxRect& rect);
|
||||
|
||||
// find the display where the given window lies, return wxNOT_FOUND if it
|
||||
// is not shown at all
|
||||
static int GetFromWindow(const wxWindow *window);
|
||||
|
|
|
|||
|
|
@ -848,11 +848,20 @@ public:
|
|||
|
||||
// centre this rectangle in the given (usually, but not necessarily,
|
||||
// larger) one
|
||||
void MakeCenteredIn(const wxRect& r, int dir = wxBOTH)
|
||||
{
|
||||
if ( dir & wxHORIZONTAL )
|
||||
x = r.x + (r.width - width)/2;
|
||||
if ( dir & wxVERTICAL )
|
||||
y = r.y + (r.height - height)/2;
|
||||
}
|
||||
|
||||
// same as above but returns the new rectangle instead of modifying this one
|
||||
wxRect CentreIn(const wxRect& r, int dir = wxBOTH) const
|
||||
{
|
||||
return wxRect(dir & wxHORIZONTAL ? r.x + (r.width - width)/2 : x,
|
||||
dir & wxVERTICAL ? r.y + (r.height - height)/2 : y,
|
||||
width, height);
|
||||
wxRect rect(*this);
|
||||
rect.MakeCenteredIn(r, dir);
|
||||
return rect;
|
||||
}
|
||||
|
||||
wxRect CenterIn(const wxRect& r, int dir = wxBOTH) const
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ public:
|
|||
// return the display for the given point or wxNOT_FOUND
|
||||
virtual int GetFromPoint(const wxPoint& pt) = 0;
|
||||
|
||||
// return the display with biggest intersection with the given rectangle or
|
||||
// wxNOT_FOUND
|
||||
virtual int GetFromRect(const wxRect& rect);
|
||||
|
||||
// return the display for the given window or wxNOT_FOUND
|
||||
//
|
||||
// the window pointer must not be null (i.e. caller should check it)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,24 @@ public:
|
|||
*/
|
||||
static int GetFromPoint(const wxPoint& pt);
|
||||
|
||||
/**
|
||||
Returns the index of the display with biggest intersection with the
|
||||
given rectangle or @c wxNOT_FOUND if the rectangle doesn't intersect
|
||||
any display.
|
||||
|
||||
Note that usually the returned display will be the same display which
|
||||
contains the center of the rectangle, but this is not always the case,
|
||||
as rectangle might be partly visible even if its center is off screen,
|
||||
and in this case GetFromPoint() would returns @c wxNOT_FOUND, but this
|
||||
function would return a valid display.
|
||||
|
||||
@param rect
|
||||
The rectangle to check.
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
static int GetFromRect(const wxRect& rect);
|
||||
|
||||
/**
|
||||
Returns the index of the display on which the given window lies.
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,8 @@ public:
|
|||
centred in both directions but if @a dir includes only @c wxVERTICAL or
|
||||
only @c wxHORIZONTAL, then it is only centered in this direction while
|
||||
the other component of its position remains unchanged.
|
||||
|
||||
@see MakeCenteredIn()
|
||||
*/
|
||||
wxRect CentreIn(const wxRect& r, int dir = wxBOTH) const;
|
||||
wxRect CenterIn(const wxRect& r, int dir = wxBOTH) const;
|
||||
|
|
@ -475,6 +477,20 @@ public:
|
|||
*/
|
||||
bool IsEmpty() const;
|
||||
|
||||
/**
|
||||
Center this rectangle inside the given rectangle @a r.
|
||||
|
||||
By default, rectangle is centred in both directions but if @a dir
|
||||
includes only @c wxVERTICAL or only @c wxHORIZONTAL, then it is only
|
||||
centered in this direction while the other component of its position
|
||||
remains unchanged.
|
||||
|
||||
@see CenterIn()
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
void MakeCenteredIn(const wxRect& r, int dir = wxBOTH);
|
||||
|
||||
///@{
|
||||
/**
|
||||
Moves the rectangle by the specified offset. If @a dx is positive, the
|
||||
|
|
|
|||
|
|
@ -106,6 +106,11 @@ wxDisplay::wxDisplay(const wxWindow* window)
|
|||
return Factory().GetFromPoint(pt);
|
||||
}
|
||||
|
||||
/* static */ int wxDisplay::GetFromRect(const wxRect& rect)
|
||||
{
|
||||
return Factory().GetFromRect(rect);
|
||||
}
|
||||
|
||||
/* static */ int wxDisplay::GetFromWindow(const wxWindow *window)
|
||||
{
|
||||
wxCHECK_MSG( window, wxNOT_FOUND, wxT("invalid window") );
|
||||
|
|
@ -241,6 +246,34 @@ wxDisplayImpl* wxDisplayFactory::GetPrimaryDisplay()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
int wxDisplayFactory::GetFromRect(const wxRect& r)
|
||||
{
|
||||
int display = wxNOT_FOUND;
|
||||
|
||||
// Find the display with the biggest intersection with the given window.
|
||||
//
|
||||
// Note that just using GetFromPoint() with the center of the rectangle is
|
||||
// not correct in general, as the center might lie outside of the visible
|
||||
// area, while the rectangle itself could be partially visible. Moreover,
|
||||
// in some exotic (L-shaped) display layouts, the center might not actually
|
||||
// be on the display containing the biggest part of the rectangle even if
|
||||
// it is visible.
|
||||
int biggestOverlapArea = 0;
|
||||
const unsigned count = GetCount();
|
||||
for ( unsigned n = 0; n < count; ++n )
|
||||
{
|
||||
const auto overlap = GetDisplay(n)->GetGeometry().Intersect(r);
|
||||
const int overlapArea = overlap.width * overlap.height;
|
||||
if ( overlapArea > biggestOverlapArea )
|
||||
{
|
||||
biggestOverlapArea = overlapArea;
|
||||
display = n;
|
||||
}
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
int wxDisplayFactory::GetFromWindow(const wxWindow *window)
|
||||
{
|
||||
wxCHECK_MSG( window, wxNOT_FOUND, "window can't be null" );
|
||||
|
|
@ -252,9 +285,7 @@ int wxDisplayFactory::GetFromWindow(const wxWindow *window)
|
|||
if ( !window->GetHandle() )
|
||||
return wxNOT_FOUND;
|
||||
|
||||
// consider that the window belongs to the display containing its centre
|
||||
const wxRect r(window->GetScreenRect());
|
||||
return GetFromPoint(wxPoint(r.x + r.width/2, r.y + r.height/2));
|
||||
return GetFromRect(window->GetScreenRect());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ public:
|
|||
virtual wxDisplayImpl *CreateDisplay(unsigned n) override;
|
||||
virtual unsigned GetCount() override { return unsigned(m_displays.size()); }
|
||||
virtual int GetFromPoint(const wxPoint& pt) override;
|
||||
virtual int GetFromRect(const wxRect& rect) override;
|
||||
virtual int GetFromWindow(const wxWindow *window) override;
|
||||
|
||||
void InvalidateCache() override
|
||||
|
|
@ -608,7 +609,7 @@ wxDisplayImpl *wxDisplayFactoryMSW::CreateDisplay(unsigned n)
|
|||
return new wxDisplayMSW(n, m_displays[n]);
|
||||
}
|
||||
|
||||
// helper for GetFromPoint() and GetFromWindow()
|
||||
// helper for all GetFromXXX() functions
|
||||
int wxDisplayFactoryMSW::FindDisplayFromHMONITOR(HMONITOR hmon) const
|
||||
{
|
||||
if ( hmon )
|
||||
|
|
@ -634,17 +635,21 @@ int wxDisplayFactoryMSW::GetFromPoint(const wxPoint& pt)
|
|||
MONITOR_DEFAULTTONULL));
|
||||
}
|
||||
|
||||
int wxDisplayFactoryMSW::GetFromRect(const wxRect& rect)
|
||||
{
|
||||
RECT rc;
|
||||
wxCopyRectToRECT(rect, rc);
|
||||
return FindDisplayFromHMONITOR(::MonitorFromRect(&rc,
|
||||
MONITOR_DEFAULTTONULL));
|
||||
}
|
||||
|
||||
int wxDisplayFactoryMSW::GetFromWindow(const wxWindow *window)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
return FindDisplayFromHMONITOR(::MonitorFromWindow(GetHwndOf(window),
|
||||
MONITOR_DEFAULTTONULL));
|
||||
#else
|
||||
const wxSize halfsize = window->GetSize() / 2;
|
||||
wxPoint pt = window->GetScreenPosition();
|
||||
pt.x += halfsize.x;
|
||||
pt.y += halfsize.y;
|
||||
return GetFromPoint(pt);
|
||||
return GetFromRect(window->GetScreenRect());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,34 @@ 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.
|
||||
const wxRect currentRect = wxRectFromRECT(rectWin);
|
||||
proposedRect.MakeCenteredIn(currentRect, wxHORIZONTAL);
|
||||
if ( wxDisplay::GetFromRect(proposedRect) != currentDisplay )
|
||||
{
|
||||
// And if this isn't sufficient, then vertically too.
|
||||
proposedRect.MakeCenteredIn(currentRect, wxVERTICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -2300,6 +2330,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 +2339,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,64 +22,38 @@
|
|||
#include "asserthelper.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// test class
|
||||
// tests
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class RectTestCase : public CppUnit::TestCase
|
||||
{
|
||||
public:
|
||||
RectTestCase() { }
|
||||
|
||||
private:
|
||||
CPPUNIT_TEST_SUITE( RectTestCase );
|
||||
CPPUNIT_TEST( CentreIn );
|
||||
CPPUNIT_TEST( InflateDeflate );
|
||||
CPPUNIT_TEST( Operators );
|
||||
CPPUNIT_TEST( Union );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void CentreIn();
|
||||
void InflateDeflate();
|
||||
void Operators();
|
||||
void Union();
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(RectTestCase);
|
||||
};
|
||||
|
||||
// register in the unnamed registry so that these tests are run by default
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( RectTestCase );
|
||||
|
||||
// also include in its own registry so that these tests can be run alone
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( RectTestCase, "RectTestCase" );
|
||||
|
||||
void RectTestCase::CentreIn()
|
||||
TEST_CASE("wxRect::CentreIn", "[rect]")
|
||||
{
|
||||
typedef wxRect R;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL( R(45, 45, 10, 10),
|
||||
R(0, 0, 10, 10).CentreIn(R(0, 0, 100, 100)));
|
||||
CHECK( R(0, 0, 10, 10).CentreIn(R(0, 0, 100, 100)) == R(45, 45, 10, 10) );
|
||||
CHECK( R(0, 0, 20, 20).CentreIn(R(0, 0, 10, 10)) == R(-5, -5, 20, 20) );
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL( R(-5, -5, 20, 20),
|
||||
R(0, 0, 20, 20).CentreIn(R(0, 0, 10, 10)));
|
||||
R r(-10, -10, 20, 20);
|
||||
r.MakeCenteredIn(R(0, 0, 100, 100), wxHORIZONTAL);
|
||||
CHECK( r == R(40, -10, 20, 20) );
|
||||
}
|
||||
|
||||
void RectTestCase::InflateDeflate()
|
||||
TEST_CASE("wxRect::InflateDeflate", "[rect]")
|
||||
{
|
||||
// This is the rectangle from the example in the documentation of wxRect::Inflate().
|
||||
const wxRect r1(10, 10, 20, 40);
|
||||
|
||||
CPPUNIT_ASSERT(r1.Inflate( 10, 10)==wxRect( 0, 0, 40, 60));
|
||||
CPPUNIT_ASSERT(r1.Inflate( 20, 30)==wxRect(-10, -20, 60, 100));
|
||||
CPPUNIT_ASSERT(r1.Inflate(-10, -10)==wxRect( 20, 20, 0, 20));
|
||||
CPPUNIT_ASSERT(r1.Inflate(-15, -15)==wxRect( 20, 25, 0, 10));
|
||||
CHECK(r1.Inflate( 10, 10)==wxRect( 0, 0, 40, 60));
|
||||
CHECK(r1.Inflate( 20, 30)==wxRect(-10, -20, 60, 100));
|
||||
CHECK(r1.Inflate(-10, -10)==wxRect( 20, 20, 0, 20));
|
||||
CHECK(r1.Inflate(-15, -15)==wxRect( 20, 25, 0, 10));
|
||||
|
||||
CPPUNIT_ASSERT(r1.Inflate( 10, 10)==r1.Deflate(-10, -10));
|
||||
CPPUNIT_ASSERT(r1.Inflate( 20, 30)==r1.Deflate(-20, -30));
|
||||
CPPUNIT_ASSERT(r1.Inflate(-10, -10)==r1.Deflate( 10, 10));
|
||||
CPPUNIT_ASSERT(r1.Inflate(-15, -15)==r1.Deflate( 15, 15));
|
||||
CHECK(r1.Inflate( 10, 10)==r1.Deflate(-10, -10));
|
||||
CHECK(r1.Inflate( 20, 30)==r1.Deflate(-20, -30));
|
||||
CHECK(r1.Inflate(-10, -10)==r1.Deflate( 10, 10));
|
||||
CHECK(r1.Inflate(-15, -15)==r1.Deflate( 15, 15));
|
||||
}
|
||||
|
||||
void RectTestCase::Operators()
|
||||
TEST_CASE("wxRect::Operators", "[rect]")
|
||||
{
|
||||
// test + operator which works like Union but does not ignore empty rectangles
|
||||
static const struct RectData
|
||||
|
|
@ -106,25 +80,20 @@ void RectTestCase::Operators()
|
|||
{
|
||||
const RectData& data = s_rects[n];
|
||||
|
||||
CPPUNIT_ASSERT(
|
||||
( data.GetFirst() + data.GetSecond() ) == data.GetResult()
|
||||
);
|
||||
|
||||
CPPUNIT_ASSERT(
|
||||
( data.GetSecond() + data.GetFirst() ) == data.GetResult()
|
||||
);
|
||||
CHECK( (data.GetFirst() + data.GetSecond()) == data.GetResult() );
|
||||
CHECK( (data.GetSecond() + data.GetFirst()) == data.GetResult() );
|
||||
}
|
||||
|
||||
// test operator*() which returns the intersection of two rectangles
|
||||
wxRect r1 = wxRect(0, 2, 3, 4);
|
||||
wxRect r2 = wxRect(1, 2, 7, 8);
|
||||
r1 *= r2;
|
||||
CPPUNIT_ASSERT(wxRect(1, 2, 2, 4) == r1);
|
||||
CHECK(wxRect(1, 2, 2, 4) == r1);
|
||||
|
||||
CPPUNIT_ASSERT( (r1 * wxRect()).IsEmpty() );
|
||||
CHECK( (r1 * wxRect()).IsEmpty() );
|
||||
}
|
||||
|
||||
void RectTestCase::Union()
|
||||
TEST_CASE("wxRect::Union", "[rect]")
|
||||
{
|
||||
static const struct RectData
|
||||
{
|
||||
|
|
@ -150,12 +119,8 @@ void RectTestCase::Union()
|
|||
{
|
||||
const RectData& data = s_rects[n];
|
||||
|
||||
CPPUNIT_ASSERT(
|
||||
data.GetFirst().Union(data.GetSecond()) == data.GetResult()
|
||||
);
|
||||
CHECK( data.GetFirst().Union(data.GetSecond()) == data.GetResult() );
|
||||
|
||||
CPPUNIT_ASSERT(
|
||||
data.GetSecond().Union(data.GetFirst()) == data.GetResult()
|
||||
);
|
||||
CHECK( data.GetSecond().Union(data.GetFirst()) == data.GetResult() );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue