Merge branch 'add-dump-window'

Add wxDumpWindow() function.

See https://github.com/wxWidgets/wxWidgets/pull/2558
This commit is contained in:
Vadim Zeitlin
2021-10-21 00:24:47 +01:00
7 changed files with 115 additions and 175 deletions

View File

@@ -2100,6 +2100,9 @@ extern WXDLLIMPEXP_CORE wxWindow *wxGetActiveWindow();
// get the (first) top level parent window // get the (first) top level parent window
WXDLLIMPEXP_CORE wxWindow* wxGetTopLevelParent(wxWindowBase *win); WXDLLIMPEXP_CORE wxWindow* wxGetTopLevelParent(wxWindowBase *win);
// Return a string with platform-dependent description of the window.
extern WXDLLIMPEXP_CORE wxString wxDumpWindow(wxWindowBase* win);
#if wxUSE_ACCESSIBILITY #if wxUSE_ACCESSIBILITY
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// accessible object for windows // accessible object for windows

View File

@@ -4251,5 +4251,22 @@ wxWindow* wxGetActiveWindow();
*/ */
wxWindow* wxGetTopLevelParent(wxWindow* window); wxWindow* wxGetTopLevelParent(wxWindow* window);
/**
Return a string with human-readable platform-specific description of
the window useful for diagnostic purposes.
The string returned from this function doesn't have any fixed form and can
vary between different wxWidgets ports and versions, but contains some
useful description of the window and uniquely identifies it. This can be
useful to include in debug or tracing messages.
@param window Window pointer which is allowed to be @NULL.
@header{wx/window.h}
@since 3.1.6
*/
wxString wxDumpWindow(wxWindow* window);
//@} //@}

View File

@@ -2672,9 +2672,7 @@ void wxWindowBase::SetConstraintSizes(bool recurse)
} }
else if ( constr ) else if ( constr )
{ {
wxLogDebug(wxT("Constraints not satisfied for %s named '%s'."), wxLogDebug(wxT("Constraints not satisfied for %s."), wxDumpWindow(this));
GetClassInfo()->GetClassName(),
GetName().c_str());
} }
if ( recurse ) if ( recurse )
@@ -3340,8 +3338,7 @@ void wxWindowBase::ReleaseMouse()
( (
wxString::Format wxString::Format
( (
"Releasing mouse in %p(%s) but it is not captured", "Releasing mouse in %s but it is not captured", wxDumpWindow(this)
this, GetClassInfo()->GetClassName()
) )
); );
} }
@@ -3351,9 +3348,8 @@ void wxWindowBase::ReleaseMouse()
( (
wxString::Format wxString::Format
( (
"Releasing mouse in %p(%s) but it is captured by %p(%s)", "Releasing mouse in %s but it is captured by %s",
this, GetClassInfo()->GetClassName(), wxDumpWindow(this), wxDumpWindow(winCapture)
winCapture, winCapture->GetClassInfo()->GetClassName()
) )
); );
} }
@@ -3661,6 +3657,31 @@ wxWindow* wxGetTopLevelParent(wxWindowBase *win_)
return win; return win;
} }
wxString wxDumpWindow(wxWindowBase* win)
{
if ( !win )
return wxString::FromAscii("[no window]");
wxString s = wxString::Format("%s@%p (",
win->GetClassInfo()->GetClassName(), win);
wxString label = win->GetLabel();
if ( label.empty() )
label = win->GetName();
s += wxString::Format("\"%s\"", label);
// Under MSW the HWND can be useful to find the window in Spy++ or similar
// program, so include it in the dump as well.
#ifdef __WXMSW__
s += wxString::Format(", HWND=%p", win->GetHandle());
#endif // __WXMSW__
s += ")";
return s;
}
#if wxUSE_ACCESSIBILITY #if wxUSE_ACCESSIBILITY
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// accessible object for windows // accessible object for windows

View File

@@ -309,26 +309,6 @@ static wxPoint gs_lastGesturePoint;
// the trace mask used for the focus debugging messages // the trace mask used for the focus debugging messages
#define TRACE_FOCUS wxT("focus") #define TRACE_FOCUS wxT("focus")
#if wxUSE_LOG_TRACE
// Function used to dump a brief description of a window.
static
wxString wxDumpWindow(wxWindowGTK* win)
{
if ( !win )
return "(no window)";
wxString s = wxString::Format("%s(%p",
win->GetClassInfo()->GetClassName(), win);
wxString label = win->GetLabel();
if ( !label.empty() )
s += wxString::Format(", \"%s\"", label);
s += ")";
return s;
}
#endif // wxUSE_LOG_TRACE
// A handy function to run from under gdb to show information about the given // A handy function to run from under gdb to show information about the given
// GtkWidget. Right now it only shows its type, we could enhance it to show // GtkWidget. Right now it only shows its type, we could enhance it to show
// more information later but this is already pretty useful. // more information later but this is already pretty useful.

View File

@@ -51,9 +51,6 @@
// debugging helpers // debugging helpers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// This one is defined in window_osx.cpp.
extern wxString wxDumpWindow(wxWindowMac* win);
// These functions are called from the code but are also useful in the debugger // These functions are called from the code but are also useful in the debugger
// (especially wxDumpNSView(), as selectors can be printed out directly anyhow), // (especially wxDumpNSView(), as selectors can be printed out directly anyhow),
// so make them just static instead of putting them in an anonymous namespace // so make them just static instead of putting them in an anonymous namespace

View File

@@ -181,28 +181,6 @@ wxIMPLEMENT_DYNAMIC_CLASS(wxBlindPlateWindow, wxWindow);
wxBEGIN_EVENT_TABLE(wxBlindPlateWindow, wxWindow) wxBEGIN_EVENT_TABLE(wxBlindPlateWindow, wxWindow)
wxEND_EVENT_TABLE() wxEND_EVENT_TABLE()
// ----------------------------------------------------------------------------
// debug helpers
// ----------------------------------------------------------------------------
// Function used to dump a brief description of a window.
extern
wxString wxDumpWindow(wxWindowMac* win)
{
if ( !win )
return "(no window)";
wxString s = wxString::Format("%s(%p",
win->GetClassInfo()->GetClassName(), win);
wxString label = win->GetLabel();
if ( !label.empty() )
s += wxString::Format(", \"%s\"", label);
s += ")";
return s;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// constructors and such // constructors and such
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -25,107 +25,79 @@
#include "wx/clipbrd.h" #include "wx/clipbrd.h"
#include "wx/dataobj.h" #include "wx/dataobj.h"
#include "wx/panel.h" #include "wx/panel.h"
#include "wx/scopedptr.h"
#include "asserthelper.h" #include "asserthelper.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// test class // the tests
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class MiscGUIFuncsTestCase : public CppUnit::TestCase TEST_CASE("GUI::DisplaySize", "[guifuncs]")
{
public:
MiscGUIFuncsTestCase() { }
private:
CPPUNIT_TEST_SUITE( MiscGUIFuncsTestCase );
CPPUNIT_TEST( DisplaySize );
CPPUNIT_TEST( URLDataObject );
CPPUNIT_TEST( ParseFileDialogFilter );
CPPUNIT_TEST( ClientToScreen );
CPPUNIT_TEST( FindWindowAtPoint );
CPPUNIT_TEST_SUITE_END();
void DisplaySize();
void URLDataObject();
void ParseFileDialogFilter();
void ClientToScreen();
void FindWindowAtPoint();
wxDECLARE_NO_COPY_CLASS(MiscGUIFuncsTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( MiscGUIFuncsTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MiscGUIFuncsTestCase, "MiscGUIFuncsTestCase" );
void MiscGUIFuncsTestCase::DisplaySize()
{ {
// test that different (almost) overloads return the same results // test that different (almost) overloads return the same results
int w, h; int w, h;
wxDisplaySize(&w, &h); wxDisplaySize(&w, &h);
wxSize sz = wxGetDisplaySize(); wxSize sz = wxGetDisplaySize();
CPPUNIT_ASSERT_EQUAL( w, sz.x ); CHECK( sz.x == w );
CPPUNIT_ASSERT_EQUAL( h, sz.y ); CHECK( sz.y == h );
// test that passing NULL works as expected, e.g. doesn't crash // test that passing NULL works as expected, e.g. doesn't crash
wxDisplaySize(NULL, NULL); wxDisplaySize(NULL, NULL);
wxDisplaySize(&w, NULL); wxDisplaySize(&w, NULL);
wxDisplaySize(NULL, &h); wxDisplaySize(NULL, &h);
CPPUNIT_ASSERT_EQUAL( w, sz.x ); CHECK( sz.x == w );
CPPUNIT_ASSERT_EQUAL( h, sz.y ); CHECK( sz.y == h );
// test that display PPI is something reasonable // test that display PPI is something reasonable
sz = wxGetDisplayPPI(); sz = wxGetDisplayPPI();
CPPUNIT_ASSERT( sz.x < 1000 ); CHECK( sz.x < 1000 );
CPPUNIT_ASSERT( sz.y < 1000 ); CHECK( sz.y < 1000 );
} }
void MiscGUIFuncsTestCase::URLDataObject()
{
#if wxUSE_DATAOBJ #if wxUSE_DATAOBJ
TEST_CASE("GUI::URLDataObject", "[guifuncs]")
{
// this tests for buffer overflow, see #11102 // this tests for buffer overflow, see #11102
const char * const const char * const
url = "http://something.long.to.overwrite.plenty.memory.example.com"; url = "http://something.long.to.overwrite.plenty.memory.example.com";
wxURLDataObject * const dobj = new wxURLDataObject(url); wxURLDataObject * const dobj = new wxURLDataObject(url);
CPPUNIT_ASSERT_EQUAL( url, dobj->GetURL() ); CHECK( dobj->GetURL() == url );
wxClipboardLocker lockClip; wxClipboardLocker lockClip;
CPPUNIT_ASSERT( wxTheClipboard->SetData(dobj) ); CHECK( wxTheClipboard->SetData(dobj) );
wxTheClipboard->Flush(); wxTheClipboard->Flush();
#endif // wxUSE_DATAOBJ
} }
#endif // wxUSE_DATAOBJ
void MiscGUIFuncsTestCase::ParseFileDialogFilter() TEST_CASE("GUI::ParseFileDialogFilter", "[guifuncs]")
{ {
wxArrayString descs, wxArrayString descs,
filters; filters;
CPPUNIT_ASSERT_EQUAL REQUIRE
( (
1,
wxParseCommonDialogsFilter("Image files|*.jpg;*.png", descs, filters) wxParseCommonDialogsFilter("Image files|*.jpg;*.png", descs, filters)
== 1
); );
CPPUNIT_ASSERT_EQUAL( "Image files", descs[0] ); CHECK( descs[0] == "Image files" );
CPPUNIT_ASSERT_EQUAL( "*.jpg;*.png", filters[0] ); CHECK( filters[0] == "*.jpg;*.png" );
CPPUNIT_ASSERT_EQUAL REQUIRE
( (
2,
wxParseCommonDialogsFilter wxParseCommonDialogsFilter
( (
"All files (*.*)|*.*|Python source (*.py)|*.py", "All files (*.*)|*.*|Python source (*.py)|*.py",
descs, filters descs, filters
) )
== 2
); );
CPPUNIT_ASSERT_EQUAL( "*.*", filters[0] ); CHECK( filters[0] == "*.*" );
CPPUNIT_ASSERT_EQUAL( "*.py", filters[1] ); CHECK( filters[1] == "*.py" );
// Test some invalid ones too. // Test some invalid ones too.
WX_ASSERT_FAILS_WITH_ASSERT WX_ASSERT_FAILS_WITH_ASSERT
@@ -138,37 +110,26 @@ void MiscGUIFuncsTestCase::ParseFileDialogFilter()
); );
} }
void MiscGUIFuncsTestCase::ClientToScreen() TEST_CASE("GUI::ClientToScreen", "[guifuncs]")
{ {
wxWindow* const tlw = wxTheApp->GetTopWindow(); wxWindow* const tlw = wxTheApp->GetTopWindow();
CPPUNIT_ASSERT( tlw ); REQUIRE( tlw );
wxPanel* const wxScopedPtr<wxPanel> const
p1 = new wxPanel(tlw, wxID_ANY, wxPoint(0, 0), wxSize(100, 50)); p1(new wxPanel(tlw, wxID_ANY, wxPoint(0, 0), wxSize(100, 50)));
wxPanel* const wxScopedPtr<wxPanel> const
p2 = new wxPanel(tlw, wxID_ANY, wxPoint(0, 50), wxSize(100, 50)); p2(new wxPanel(tlw, wxID_ANY, wxPoint(0, 50), wxSize(100, 50)));
wxWindow* const wxWindow* const
b = new wxWindow(p2, wxID_ANY, wxPoint(10, 10), wxSize(30, 10)); b = new wxWindow(p2.get(), wxID_ANY, wxPoint(10, 10), wxSize(30, 10));
// We need this to realize the windows created above under wxGTK. // We need this to realize the windows created above under wxGTK.
wxYield(); wxYield();
const wxPoint tlwOrig = tlw->ClientToScreen(wxPoint(0, 0)); const wxPoint tlwOrig = tlw->ClientToScreen(wxPoint(0, 0));
CPPUNIT_ASSERT_EQUAL CHECK( p2->ClientToScreen(wxPoint(0, 0)) == tlwOrig + wxPoint(0, 50) );
(
tlwOrig + wxPoint(0, 50),
p2->ClientToScreen(wxPoint(0, 0))
);
CPPUNIT_ASSERT_EQUAL CHECK( b->ClientToScreen(wxPoint(0, 0)) == tlwOrig + wxPoint(10, 60) );
(
tlwOrig + wxPoint(10, 60),
b->ClientToScreen(wxPoint(0, 0))
);
p1->Destroy();
p2->Destroy();
} }
namespace namespace
@@ -197,76 +158,59 @@ wxString GetLabelOfWindowAtPoint(wxWindow* parent, int x, int y)
} // anonymous namespace } // anonymous namespace
void MiscGUIFuncsTestCase::FindWindowAtPoint() TEST_CASE("GUI::FindWindowAtPoint", "[guifuncs]")
{ {
wxWindow* const parent = wxTheApp->GetTopWindow(); wxWindow* const parent = wxTheApp->GetTopWindow();
CPPUNIT_ASSERT( parent ); REQUIRE( parent );
// Set a label to allow distinguishing it from the other windows in the // Set a label to allow distinguishing it from the other windows in the
// assertion messages. // assertion messages.
parent->SetLabel("parent"); parent->SetLabel("parent");
wxWindow* btn1 = new TestButton(parent, "1", wxPoint(10, 10)); wxScopedPtr<wxWindow> btn1(new TestButton(parent, "1", wxPoint(10, 10)));
wxWindow* btn2 = new TestButton(parent, "2", wxPoint(10, 90)); wxScopedPtr<wxWindow> btn2(new TestButton(parent, "2", wxPoint(10, 90)));
wxWindow* btn3 = new TestButton(btn2, "3", wxPoint(20, 20));
// No need to use wxScopedPtr<> for this one, it will be deleted by btn2.
wxWindow* btn3 = new TestButton(btn2.get(), "3", wxPoint(20, 20));
// We need this to realize the windows created above under wxGTK. // We need this to realize the windows created above under wxGTK.
wxYield(); wxYield();
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("No window for a point outside of the window");
( CHECK( GetLabelOfWindowAtPoint(parent, 900, 900) == "NONE" );
"No window for a point outside of the window",
"NONE",
GetLabelOfWindowAtPoint(parent, 900, 900)
);
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO( "Point over a child control corresponds to it" );
( CHECK( GetLabelOfWindowAtPoint(parent, 11, 11) == btn1->GetLabel() );
"Point over a child control corresponds to it",
btn1->GetLabel(),
GetLabelOfWindowAtPoint(parent, 11, 11)
);
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("Point outside of any child control returns the TLW itself");
( CHECK( GetLabelOfWindowAtPoint(parent, 5, 5) == parent->GetLabel() );
"Point outside of any child control returns the TLW itself",
parent->GetLabel(),
GetLabelOfWindowAtPoint(parent, 5, 5)
);
btn2->Disable(); btn2->Disable();
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("Point over a disabled child control still corresponds to it");
( CHECK( GetLabelOfWindowAtPoint(parent, 11, 91) == btn2->GetLabel() );
"Point over a disabled child control still corresponds to it",
btn2->GetLabel(),
GetLabelOfWindowAtPoint(parent, 11, 91)
);
btn2->Hide(); btn2->Hide();
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("Point over a hidden child control doesn't take it into account");
( CHECK( GetLabelOfWindowAtPoint(parent, 11, 91) == parent->GetLabel() );
"Point over a hidden child control doesn't take it into account",
parent->GetLabel(),
GetLabelOfWindowAtPoint(parent, 11, 91)
);
btn2->Show(); btn2->Show();
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("Point over child control corresponds to the child");
( CHECK( GetLabelOfWindowAtPoint(parent, 31, 111) == btn3->GetLabel() );
"Point over child control corresponds to the child",
btn3->GetLabel(),
GetLabelOfWindowAtPoint(parent, 31, 111)
);
btn3->Disable(); btn3->Disable();
CPPUNIT_ASSERT_EQUAL_MESSAGE INFO("Point over disabled child controls still corresponds to this child");
( CHECK( GetLabelOfWindowAtPoint(parent, 31, 111) == btn3->GetLabel() );
"Point over disabled child controls still corresponds to this child", }
btn3->GetLabel(),
GetLabelOfWindowAtPoint(parent, 31, 111) TEST_CASE("wxWindow::Dump", "[window]")
); {
CHECK_NOTHROW( wxDumpWindow(NULL) );
btn1->Destroy();
btn2->Destroy(); wxScopedPtr<wxButton>
// btn3 was already deleted when its parent was button(new wxButton(wxTheApp->GetTopWindow(), wxID_ANY, "bloordyblop"));
const std::string s = wxDumpWindow(button.get()).utf8_string();
CHECK_THAT( s, Catch::Contains("wxButton") );
CHECK_THAT( s, Catch::Contains("bloordyblop") );
} }