Check that all windows in a sizer use associated window as parent

Using a wrong parent for the window managed by the sizer of the given
window or, alternatively, inserting a window into a wrong sizer, seems
to be a common mistake when creating sizer-based layouts and results in
hard to understand visual problems (as typically the window with the
mismatched parent/sizer just remains stuck at some wrong location).

So try to help detect it sooner and provide more information about the
problem by checking the parent of the window either when it is added to
the sizer -- if we already know which sizer the window is associated
with at that moment -- or when associating the sizer with the window
later.
This commit is contained in:
Vadim Zeitlin 2021-10-20 23:38:17 +01:00
parent a809b6f058
commit 62c3d921b2

View File

@ -187,6 +187,21 @@ wxString MakeFlagsCheckMessage(const char* start, const char* whatToRemove)
); );
} }
wxString MakeExpectedParentMessage(wxWindow* w, wxWindow* expectedParent)
{
return wxString::Format
(
"Windows managed by the sizer associated with the given "
"window must have this window as parent, otherwise they "
"will not be repositioned correctly.\n"
"\n"
"Please use the window %s with which this sizer is associated, "
"as the parent when creating the window %s managed by it.",
wxDumpWindow(expectedParent),
wxDumpWindow(w)
);
}
} // anonymous namespace } // anonymous namespace
#endif // wxDEBUG_LEVEL #endif // wxDEBUG_LEVEL
@ -225,6 +240,18 @@ wxString MakeFlagsCheckMessage(const char* start, const char* whatToRemove)
ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_HORIZONTAL, wxALIGN_RIGHT); \ ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_HORIZONTAL, wxALIGN_RIGHT); \
ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_VERTICAL, wxALIGN_BOTTOM) ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_VERTICAL, wxALIGN_BOTTOM)
// Verify that the given window has the expected parent.
//
// Both pointers must be non-null.
//
// Note that this is a serious error and that, unlike for benign sizer flag
// checks, it can't be disabled by setting some environment variable.
#define ASSERT_WINDOW_PARENT_IS(w, expectedParent) \
wxASSERT_MSG \
( \
w->GetParent() == expectedParent, \
MakeExpectedParentMessage(w, expectedParent) \
)
/* static */ /* static */
void wxSizerFlags::DisableConsistencyChecks() void wxSizerFlags::DisableConsistencyChecks()
@ -779,8 +806,18 @@ wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
ContainingSizerGuard guard( item ); ContainingSizerGuard guard( item );
if ( item->GetWindow() ) if ( wxWindow* const w = item->GetWindow() )
item->GetWindow()->SetContainingSizer( this ); {
w->SetContainingSizer( this );
// If possible, detect adding windows with a wrong parent to the sizer
// as early as possible, as this allows to see where exactly it happens
// (otherwise this will be checked when the containing window is set
// later, but by this time the stack trace at the moment of assertion
// won't point out the culprit any longer).
if ( m_containingWindow )
ASSERT_WINDOW_PARENT_IS(w, m_containingWindow);
}
if ( item->GetSizer() ) if ( item->GetSizer() )
item->GetSizer()->SetContainingWindow( m_containingWindow ); item->GetSizer()->SetContainingWindow( m_containingWindow );
@ -810,6 +847,9 @@ void wxSizer::SetContainingWindow(wxWindow *win)
{ {
sizer->SetContainingWindow(win); sizer->SetContainingWindow(win);
} }
if ( wxWindow* const w = item->GetWindow() )
ASSERT_WINDOW_PARENT_IS(w, m_containingWindow);
} }
} }