From 1a986642f587a0ef9c77b7644cc1df7573bce75d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 31 May 2013 23:21:03 +0000 Subject: [PATCH] Add wxWindow::BeginRepositioningChildren() and EndRepositioningChildren(). This is just a refactoring of wxMSW code to make it possible to use deferred window positioning from other places in subsequent commits. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74066 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/msw/window.h | 3 ++ include/wx/window.h | 37 ++++++++++++++++ interface/wx/window.h | 64 ++++++++++++++++++++++++++++ src/msw/window.cpp | 93 ++++++++++++++++++++++------------------- 5 files changed, 155 insertions(+), 43 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 4eba8dbd67..b31383bd79 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -666,6 +666,7 @@ All (GUI): - Allow associating a validator with wxGridCellTextEditor (derEine). - Add more convenient wxFont(wxFontInfo) ctor. - Pass menu events to the handler in the associated wxMenuBar. +- Add wxWindow::BeginRepositioningChildren() and EndRepositioningChildren(). wxGTK: diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 9126006eda..02e2fb5ce7 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -67,6 +67,9 @@ public: virtual void Raise(); virtual void Lower(); + virtual bool BeginRepositioningChildren(); + virtual void EndRepositioningChildren(); + virtual bool Show(bool show = true); virtual bool ShowWithEffect(wxShowEffect effect, unsigned timeout = 0) diff --git a/include/wx/window.h b/include/wx/window.h index 22f7a5b0ee..a5cc6c4fa5 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -555,6 +555,43 @@ public: // this is the same as SendSizeEventToParent() but using PostSizeEvent() void PostSizeEventToParent() { SendSizeEventToParent(wxSEND_EVENT_POST); } + // These functions should be used before repositioning the children of + // this window to reduce flicker or, in MSW case, even avoid display + // corruption in some situations (so they're more than just optimization). + // + // EndRepositioningChildren() should be called if and only if + // BeginRepositioningChildren() returns true. To ensure that this is always + // done automatically, use ChildrenRepositioningGuard class below. + virtual bool BeginRepositioningChildren() { return false; } + virtual void EndRepositioningChildren() { } + + // A simple helper which ensures that EndRepositioningChildren() is called + // from its dtor if and only if calling BeginRepositioningChildren() from + // the ctor returned true. + class ChildrenRepositioningGuard + { + public: + // Notice that window can be NULL here, for convenience. In this case + // this class simply doesn't do anything. + wxEXPLICIT ChildrenRepositioningGuard(wxWindowBase* win) + : m_win(win), + m_callEnd(win && win->BeginRepositioningChildren()) + { + } + + ~ChildrenRepositioningGuard() + { + if ( m_callEnd ) + m_win->EndRepositioningChildren(); + } + + private: + wxWindowBase* const m_win; + const bool m_callEnd; + + wxDECLARE_NO_COPY_CLASS(ChildrenRepositioningGuard); + }; + // window state // ------------ diff --git a/interface/wx/window.h b/interface/wx/window.h index 12d91d53c9..dd36e63c25 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -766,6 +766,70 @@ public: */ //@{ + /** + Helper for ensuring EndRepositioningChildren() is called correctly. + + This class wraps the calls to BeginRepositioningChildren() and + EndRepositioningChildren() by performing the former in its constructor + and the latter in its destructor if, and only if, the first call + returned @true. This is the simplest way to call these methods and if + this class is created as a local variable, it also ensures that + EndRepositioningChildren() is correctly called (or not) on scope exit, + so its use instead of calling these methods manually is highly + recommended. + + @since 2.9.5 + */ + class ChildrenRepositioningGuard + { + public: + /** + Constructor calls wxWindow::BeginRepositioningChildren(). + + @param win The window to call BeginRepositioningChildren() on. If + it is @NULL, nothing is done. + */ + explicit ChildrenRepositioningGuard(wxWindow* win); + + /** + Destructor calls wxWindow::EndRepositioningChildren() if necessary. + + EndRepositioningChildren() is called only if a valid window was + passed to the constructor and if BeginRepositioningChildren() + returned @true. + */ + ~ChildrenRepositioningGuard(); + }; + + /** + Prepare for changing positions of multiple child windows. + + This method should be called before changing positions of multiple + child windows to reduce flicker and, in MSW case, even avoid display + corruption in some cases. It is used internally by wxWidgets and called + automatically when the window size changes but it can also be useful to + call it from outside of the library if a repositioning involving + multiple children is done without changing the window size. + + If this method returns @true, then EndRepositioningChildren() must be + called after setting all children positions. Use + ChildrenRepositioningGuard class to ensure that this requirement is + satisfied. + + @since 2.9.5 + */ + bool BeginRepositioningChildren(); + + /** + Fix child window positions after setting all of them at once. + + This method must be called if and only if the previous call to + BeginRepositioningChildren() returned @true. + + @since 2.9.5 + */ + void EndRepositioningChildren(); + /** Sets the cached best size value. diff --git a/src/msw/window.cpp b/src/msw/window.cpp index c614dca82d..6cef9564b7 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -5109,11 +5109,9 @@ bool wxWindowMSW::HandleExitSizeMove() return HandleWindowEvent(event); } -bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) +bool wxWindowMSW::BeginRepositioningChildren() { #if wxUSE_DEFERRED_SIZING - // when we resize this window, its children are probably going to be - // repositioned as well, prepare to use DeferWindowPos() for them int numChildren = 0; for ( HWND child = ::GetWindow(GetHwndOf(this), GW_CHILD); child; @@ -5122,23 +5120,60 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) numChildren ++; } + // Nothing is gained by deferring the repositioning of a single child. + if ( numChildren < 2 ) + return false; + // Protect against valid m_hDWP being overwritten - bool useDefer = false; + if ( m_hDWP ) + return false; - if ( numChildren > 1 ) + m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren); + if ( !m_hDWP ) { - if (!m_hDWP) - { - m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren); - if ( !m_hDWP ) - { - wxLogLastError(wxT("BeginDeferWindowPos")); - } - if (m_hDWP) - useDefer = true; - } + wxLogLastError(wxT("BeginDeferWindowPos")); + return false; } + + // Return true to indicate that EndDeferWindowPos() should be called. + return true; #endif // wxUSE_DEFERRED_SIZING +} + +void wxWindowMSW::EndRepositioningChildren() +{ +#if wxUSE_DEFERRED_SIZING + wxASSERT_MSG( m_hDWP, wxS("Shouldn't be called") ); + + // reset m_hDWP to NULL so that child windows don't try to use our + // m_hDWP after we call EndDeferWindowPos() on it (this shouldn't + // happen anyhow normally but who knows what weird flow of control we + // may have depending on what the users EVT_SIZE handler does...) + HDWP hDWP = (HDWP)m_hDWP; + m_hDWP = NULL; + + // do put all child controls in place at once + if ( !::EndDeferWindowPos(hDWP) ) + { + wxLogLastError(wxT("EndDeferWindowPos")); + } + + // Reset our children's pending pos/size values. + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindowMSW * const child = node->GetData(); + child->MSWEndDeferWindowPos(); + } +#endif // wxUSE_DEFERRED_SIZING +} + +bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) +{ + // when we resize this window, its children are probably going to be + // repositioned as well, prepare to use DeferWindowPos() for them + ChildrenRepositioningGuard repositionGuard(this); // update this window size bool processed = false; @@ -5171,34 +5206,6 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) processed = HandleWindowEvent(event); } -#if wxUSE_DEFERRED_SIZING - // and finally change the positions of all child windows at once - if ( useDefer && m_hDWP ) - { - // reset m_hDWP to NULL so that child windows don't try to use our - // m_hDWP after we call EndDeferWindowPos() on it (this shouldn't - // happen anyhow normally but who knows what weird flow of control we - // may have depending on what the users EVT_SIZE handler does...) - HDWP hDWP = (HDWP)m_hDWP; - m_hDWP = NULL; - - // do put all child controls in place at once - if ( !::EndDeferWindowPos(hDWP) ) - { - wxLogLastError(wxT("EndDeferWindowPos")); - } - - // Reset our children's pending pos/size values. - for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); - node; - node = node->GetNext() ) - { - wxWindowMSW * const child = node->GetData(); - child->MSWEndDeferWindowPos(); - } - } -#endif // wxUSE_DEFERRED_SIZING - return processed; } -- 2.45.2