Merge branch 'MoveOutsideShortInterval' of https://github.com/catalinr/wxWidgets

Allow using positions in the entire int range for window positions under
MSW, and not just those in (slightly less than) short range, that are
supported by the native API.

Closes #4262.

Closes https://github.com/wxWidgets/wxWidgets/pull/779
This commit is contained in:
Vadim Zeitlin
2018-05-02 23:19:11 +02:00
7 changed files with 182 additions and 90 deletions

View File

@@ -91,6 +91,7 @@ wxMSW:
- Fix handling of AUX2 mouse button events (Trylz). - Fix handling of AUX2 mouse button events (Trylz).
- Fix saving/restoring window position for maximized windows. - Fix saving/restoring window position for maximized windows.
- Fix stack corruption when using wxStackWalker (srfisk). - Fix stack corruption when using wxStackWalker (srfisk).
- Fix positioning windows at positions >= SHORT_MAX (Cătălin Răceanu).
3.1.1: (released 2018-02-19) 3.1.1: (released 2018-02-19)

View File

@@ -729,8 +729,27 @@ private:
bool MSWSafeIsDialogMessage(WXMSG* msg); bool MSWSafeIsDialogMessage(WXMSG* msg);
#endif // __WXUNIVERSAL__ #endif // __WXUNIVERSAL__
#if wxUSE_DEFERRED_SIZING static inline bool MSWIsPositionDirectlySupported(int x, int y)
{
// The supported coordinate intervals for various functions are:
// - MoveWindow, DeferWindowPos: [-32768, 32767] a.k.a. [SHRT_MIN, SHRT_MAX];
// - CreateWindow, CreateWindowEx: [-32768, 32554].
// CreateXXX will _sometimes_ manage to create the window at higher coordinates
// like 32580, 32684, 32710, but that was not consistent and the lowest common
// limit was 32554 (so far at least).
return (x >= SHRT_MIN && x <= 32554 && y >= SHRT_MIN && y <= 32554);
}
protected: protected:
WXHWND MSWCreateWindowAtAnyPosition(WXDWORD exStyle, const wxChar* clName,
const wxChar* title, WXDWORD style,
int x, int y, int width, int height,
WXHWND parent, wxWindowID id);
void MSWMoveWindowToAnyPosition(WXHWND hwnd, int x, int y,
int width, int height, bool bRepaint);
#if wxUSE_DEFERRED_SIZING
// this function is called after the window was resized to its new size // this function is called after the window was resized to its new size
virtual void MSWEndDeferWindowPos() virtual void MSWEndDeferWindowPos()
{ {

View File

@@ -132,27 +132,19 @@ bool wxControl::MSWCreateControl(const wxChar *classname,
// ... and adjust it to account for a possible parent frames toolbar // ... and adjust it to account for a possible parent frames toolbar
AdjustForParentClientOrigin(x, y); AdjustForParentClientOrigin(x, y);
m_hWnd = (WXHWND)::CreateWindowEx m_hWnd = MSWCreateWindowAtAnyPosition
( (
exstyle, // extended style exstyle, // extended style
classname, // the kind of control to create classname, // the kind of control to create
label.t_str(), // the window name label.t_str(), // the window name
style, // the window style style, // the window style
x, y, w, h, // the window position and size x, y, w, h, // the window position and size
GetHwndOf(GetParent()), // parent GetHwndOf(GetParent()), // parent
(HMENU)wxUIntToPtr(GetId()), // child id GetId() // child id
wxGetInstance(), // app instance );
NULL // creation parameters
);
if ( !m_hWnd ) if ( !m_hWnd )
{ {
wxLogLastError(wxString::Format
(
wxT("CreateWindowEx(\"%s\", flags=%08lx, ex=%08lx)"),
classname, style, exstyle
));
return false; return false;
} }

View File

@@ -298,19 +298,17 @@ bool wxSpinCtrl::Create(wxWindow *parent,
// create the text window // create the text window
m_hwndBuddy = (WXHWND)::CreateWindowEx m_hwndBuddy = MSWCreateWindowAtAnyPosition
( (
exStyle, // sunken border exStyle, // sunken border
wxT("EDIT"), // window class wxT("EDIT"), // window class
NULL, // no window title NULL, // no window title
msStyle, // style (will be shown later) msStyle, // style (will be shown later)
pos.x, pos.y, // position pos.x, pos.y, // position
0, 0, // size (will be set later) 0, 0, // size (will be set later)
GetHwndOf(parent), // parent GetHwndOf(parent), // parent
(HMENU)-1, // control id -1 // control id
wxGetInstance(), // app instance );
NULL // unused client data
);
if ( !m_hwndBuddy ) if ( !m_hwndBuddy )
{ {

View File

@@ -333,7 +333,7 @@ void wxStaticBitmap::SetImageNoCopy( wxGDIImage* image)
w = width; w = width;
h = height; h = height;
::MoveWindow(GetHwnd(), x, y, width, height, FALSE); MSWMoveWindowToAnyPosition(GetHwnd(), x, y, width, height, false);
} }
} }

View File

@@ -1935,38 +1935,6 @@ void wxWindowMSW::DoClientToScreen(int *x, int *y) const
bool bool
wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height) wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height)
{ {
#if wxUSE_DEFERRED_SIZING
// if our parent had prepared a defer window handle for us, use it (unless
// we are a top level window)
wxWindowMSW * const parent = IsTopLevel() ? NULL : GetParent();
HDWP hdwp = parent ? (HDWP)parent->m_hDWP : NULL;
if ( hdwp )
{
hdwp = ::DeferWindowPos(hdwp, (HWND)hwnd, NULL, x, y, width, height,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if ( !hdwp )
{
wxLogLastError(wxT("DeferWindowPos"));
}
}
if ( parent )
{
// hdwp must be updated as it may have been changed
parent->m_hDWP = (WXHANDLE)hdwp;
}
if ( hdwp )
{
// did deferred move, remember new coordinates of the window as they're
// different from what Windows would return for it
return true;
}
// otherwise (or if deferring failed) move the window in place immediately
#endif // wxUSE_DEFERRED_SIZING
// toplevel window's coordinates are mirrored if the TLW is a child of another // toplevel window's coordinates are mirrored if the TLW is a child of another
// RTL window and changing width without moving the position would enlarge the // RTL window and changing width without moving the position would enlarge the
// window in the wrong direction, so we need to adjust for it // window in the wrong direction, so we need to adjust for it
@@ -1986,16 +1954,70 @@ wxWindowMSW::DoMoveSibling(WXHWND hwnd, int x, int y, int width, int height)
} }
} }
if ( !::MoveWindow((HWND)hwnd, x, y, width, height, IsShown()) ) #if wxUSE_DEFERRED_SIZING
else if ( MSWIsPositionDirectlySupported(x, y) )
{ {
wxLogLastError(wxT("MoveWindow")); // if our parent had prepared a defer window handle for us, use it
wxWindowMSW * const parent = GetParent();
HDWP hdwp = parent ? (HDWP)parent->m_hDWP : NULL;
if ( hdwp )
{
hdwp = ::DeferWindowPos(hdwp, (HWND)hwnd, NULL, x, y, width, height,
SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
if ( !hdwp )
{
wxLogLastError(wxT("DeferWindowPos"));
}
}
if ( parent )
{
// hdwp must be updated as it may have been changed
parent->m_hDWP = (WXHANDLE)hdwp;
}
if ( hdwp )
{
// did deferred move, remember new coordinates of the window as they're
// different from what Windows would return for it
return true;
}
// otherwise (or if deferring failed) move the window in place immediately
} }
#endif // wxUSE_DEFERRED_SIZING
MSWMoveWindowToAnyPosition(hwnd, x, y, width, height, IsShown());
// if wxUSE_DEFERRED_SIZING, indicates that we didn't use deferred move, // if wxUSE_DEFERRED_SIZING, indicates that we didn't use deferred move,
// ignored otherwise // ignored otherwise
return false; return false;
} }
void wxWindowMSW::MSWMoveWindowToAnyPosition(WXHWND hwnd, int x, int y, int width, int height, bool bRepaint)
{
bool scroll = GetParent() && !MSWIsPositionDirectlySupported(x, y);
if ( scroll )
{
// scroll to the actual position (looks like there is no need to Freeze() the parent)
::ScrollWindow(GetHwndOf(GetParent()), -x, -y, NULL, NULL);
}
// move to relative coordinates
if ( !::MoveWindow(hwnd, (scroll ? 0 : x), (scroll ? 0 : y), width, height, bRepaint) )
{
wxLogLastError(wxT("MoveWindow"));
}
if ( scroll )
{
// scroll back
::ScrollWindow(GetHwndOf(GetParent()), x, y, NULL, NULL);
}
}
void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height)
{ {
// TODO: is this consistent with other platforms? // TODO: is this consistent with other platforms?
@@ -2168,15 +2190,9 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
// and not defer it here as otherwise the value returned by // and not defer it here as otherwise the value returned by
// GetClient/WindowRect() wouldn't change as the window wouldn't be // GetClient/WindowRect() wouldn't change as the window wouldn't be
// really resized // really resized
if ( !::MoveWindow(GetHwnd(), MSWMoveWindowToAnyPosition(GetHwnd(), rectWin.left, rectWin.top,
rectWin.left, width + widthWin - rectClient.right,
rectWin.top, height + heightWin - rectClient.bottom, true);
width + widthWin - rectClient.right,
height + heightWin - rectClient.bottom,
TRUE) )
{
wxLogLastError(wxT("MoveWindow"));
}
} }
} }
@@ -3897,23 +3913,19 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
// do create the window // do create the window
wxWindowCreationHook hook(this); wxWindowCreationHook hook(this);
m_hWnd = (WXHWND)::CreateWindowEx m_hWnd = MSWCreateWindowAtAnyPosition
( (
extendedStyle, extendedStyle,
wclass, wclass,
title ? title : m_windowName.t_str(), title ? title : m_windowName.t_str(),
style, style,
x, y, w, h, x, y, w, h,
(HWND)MSWGetParent(), MSWGetParent(),
(HMENU)wxUIntToPtr(controlId), controlId
wxGetInstance(), );
NULL // no extra data
);
if ( !m_hWnd ) if ( !m_hWnd )
{ {
wxLogSysError(_("Can't create window of class %s"), wclass);
return false; return false;
} }
@@ -3922,6 +3934,32 @@ bool wxWindowMSW::MSWCreate(const wxChar *wclass,
return true; return true;
} }
WXHWND wxWindowMSW::MSWCreateWindowAtAnyPosition(WXDWORD exStyle, const wxChar* clName,
const wxChar* title, WXDWORD style,
int x, int y, int width, int height,
WXHWND parent, wxWindowID id)
{
WXHWND hWnd = ::CreateWindowEx(exStyle, clName, title, style, x, y, width, height,
parent, (HMENU)wxUIntToPtr(id), wxGetInstance(),
NULL); // no extra data
if ( !hWnd )
{
wxLogLastError(wxString::Format
(
wxT("CreateWindowEx(\"%s\", flags=%08lx, ex=%08lx)"),
clName, style, exStyle
));
}
else if ( !IsTopLevel() && !MSWIsPositionDirectlySupported(x, y) )
{
// fix position if limited by Short range
MSWMoveWindowToAnyPosition(hWnd, x, y, width, height, IsShown());
}
return hWnd;
}
// =========================================================================== // ===========================================================================
// MSW message handlers // MSW message handlers
// =========================================================================== // ===========================================================================

View File

@@ -16,6 +16,7 @@
#include "wx/app.h" #include "wx/app.h"
#include "wx/window.h" #include "wx/window.h"
#include "wx/button.h" #include "wx/button.h"
#include "wx/sizer.h"
#endif // WX_PRECOMP #endif // WX_PRECOMP
#include "asserthelper.h" #include "asserthelper.h"
@@ -49,6 +50,7 @@ private:
CPPUNIT_TEST( Children ); CPPUNIT_TEST( Children );
CPPUNIT_TEST( Focus ); CPPUNIT_TEST( Focus );
CPPUNIT_TEST( Positioning ); CPPUNIT_TEST( Positioning );
CPPUNIT_TEST( PositioningBeyondShortLimit );
CPPUNIT_TEST( Show ); CPPUNIT_TEST( Show );
CPPUNIT_TEST( Enable ); CPPUNIT_TEST( Enable );
CPPUNIT_TEST( FindWindowBy ); CPPUNIT_TEST( FindWindowBy );
@@ -68,6 +70,7 @@ private:
void Children(); void Children();
void Focus(); void Focus();
void Positioning(); void Positioning();
void PositioningBeyondShortLimit();
void Show(); void Show();
void Enable(); void Enable();
void FindWindowBy(); void FindWindowBy();
@@ -327,6 +330,47 @@ void WindowTestCase::Positioning()
m_window->GetScreenRect().GetTopLeft()); m_window->GetScreenRect().GetTopLeft());
} }
void WindowTestCase::PositioningBeyondShortLimit()
{
#ifdef __WXMSW__
//Positioning under MSW is limited to short relative coordinates
//
//Test window creation beyond SHRT_MAX
int commonDim = 10;
wxWindow* w = new wxWindow(m_window, wxID_ANY,
wxPoint(0, SHRT_MAX + commonDim),
wxSize(commonDim, commonDim));
CPPUNIT_ASSERT_EQUAL(SHRT_MAX + commonDim, w->GetPosition().y);
w->Move(0, 0);
//
//Test window moving beyond SHRT_MAX
w->Move(0, SHRT_MAX + commonDim);
CPPUNIT_ASSERT_EQUAL(SHRT_MAX + commonDim, w->GetPosition().y);
//
//Test window moving below SHRT_MIN
w->Move(0, SHRT_MIN - commonDim);
CPPUNIT_ASSERT_EQUAL(SHRT_MIN - commonDim, w->GetPosition().y);
//
//Test deferred move beyond SHRT_MAX
m_window->SetVirtualSize(-1, SHRT_MAX + 2 * commonDim);
wxWindow* bigWin = new wxWindow(m_window, wxID_ANY, wxDefaultPosition,
//size is also limited by SHRT_MAX
wxSize(commonDim, SHRT_MAX));
wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(bigWin);
sizer->AddSpacer(commonDim); //add some space to go beyond SHRT_MAX
sizer->Add(w);
m_window->SetSizer(sizer);
m_window->Layout();
CPPUNIT_ASSERT_EQUAL(SHRT_MAX + commonDim, w->GetPosition().y);
#endif
}
void WindowTestCase::Show() void WindowTestCase::Show()
{ {
CPPUNIT_ASSERT(m_window->IsShown()); CPPUNIT_ASSERT(m_window->IsShown());