diff --git a/docs/changes.txt b/docs/changes.txt index a97a82e1a5..0ca9f953bb 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -11,7 +11,16 @@ All: wxMSW: -- Fixed erroneous selection of content in wxComboBox when within a wxStaticBox. +- Fixed erroneous selection of content in wxComboBox when within a wxStaticBox + (checking for selection caused by WM_STYLECHANGED). +- Worked around an apparent bug in Windows whereby some deferred positioning + failed: specifically when changing a position from x, to y, to x again. +- Added deferred positioning to wxRadioBox, wxSlider and wxSpinCtrl and thereby + eliminated some refresh glitches when resizing. +- Eliminated further refresh glitches caused by wxRadioBox (to nearby controls) + by refreshing parent when the radio box moves. +- Added ability set the system option "msw.staticbox.optimized-paint" to 0 to + allow a panel to paint graphics around controls within a static box. wxMac: diff --git a/docs/latex/wx/sysopt.tex b/docs/latex/wx/sysopt.tex index 91ffd8eba4..e865e2bb30 100644 --- a/docs/latex/wx/sysopt.tex +++ b/docs/latex/wx/sysopt.tex @@ -28,6 +28,9 @@ pages).} \twocolitem{msw.staticbitmap.htclient}{If set to 1, allows the static bitmap to respond to mouse events. The default is 0, since a value of 1 can interfere with refresh in static boxes. Note that once set, this option cannot be unset later in the application.} +\twocolitem{msw.staticbox.optimized-paint}{If set to 0, switches off optimized wxStaticBox painting. +Setting this to 0 causes more flicker, but allows applications to paint graphics on the parent of a static box +(the optimized refresh causes any such drawing to disappear).} \end{twocollist} \wxheading{Mac} diff --git a/include/wx/msw/private.h b/include/wx/msw/private.h index a2a08d8316..f974941a37 100644 --- a/include/wx/msw/private.h +++ b/include/wx/msw/private.h @@ -701,6 +701,9 @@ inline bool wxStyleHasBorder(long style) wxSUNKEN_BORDER | wxDOUBLE_BORDER)) != 0; } +// Deferred window moving +bool wxMoveWindowDeferred(HDWP& hdwp, wxWindow* win, HWND hWnd, int x, int y, int width, int height); + // ---------------------------------------------------------------------------- // functions mapping HWND to wxWindow // ---------------------------------------------------------------------------- diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 627f4378f7..377afca91b 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -47,6 +47,9 @@ enum class WXDLLEXPORT wxWindowMSW : public wxWindowBase { + friend class wxSpinCtrl; + friend class wxSlider; + friend class wxRadioBox; public: wxWindowMSW() { Init(); } @@ -541,5 +544,17 @@ WX_DECLARE_HASH(wxWindowMSW, wxWindowList, wxWinHashTable); extern wxWinHashTable *wxWinHandleHash; +// ---------------------------------------------------------------------------- +// extra data needed for correcting problems with deferred positioning +// ---------------------------------------------------------------------------- + +struct wxExtraWindowData +{ + // Stored during deferred positioning + wxPoint m_pos; + wxSize m_size; + bool m_deferring:1; +}; + #endif // _WX_WINDOW_H_ diff --git a/src/msw/radiobox.cpp b/src/msw/radiobox.cpp index c8e8ee0a6b..82c3471b21 100644 --- a/src/msw/radiobox.cpp +++ b/src/msw/radiobox.cpp @@ -40,6 +40,8 @@ #include "wx/msw/subwin.h" +#define USE_DEFERRED_SIZING 1 + #if wxUSE_TOOLTIPS #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__) #include @@ -545,7 +547,17 @@ void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags) height = heightOld; } - ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE); + // if our parent had prepared a defer window handle for us, use it (unless + // we are a top level window) + wxWindowMSW *parent = GetParent(); + +#if USE_DEFERRED_SIZING + HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL; +#else + HDWP hdwp = 0; +#endif + + wxMoveWindowDeferred(hdwp, this, GetHwnd(), xx, yy, width, height); // Now position all the buttons: the current button will be put at // wxPoint(x_offset, y_offset) and the new row/column will start at @@ -642,6 +654,22 @@ void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags) x_offset += widthBtn + cx1; } } + if (hdwp) + { + // Store the size so we can report it accurately + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (!extraData) + { + extraData = new wxExtraWindowData; + m_windowReserved = (void*) extraData; + } + extraData->m_pos = wxPoint(xx, yy); + extraData->m_size = wxSize(width, height); + extraData->m_deferring = true; + + // hdwp must be updated as it may have been changed + parent->m_hDWP = (WXHANDLE)hdwp; + } } // ---------------------------------------------------------------------------- @@ -687,6 +715,17 @@ wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) return 0; } + // FIXME: Without this, the radiobox corrupts other controls as it moves + // in a dynamic layout. Refreshing causes flicker, but it's better than + // leaving droppings. Note that for some reason, wxStaticBox doesn't need + // this (perhaps because it has no real children?) + else if (nMsg == WM_MOVE && IsKindOf(CLASSINFO(wxRadioBox))) + { + WXLRESULT res = wxControl::MSWWindowProc(nMsg, wParam, lParam); + wxRect rect = GetRect(); + GetParent()->Refresh(true, & rect); + return res; + } return wxStaticBox::MSWWindowProc(nMsg, wParam, lParam); } diff --git a/src/msw/slider95.cpp b/src/msw/slider95.cpp index 5db488c88c..7efe6d803d 100644 --- a/src/msw/slider95.cpp +++ b/src/msw/slider95.cpp @@ -42,6 +42,8 @@ #include #endif +#define USE_DEFERRED_SIZING 1 + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -441,6 +443,16 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height) return; } + // if our parent had prepared a defer window handle for us, use it (unless + // we are a top level window) + wxWindowMSW *parent = GetParent(); + +#if USE_DEFERRED_SIZING + HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL; +#else + HDWP hdwp = 0; +#endif + // be careful to position the slider itself after moving the labels as // otherwise our GetBoundingBox(), which is called from WM_SIZE handler, // would return a wrong result and wrong size would be cached internally @@ -453,22 +465,21 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height) // position all labels: min at the top, value in the middle and max at // the bottom - ::MoveWindow((*m_labels)[SliderLabel_Min], - xLabel, y, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Min], + xLabel, y, wLabel, hLabel); - ::MoveWindow((*m_labels)[SliderLabel_Value], - xLabel, y + (height - hLabel)/2, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Value], + xLabel, y + (height - hLabel)/2, wLabel, hLabel); - ::MoveWindow((*m_labels)[SliderLabel_Max], - xLabel, y + height - hLabel, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Max], + xLabel, y + height - hLabel, wLabel, hLabel); // position the slider itself along the left/right edge - ::MoveWindow(GetHwnd(), + wxMoveWindowDeferred(hdwp, this, GetHwnd(), HasFlag(wxSL_LEFT) ? x : x + wLabel + HGAP, y + hLabel/2, width - wLabel - HGAP, - height - hLabel, - TRUE); + height - hLabel); } else // horizontal { @@ -479,22 +490,37 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height) // position all labels: min on the left, value in the middle and max to // the right - ::MoveWindow((*m_labels)[SliderLabel_Min], - x, yLabel, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Min], + x, yLabel, wLabel, hLabel); - ::MoveWindow((*m_labels)[SliderLabel_Value], - x + (width - wLabel)/2, yLabel, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Value], + x + (width - wLabel)/2, yLabel, wLabel, hLabel); - ::MoveWindow((*m_labels)[SliderLabel_Max], - x + width - wLabel, yLabel, wLabel, hLabel, TRUE); + wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Max], + x + width - wLabel, yLabel, wLabel, hLabel); // position the slider itself along the top/bottom edge - ::MoveWindow(GetHwnd(), + wxMoveWindowDeferred(hdwp, this, GetHwnd(), x, HasFlag(wxSL_TOP) ? y : y + hLabel, width, - height - hLabel, - TRUE); + height - hLabel); + } + if ( hdwp ) + { + // Store the size so we can report it accurately + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (!extraData) + { + extraData = new wxExtraWindowData; + m_windowReserved = (void*) extraData; + } + extraData->m_pos = wxPoint(x, y); + extraData->m_size = wxSize(width, height); + extraData->m_deferring = true; + + // hdwp must be updated as it may have been changed + parent->m_hDWP = (WXHANDLE)hdwp; } } diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index 02319d7e7b..907fb9f882 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -45,6 +45,8 @@ #include // for INT_MIN +#define USE_DEFERRED_SIZING 1 + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -567,21 +569,55 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height) wxLogDebug(_T("not enough space for wxSpinCtrl!")); } - if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) ) - { - wxLogLastError(wxT("MoveWindow(buddy)")); - } + // if our parent had prepared a defer window handle for us, use it (unless + // we are a top level window) + wxWindowMSW *parent = GetParent(); + int originalX = x; +#if USE_DEFERRED_SIZING + HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL; +#else + HDWP hdwp = 0; +#endif + + // 1) The buddy window + wxMoveWindowDeferred(hdwp, this, GetBuddyHwnd(), + x, y, widthText, height); + + // 2) The button window x += widthText + MARGIN_BETWEEN; - if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) ) + wxMoveWindowDeferred(hdwp, this, GetHwnd(), + x, y, widthBtn, height); + + if (hdwp) { - wxLogLastError(wxT("MoveWindow")); + // Store the size so we can report it accurately + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (!extraData) + { + extraData = new wxExtraWindowData; + m_windowReserved = (void*) extraData; + } + extraData->m_pos = wxPoint(originalX, y); + extraData->m_size = wxSize(width, height); + extraData->m_deferring = true; + + // hdwp must be updated as it may have been changed + parent->m_hDWP = (WXHANDLE)hdwp; } } // get total size of the control void wxSpinCtrl::DoGetSize(int *x, int *y) const { + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP) + { + *x = extraData->m_size.x; + *y = extraData->m_size.y; + return; + } + RECT spinrect, textrect, ctrlrect; GetWindowRect(GetHwnd(), &spinrect); GetWindowRect(GetBuddyHwnd(), &textrect); @@ -595,6 +631,14 @@ void wxSpinCtrl::DoGetSize(int *x, int *y) const void wxSpinCtrl::DoGetPosition(int *x, int *y) const { + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP) + { + *x = extraData->m_pos.x; + *y = extraData->m_pos.y; + return; + } + // hack: pretend that our HWND is the text control just for a moment WXHWND hWnd = GetHWND(); wxConstCast(this, wxSpinCtrl)->m_hWnd = m_hwndBuddy; diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 6fc526fac7..b17495ed68 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -119,6 +119,8 @@ #define HAVE_TRACKMOUSEEVENT #endif // everything needed for TrackMouseEvent() +#define USE_DEFERRED_SIZING 1 + // --------------------------------------------------------------------------- // global variables // --------------------------------------------------------------------------- @@ -468,6 +470,12 @@ wxWindowMSW::~wxWindowMSW() { m_isBeingDeleted = true; + if (m_windowReserved) + { + delete (wxExtraWindowData*) m_windowReserved; + m_windowReserved = NULL; + } + #ifndef __WXUNIVERSAL__ // VS: make sure there's no wxFrame with last focus set to us: for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) @@ -1443,6 +1451,14 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip) // Get total size void wxWindowMSW::DoGetSize(int *x, int *y) const { + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP) + { + *x = extraData->m_size.x; + *y = extraData->m_size.y; + return; + } + RECT rect = wxGetWindowRect(GetHwnd()); if ( x ) @@ -1454,6 +1470,14 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const // Get size *available for subwindows* i.e. excluding menu bar etc. void wxWindowMSW::DoGetClientSize(int *x, int *y) const { + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP) + { + *x = extraData->m_pos.x; + *y = extraData->m_pos.y; + return; + } + RECT rect = wxGetClientRect(GetHwnd()); if ( x ) @@ -1546,29 +1570,31 @@ void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height) // if our parent had prepared a defer window handle for us, use it (unless // we are a top level window) wxWindowMSW *parent = GetParent(); + +#if USE_DEFERRED_SIZING HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL; +#else + HDWP hdwp = 0; +#endif + + wxMoveWindowDeferred(hdwp, this, GetHwnd(), x, y, width, height); + if ( hdwp ) { - hdwp = ::DeferWindowPos(hdwp, GetHwnd(), NULL, - x, y, width, height, - SWP_NOZORDER); - if ( !hdwp ) + // Store the size so we can report it accurately + wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved; + if (!extraData) { - wxLogLastError(_T("DeferWindowPos")); + extraData = new wxExtraWindowData; + m_windowReserved = (void*) extraData; } + extraData->m_pos = wxPoint(x, y); + extraData->m_size = wxSize(width, height); + extraData->m_deferring = true; // hdwp must be updated as it may have been changed parent->m_hDWP = (WXHANDLE)hdwp; } - - // otherwise (or if deferring failed) move the window in place immediately - if ( !hdwp ) - { - if ( !::MoveWindow(GetHwnd(), x, y, width, height, IsShown()) ) - { - wxLogLastError(wxT("MoveWindow")); - } - } } // set the size of the window: if the dimensions are positive, just use them, @@ -5931,6 +5957,31 @@ bool wxWindowMSW::HandleHotKey(WXWPARAM wParam, WXLPARAM lParam) #endif // wxUSE_HOTKEY +// Moves a window by deferred method or normal method +bool wxMoveWindowDeferred(HDWP& hdwp, wxWindow* win, HWND hWnd, int x, int y, int width, int height) +{ + if ( hdwp ) + { + hdwp = ::DeferWindowPos(hdwp, hWnd, NULL, + x, y, width, height, + SWP_NOZORDER); + if ( !hdwp ) + { + wxLogLastError(_T("DeferWindowPos")); + } + } + + // otherwise (or if deferring failed) move the window in place immediately + if ( !hdwp ) + { + if ( !::MoveWindow(hWnd, x, y, width, height, win->IsShown()) ) + { + wxLogLastError(wxT("MoveWindow")); + } + } + return hdwp != NULL; +} + // Not tested under WinCE #ifndef __WXWINCE__