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
This commit is contained in:
Vadim Zeitlin
2013-05-31 23:21:03 +00:00
parent 4300317631
commit 1a986642f5
5 changed files with 156 additions and 44 deletions

View File

@@ -666,6 +666,7 @@ All (GUI):
- Allow associating a validator with wxGridCellTextEditor (derEine). - Allow associating a validator with wxGridCellTextEditor (derEine).
- Add more convenient wxFont(wxFontInfo) ctor. - Add more convenient wxFont(wxFontInfo) ctor.
- Pass menu events to the handler in the associated wxMenuBar. - Pass menu events to the handler in the associated wxMenuBar.
- Add wxWindow::BeginRepositioningChildren() and EndRepositioningChildren().
wxGTK: wxGTK:

View File

@@ -67,6 +67,9 @@ public:
virtual void Raise(); virtual void Raise();
virtual void Lower(); virtual void Lower();
virtual bool BeginRepositioningChildren();
virtual void EndRepositioningChildren();
virtual bool Show(bool show = true); virtual bool Show(bool show = true);
virtual bool ShowWithEffect(wxShowEffect effect, virtual bool ShowWithEffect(wxShowEffect effect,
unsigned timeout = 0) unsigned timeout = 0)

View File

@@ -555,6 +555,43 @@ public:
// this is the same as SendSizeEventToParent() but using PostSizeEvent() // this is the same as SendSizeEventToParent() but using PostSizeEvent()
void PostSizeEventToParent() { SendSizeEventToParent(wxSEND_EVENT_POST); } 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 // window state
// ------------ // ------------

View File

@@ -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. Sets the cached best size value.

View File

@@ -5109,11 +5109,9 @@ bool wxWindowMSW::HandleExitSizeMove()
return HandleWindowEvent(event); return HandleWindowEvent(event);
} }
bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam) bool wxWindowMSW::BeginRepositioningChildren()
{ {
#if wxUSE_DEFERRED_SIZING #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; int numChildren = 0;
for ( HWND child = ::GetWindow(GetHwndOf(this), GW_CHILD); for ( HWND child = ::GetWindow(GetHwndOf(this), GW_CHILD);
child; child;
@@ -5122,23 +5120,60 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam)
numChildren ++; numChildren ++;
} }
// Protect against valid m_hDWP being overwritten // Nothing is gained by deferring the repositioning of a single child.
bool useDefer = false; if ( numChildren < 2 )
return false;
if ( numChildren > 1 ) // Protect against valid m_hDWP being overwritten
if ( m_hDWP )
return false;
m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren);
if ( !m_hDWP )
{ {
if (!m_hDWP) wxLogLastError(wxT("BeginDeferWindowPos"));
{ return false;
m_hDWP = (WXHANDLE)::BeginDeferWindowPos(numChildren); }
if ( !m_hDWP )
{ // Return true to indicate that EndDeferWindowPos() should be called.
wxLogLastError(wxT("BeginDeferWindowPos")); return true;
} #endif // wxUSE_DEFERRED_SIZING
if (m_hDWP) }
useDefer = true;
} 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 #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 // update this window size
bool processed = false; bool processed = false;
@@ -5171,34 +5206,6 @@ bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam)
processed = HandleWindowEvent(event); 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; return processed;
} }