diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 92f282994f..529523c471 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -936,7 +936,7 @@ struct WXDLLIMPEXP_CORE wxGridSizesInfo // wxGrid // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_CORE wxGrid : public wxScrolledWindow +class WXDLLIMPEXP_CORE wxGrid : public wxScrolledCanvas { public: // possible selection modes @@ -1937,9 +1937,8 @@ public: // override some base class functions - virtual wxWindow *GetMainWindowOfCompositeControl() wxOVERRIDE - { return (wxWindow*)m_gridWin; } virtual void Fit() wxOVERRIDE; + virtual void SetFocus() wxOVERRIDE; // implementation only void CancelMouseCapture(); @@ -2277,7 +2276,7 @@ protected: private: - // implement wxScrolledWindow method to return m_gridWin size + // implement wxScrolledCanvas method to return m_gridWin size virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size) wxOVERRIDE; // depending on the values of m_numFrozenRows and m_numFrozenCols, it will diff --git a/interface/wx/grid.h b/interface/wx/grid.h index e7a5513350..e1da2f0bcb 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -2866,8 +2866,14 @@ public: /** Enables or disables in-place editing of grid cell data. - The grid will issue either a @c wxEVT_GRID_EDITOR_SHOWN or - @c wxEVT_GRID_EDITOR_HIDDEN event. + Enabling in-place editing generates @c wxEVT_GRID_EDITOR_SHOWN and, if + it isn't vetoed by the application, shows the in-place editor which + allows the user to change the cell value. + + Disabling in-place editing does nothing if the in-place editor isn't + currently show, otherwise the @c wxEVT_GRID_EDITOR_HIDDEN event is + generated but, unlike the "shown" event, it can't be vetoed and the + in-place editor is dismissed unconditionally. */ void EnableCellEditControl(bool enable = true); @@ -3249,7 +3255,11 @@ public: void SetReadOnly(int row, int col, bool isReadOnly = true); /** - Displays the in-place cell edit control for the current cell. + Displays the active in-place cell edit control for the current cell + after it was hidden. + + Note that this method does @em not start editing the cell, this is only + done by EnableCellEditControl(). */ void ShowCellEditControl(); diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index 4f5ea81c00..30617b9ee7 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -183,6 +183,7 @@ wxBEGIN_EVENT_TABLE( GridFrame, wxFrame ) EVT_MENU( ID_DELETEROW, GridFrame::DeleteSelectedRows ) EVT_MENU( ID_DELETECOL, GridFrame::DeleteSelectedCols ) EVT_MENU( ID_CLEARGRID, GridFrame::ClearGrid ) + EVT_MENU( ID_EDITCELL, GridFrame::EditCell ) EVT_MENU( ID_SETCORNERLABEL, GridFrame::SetCornerLabelValue ) EVT_MENU( ID_SHOWSEL, GridFrame::ShowSelection ) EVT_MENU( ID_SELCELLS, GridFrame::SelectCells ) @@ -372,6 +373,7 @@ GridFrame::GridFrame() editMenu->Append( ID_DELETEROW, "Delete selected ro&ws" ); editMenu->Append( ID_DELETECOL, "Delete selected co&ls" ); editMenu->Append( ID_CLEARGRID, "Cl&ear grid cell contents" ); + editMenu->Append( ID_EDITCELL, "&Edit current cell" ); editMenu->Append( ID_SETCORNERLABEL, "&Set corner label..." ); editMenu->AppendCheckItem( ID_FREEZE_OR_THAW, "Freeze up to cursor\tCtrl-F" ); @@ -1161,6 +1163,11 @@ void GridFrame::ClearGrid( wxCommandEvent& WXUNUSED(ev) ) grid->ClearGrid(); } +void GridFrame::EditCell( wxCommandEvent& WXUNUSED(ev) ) +{ + grid->EnableCellEditControl(); +} + void GridFrame::SetCornerLabelValue( wxCommandEvent& WXUNUSED(ev) ) { wxTextEntryDialog dialog(this, diff --git a/samples/grid/griddemo.h b/samples/grid/griddemo.h index 086136b7ed..9c7ab3e8f3 100644 --- a/samples/grid/griddemo.h +++ b/samples/grid/griddemo.h @@ -69,6 +69,7 @@ class GridFrame : public wxFrame void DeleteSelectedRows( wxCommandEvent& ); void DeleteSelectedCols( wxCommandEvent& ); void ClearGrid( wxCommandEvent& ); + void EditCell( wxCommandEvent& ); void SetCornerLabelValue( wxCommandEvent& ); void ShowSelection( wxCommandEvent& ); void SelectCells( wxCommandEvent& ); @@ -179,6 +180,7 @@ public: ID_DELETEROW, ID_DELETECOL, ID_CLEARGRID, + ID_EDITCELL, ID_SETCORNERLABEL, ID_SHOWSEL, ID_CHANGESEL, diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index b76f41d04f..6bd351dd84 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -2257,7 +2257,7 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) ///////////////////////////////////////////////////////////////////// -wxBEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow ) +wxBEGIN_EVENT_TABLE( wxGrid, wxScrolledCanvas ) EVT_PAINT( wxGrid::OnPaint ) EVT_SIZE( wxGrid::OnSize ) EVT_KEY_DOWN( wxGrid::OnKeyDown ) @@ -2271,7 +2271,7 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) { - if (!wxScrolledWindow::Create(parent, id, pos, size, + if (!wxScrolledCanvas::Create(parent, id, pos, size, style | wxWANTS_CHARS, name)) return false; @@ -3390,7 +3390,7 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg, void wxGrid::PrepareDCFor(wxDC &dc, wxGridWindow *gridWindow) { - wxScrolledWindow::PrepareDC( dc ); + wxScrolledCanvas::PrepareDC( dc ); wxPoint dcOrigin = dc.GetDeviceOrigin() - GetGridWindowOffset(gridWindow); @@ -5186,7 +5186,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) if ( m_created && !GetBatchCount() ) { // Refresh to get correct scrolled position: - wxScrolledWindow::Refresh(eraseb, rect); + wxScrolledCanvas::Refresh(eraseb, rect); if (rect) { @@ -6824,7 +6824,7 @@ void wxGrid::ForceRefresh() void wxGrid::DoEnable(bool enable) { - wxScrolledWindow::DoEnable(enable); + wxScrolledCanvas::DoEnable(enable); Refresh(false /* don't erase background */); } @@ -7908,7 +7908,7 @@ void wxGrid::SetRowLabelSize( int width ) m_rowLabelWidth = width; InvalidateBestSize(); CalcWindowSizes(); - wxScrolledWindow::Refresh( true ); + wxScrolledCanvas::Refresh( true ); } } @@ -7938,7 +7938,7 @@ void wxGrid::SetColLabelSize( int height ) m_colLabelHeight = height; InvalidateBestSize(); CalcWindowSizes(); - wxScrolledWindow::Refresh( true ); + wxScrolledCanvas::Refresh( true ); } } @@ -9397,52 +9397,67 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) } // now also compare with the column label extent - wxCoord w, h; + wxCoord extentLabel; dc.SetFont( GetLabelFont() ); - bool addMargin = true; + // We add some margin around text for better readability. + const int margin = FromDIP(column ? 10 : 6); if ( column ) { if ( m_useNativeHeader ) { - w = GetGridColHeader()->GetColumnTitleWidth(colOrRow); + extentLabel = GetGridColHeader()->GetColumnTitleWidth(colOrRow); - // GetColumnTitleWidth already adds margins internally. - addMargin = false; - h = 0; + // Note that GetColumnTitleWidth already adds margins internally, + // so we don't need to add them here. } else { - dc.GetMultiLineTextExtent( GetColLabelValue(colOrRow), &w, &h ); - if ( GetColLabelTextOrientation() == wxVERTICAL ) - w = h; + const wxSize + size = dc.GetMultiLineTextExtent(GetColLabelValue(colOrRow)); + extentLabel = GetColLabelTextOrientation() == wxVERTICAL + ? size.y + : size.x; + + // Add some margins around text for better readability. + extentLabel += margin; } } else { - dc.GetMultiLineTextExtent( GetRowLabelValue(colOrRow), &w, &h ); + extentLabel = dc.GetMultiLineTextExtent(GetRowLabelValue(colOrRow)).y; + + // As above, add some margins for readability, although a smaller one + // in vertical direction. + extentLabel += margin; } - extent = column ? w : h; - if ( extent > extentMax ) - extentMax = extent; + // Finally determine the suitable extent fitting both the cells contents + // and the label. if ( !extentMax ) { - // empty column - give default extent (notice that if extentMax is less - // than default extent but != 0, it's OK) - extentMax = column ? m_defaultColWidth : m_defaultRowHeight; + // Special case: all the cells are empty, use the label extent. + extentMax = extentLabel; + if ( !extentMax ) + { + // But if the label is empty too, use the default width/height. + extentMax = column ? m_defaultColWidth : m_defaultRowHeight; + } } - else if ( addMargin ) + else // We have some data in the column cells. { - // leave some space around text - if ( column ) - extentMax += 10; - else - extentMax += 6; + // Ensure we have the same margin around the cells text as we use + // around the label. + extentMax += margin; + + // And increase it to fit the label if necessary. + if ( extentLabel > extentMax ) + extentMax = extentLabel; } + if ( column ) { // Ensure automatic width is not less than minimal width. See the @@ -9659,6 +9674,11 @@ void wxGrid::Fit() AutoSize(); } +void wxGrid::SetFocus() +{ + m_gridWin->SetFocus(); +} + #if WXWIN_COMPATIBILITY_2_8 wxPen& wxGrid::GetDividerPen() const { diff --git a/src/unix/uiactionx11.cpp b/src/unix/uiactionx11.cpp index 96fe8c0629..5233e6d64f 100644 --- a/src/unix/uiactionx11.cpp +++ b/src/unix/uiactionx11.cpp @@ -28,6 +28,8 @@ #include "wx/unix/utilsx11.h" #ifdef __WXGTK3__ +#include "wx/utils.h" + #include "wx/gtk/private/wrapgtk.h" GtkWidget* wxGetTopLevelGTK(); #endif @@ -108,7 +110,12 @@ bool wxUIActionSimulatorX11Impl::SendButtonEvent(int button, bool isDown) // all pending events, notably mouse moves. XSync(m_display, False /* don't discard */); - return DoX11Button(xbutton, isDown); + if ( !DoX11Button(xbutton, isDown) ) + return false; + + XFlush(m_display); + + return true; } #if wxUSE_PLAINX_IMPL @@ -243,7 +250,7 @@ private: bool wxUIActionSimulatorXTestImpl::DoX11Button(int xbutton, bool isDown) { - return XTestFakeButtonEvent(m_display, xbutton, isDown, 0) != 0; + return XTestFakeButtonEvent(m_display, xbutton, isDown, CurrentTime) != 0; } bool wxUIActionSimulatorXTestImpl::DoX11MouseMove(long x, long y) @@ -266,7 +273,7 @@ bool wxUIActionSimulatorXTestImpl::DoX11MouseMove(long x, long y) #endif // GTK+ 3.10+ #endif // __WXGTK3__ - return XTestFakeMotionEvent(m_display, -1, x, y, 0) != 0; + return XTestFakeMotionEvent(m_display, -1, x, y, CurrentTime) != 0; } bool @@ -274,7 +281,7 @@ wxUIActionSimulatorXTestImpl::DoX11Key(KeyCode xkeycode, int WXUNUSED(modifiers), bool isDown) { - return XTestFakeKeyEvent(m_display, xkeycode, isDown, 0) != 0; + return XTestFakeKeyEvent(m_display, xkeycode, isDown, CurrentTime) != 0; } #endif // wxUSE_XTEST @@ -328,6 +335,13 @@ bool wxUIActionSimulatorX11Impl::MouseMove(long x, long y) bool wxUIActionSimulatorX11Impl::MouseUp(int button) { +#ifdef __WXGTK3__ + // This is a horrible hack, but some mouse click events are just lost + // without any apparent reason when using GTK 3 without this, i.e. they + // simply never reach GTK in some runs of the tests. + wxMilliSleep(10); +#endif + return SendButtonEvent(button, false); } @@ -341,7 +355,12 @@ bool wxUIActionSimulatorX11Impl::DoKey(int keycode, int modifiers, bool isDown) if ( xkeycode == NoSymbol ) return false; - return DoX11Key(xkeycode, modifiers, isDown); + if ( !DoX11Key(xkeycode, modifiers, isDown) ) + return false; + + XFlush(m_display); + + return true; } wxUIActionSimulator::wxUIActionSimulator() diff --git a/tests/controls/gridtest.cpp b/tests/controls/gridtest.cpp index 8d7f9fe671..cadc110392 100644 --- a/tests/controls/gridtest.cpp +++ b/tests/controls/gridtest.cpp @@ -16,26 +16,20 @@ #ifndef WX_PRECOMP #include "wx/app.h" + #include "wx/dcclient.h" #endif // WX_PRECOMP #include "wx/grid.h" +#include "wx/headerctrl.h" #include "testableframe.h" #include "asserthelper.h" #include "wx/uiaction.h" -// FIXME: A lot of mouse-related tests sporadically fail in wxGTK. This happens -// almost all the time but sometimes the tests do pass and the failure -// doesn't happen when debugging so this looks like some kind of event -// dispatching/simulating problem rather than a real problem in wxGrid. -// -// Just disable these tests for now but it would be really great to -// really fix the problem. #ifdef __WXGTK__ - #define NONGTK_TEST(test) -#else - #define NONGTK_TEST(test) WXUISIM_TEST(test) -#endif + #include "wx/stopwatch.h" +#endif // __WXGTK__ +#include "waitforpaint.h" class GridTestCase : public CppUnit::TestCase { @@ -48,13 +42,13 @@ public: private: CPPUNIT_TEST_SUITE( GridTestCase ); WXUISIM_TEST( CellEdit ); - NONGTK_TEST( CellClick ); - NONGTK_TEST( ReorderedColumnsCellClick ); - NONGTK_TEST( CellSelect ); - NONGTK_TEST( LabelClick ); - NONGTK_TEST( SortClick ); + WXUISIM_TEST( CellClick ); + WXUISIM_TEST( ReorderedColumnsCellClick ); + WXUISIM_TEST( CellSelect ); + WXUISIM_TEST( LabelClick ); + WXUISIM_TEST( SortClick ); WXUISIM_TEST( Size ); - NONGTK_TEST( RangeSelect ); + WXUISIM_TEST( RangeSelect ); CPPUNIT_TEST( Cursor ); CPPUNIT_TEST( Selection ); CPPUNIT_TEST( AddRowCol ); @@ -70,18 +64,21 @@ private: WXUISIM_TEST( ReadOnly ); WXUISIM_TEST( ResizeScrolledHeader ); WXUISIM_TEST( ColumnMinWidth ); + WXUISIM_TEST( AutoSizeColumn ); CPPUNIT_TEST( PseudoTest_NativeHeader ); - NONGTK_TEST( LabelClick ); - NONGTK_TEST( SortClick ); + WXUISIM_TEST( LabelClick ); + WXUISIM_TEST( SortClick ); CPPUNIT_TEST( ColumnOrder ); WXUISIM_TEST( ResizeScrolledHeader ); WXUISIM_TEST( ColumnMinWidth ); + WXUISIM_TEST( AutoSizeColumn ); CPPUNIT_TEST( DeleteAndAddRowCol ); CPPUNIT_TEST( PseudoTest_NativeLabels ); - NONGTK_TEST( LabelClick ); - NONGTK_TEST( SortClick ); + WXUISIM_TEST( LabelClick ); + WXUISIM_TEST( SortClick ); CPPUNIT_TEST( ColumnOrder ); WXUISIM_TEST( WindowAsEditorControl ); + WXUISIM_TEST( AutoSizeColumn ); CPPUNIT_TEST_SUITE_END(); void CellEdit(); @@ -108,10 +105,25 @@ private: void WindowAsEditorControl(); void ResizeScrolledHeader(); void ColumnMinWidth(); + void AutoSizeColumn(); void PseudoTest_NativeHeader() { ms_nativeheader = true; } void PseudoTest_NativeLabels() { ms_nativeheader = false; ms_nativelabels = true; } + // The helper function to determine the width of the column label depending + // on whether the native column is used. + int GetColumnLabelWidth(wxClientDC& dc, int col, int margin) const + { + if (ms_nativeheader) + return m_grid->GetGridColHeader()->GetColumnTitleWidth(col); + + int w, h; + dc.GetMultiLineTextExtent(m_grid->GetColLabelValue(col), &w, &h); + return w + margin; + } + + void CheckFirstColAutoSize(int expected); + static bool ms_nativeheader; static bool ms_nativelabels; @@ -142,8 +154,15 @@ void GridTestCase::setUp() if( ms_nativelabels ) m_grid->SetUseNativeColLabels(); + WaitForPaint waitForPaint(m_grid->GetGridWindow()); + m_grid->Refresh(); m_grid->Update(); + + if ( !waitForPaint.YieldUntilPainted() ) + { + WARN("Grid not repainted until timeout expiration"); + } } void GridTestCase::tearDown() @@ -176,16 +195,48 @@ void GridTestCase::CellEdit() m_grid->SetFocus(); m_grid->SetGridCursor(1, 1); - m_grid->ShowCellEditControl(); + + wxYield(); sim.Text("abab"); + + // We need to wait until the editor is really shown under GTK, consider + // that it happens once it gets focus. +#ifdef __WXGTK__ + for ( wxStopWatch sw; wxWindow::FindFocus() == m_grid; ) + { + if ( sw.Time() > 250 ) + { + WARN("Editor control not shown until timeout expiration"); + break; + } + + wxYield(); + } +#endif // __WXGTK__ + sim.Char(WXK_RETURN); wxYield(); - CPPUNIT_ASSERT_EQUAL(1, created.GetCount()); - CPPUNIT_ASSERT_EQUAL(1, changing.GetCount()); - CPPUNIT_ASSERT_EQUAL(1, changed.GetCount()); +#ifdef __WXGTK__ + for ( wxStopWatch sw; wxWindow::FindFocus() != m_grid; ) + { + if ( sw.Time() > 250 ) + { + WARN("Editor control not hidden until timeout expiration"); + break; + } + + wxYield(); + } +#endif // __WXGTK__ + + CHECK(m_grid->GetCellValue(1, 1) == "abab"); + + CHECK(created.GetCount() == 1); + CHECK(changing.GetCount() == 1); + CHECK(changed.GetCount() == 1); #endif } @@ -770,7 +821,6 @@ void GridTestCase::Editable() m_grid->SetFocus(); m_grid->SetGridCursor(1, 1); - m_grid->ShowCellEditControl(); sim.Text("abab"); wxYield(); @@ -797,12 +847,20 @@ void GridTestCase::ReadOnly() CPPUNIT_ASSERT(m_grid->IsReadOnly(1, 1)); m_grid->SetFocus(); + +#ifdef __WXGTK__ + // This is a mystery, but we somehow get WXK_RETURN generated by the + // previous test (Editable) in this one. In spite of wxYield() in that + // test, the key doesn't get dispatched there and we have to consume it + // here before setting the current grid cell, as getting WXK_RETURN later + // would move the selection down, to a non read-only cell. + wxYield(); +#endif // __WXGTK__ + m_grid->SetGridCursor(1, 1); CPPUNIT_ASSERT(m_grid->IsCurrentCellReadOnly()); - m_grid->ShowCellEditControl(); - sim.Text("abab"); wxYield(); @@ -950,4 +1008,94 @@ void GridTestCase::ColumnMinWidth() #endif } +void GridTestCase::CheckFirstColAutoSize(int expected) +{ + m_grid->AutoSizeColumn(0); + + wxYield(); + CHECK(m_grid->GetColSize(0) == expected); +} + +void GridTestCase::AutoSizeColumn() +{ + // Hardcoded extra margin for the columns used in grid.cpp. + const int margin = m_grid->FromDIP(10); + + wxGridCellAttr *attr = m_grid->GetOrCreateCellAttr(0, 0); + wxGridCellRenderer *renderer = attr->GetRenderer(m_grid, 0, 0); + REQUIRE(renderer != NULL); + + wxClientDC dcCell(m_grid->GetGridWindow()); + + wxClientDC dcLabel(m_grid->GetGridWindow()); + dcLabel.SetFont(m_grid->GetLabelFont()); + + const wxString shortStr = "W"; + const wxString mediumStr = "WWWW"; + const wxString longStr = "WWWWWWWW"; + const wxString multilineStr = mediumStr + "\n" + longStr; + + SECTION("Empty column and label") + { + m_grid->SetColLabelValue(0, wxString()); + CheckFirstColAutoSize( m_grid->GetDefaultColSize() ); + } + + SECTION("Empty column with label") + { + m_grid->SetColLabelValue(0, mediumStr); + CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) ); + } + + SECTION("Column with empty label") + { + m_grid->SetColLabelValue(0, wxString()); + m_grid->SetCellValue(0, 0, mediumStr); + m_grid->SetCellValue(1, 0, shortStr); + m_grid->SetCellValue(3, 0, longStr); + + CheckFirstColAutoSize( + renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0, + m_grid->GetRowHeight(3)) + margin ); + } + + SECTION("Column with label longer than contents") + { + m_grid->SetColLabelValue(0, multilineStr); + m_grid->SetCellValue(0, 0, mediumStr); + m_grid->SetCellValue(1, 0, shortStr); + CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) ); + } + + SECTION("Column with contents longer than label") + { + m_grid->SetColLabelValue(0, mediumStr); + m_grid->SetCellValue(0, 0, mediumStr); + m_grid->SetCellValue(1, 0, shortStr); + m_grid->SetCellValue(3, 0, multilineStr); + CheckFirstColAutoSize( + renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0, + m_grid->GetRowHeight(3)) + margin ); + } + + SECTION("Column with equally sized contents and label") + { + m_grid->SetColLabelValue(0, mediumStr); + m_grid->SetCellValue(0, 0, mediumStr); + m_grid->SetCellValue(1, 0, mediumStr); + m_grid->SetCellValue(3, 0, mediumStr); + + const int labelWidth = GetColumnLabelWidth(dcLabel, 0, margin); + + const int cellWidth = + renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0, + m_grid->GetRowHeight(3)) + + margin; + + // We can't be sure which size will be greater because of different fonts + // so just calculate the maximum width. + CheckFirstColAutoSize( wxMax(labelWidth, cellWidth) ); + } +} + #endif //wxUSE_GRID diff --git a/tests/test.cpp b/tests/test.cpp index f5135f87fb..1d42529be5 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -577,11 +577,11 @@ bool TestApp::ProcessEvent(wxEvent& event) int TestApp::RunTests() { #if wxUSE_LOG - // Switch off logging unless --verbose - bool verbose = wxLog::GetVerbose(); - wxLog::EnableLogging(verbose); -#else - bool verbose = false; + // Switch off logging to avoid interfering with the tests output unless + // WXTRACE is set, as otherwise setting it would have no effect while + // running the tests. + if ( !wxGetEnv("WXTRACE", NULL) ) + wxLog::EnableLogging(false); #endif // Cast is needed under MSW where Catch also provides an overload taking diff --git a/tests/waitforpaint.h b/tests/waitforpaint.h new file mode 100644 index 0000000000..481fcc8b9a --- /dev/null +++ b/tests/waitforpaint.h @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/waitforpaint.h +// Purpose: Helper WaitForPaint class +// Author: Vadim Zeitlin +// Created: 2019-10-17 (extracted from tests/window/setsize.cpp) +// Copyright: (c) 2019 Vadim Zeitlin +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_TESTS_WAITFORPAINT_H_ +#define _WX_TESTS_WAITFORPAINT_H_ + +#include "wx/stopwatch.h" +#include "wx/window.h" + +// Class used to check if we received the (first) paint event: this is +// currently used under GTK only, as MSW doesn't seem to need to wait for the +// things to work, while under Mac nothing works anyhow. +#ifdef __WXGTK__ + +class WaitForPaint +{ +public: + explicit WaitForPaint(wxWindow* win) + : m_win(*win), + m_painted(false), + m_handler(&m_painted) + { + m_win.Bind(wxEVT_PAINT, m_handler); + } + + // This function waits up to the given number of milliseconds for the paint + // event to come and returns true if we did get it or false otherwise. + bool YieldUntilPainted(int timeoutInMS = 250) const + { + wxStopWatch sw; + for ( ;; ) + { + wxYield(); + + if ( m_painted ) + return true; + + if ( sw.Time() > timeoutInMS ) + return false; + } + } + + ~WaitForPaint() + { + m_win.Unbind(wxEVT_PAINT, m_handler); + } + +private: + wxWindow& m_win; + bool m_painted; + + class PaintHandler + { + public: + // Note that we have to use a pointer here, i.e. we can't just store + // the flag inside the class itself because it's going to be cloned + // inside wx and querying the flag of the original copy wouldtn' work. + explicit PaintHandler(bool* painted) + : m_painted(*painted) + { + } + + void operator()(wxPaintEvent& event) + { + event.Skip(); + m_painted = true; + } + + private: + bool& m_painted; + } m_handler; +}; + +#else // !__WXGTK__ + +class WaitForPaint +{ +public: + explicit WaitForPaint(wxWindow* WXUNUSED(win)) + { + } + + bool YieldUntilPainted(int WXUNUSED(timeoutInMS) = 250) const + { + return true; + } +}; + +#endif // __WXGTK__/!__WXGTK__ + +#endif // _WX_TESTS_WAITFORPAINT_H_ diff --git a/tests/window/setsize.cpp b/tests/window/setsize.cpp index 81b3eeb8d5..e99407faac 100644 --- a/tests/window/setsize.cpp +++ b/tests/window/setsize.cpp @@ -23,9 +23,9 @@ #endif // WX_PRECOMP #include "wx/scopedptr.h" -#include "wx/stopwatch.h" #include "asserthelper.h" +#include "waitforpaint.h" // ---------------------------------------------------------------------------- // tests helpers @@ -47,57 +47,6 @@ protected: virtual wxSize DoGetBestSize() const wxOVERRIDE { return wxSize(50, 250); } }; -// Class used to check if we received the (first) paint event. -class WaitForPaint -{ -public: - // Note that we have to use a pointer here, i.e. we can't just store the - // flag inside the class itself because it's going to be cloned inside wx - // and querying the flag of the original copy is not going to work. - explicit WaitForPaint(bool* painted) - : m_painted(*painted) - { - m_painted = false; - } - - void operator()(wxPaintEvent& event) - { - event.Skip(); - m_painted = true; - } - -private: - bool& m_painted; -}; - -// This function should be used to show the window and wait until we can get -// its real geometry. -void ShowAndWaitForPaint(wxWindow* w) -{ - // Unfortunately showing the window is asynchronous, at least when using - // X11, so we have to wait for some time before retrieving its true - // geometry. And it's not clear how long should we wait, so we do it until - // we get the first paint event -- by then the window really should have - // its final size. - bool painted; - WaitForPaint waitForPaint(&painted); - w->Bind(wxEVT_PAINT, waitForPaint); - - w->Show(); - - wxStopWatch sw; - while ( !painted ) - { - wxYield(); - - if ( sw.Time() > 250 ) - { - WARN("Didn't get a paint event until timeout expiration"); - break; - } - } -} - } // anonymous namespace // ---------------------------------------------------------------------------- @@ -143,7 +92,19 @@ TEST_CASE("wxWindow::MovePreservesSize", "[window][size][move]") wxScopedPtr w(new wxFrame(wxTheApp->GetTopWindow(), wxID_ANY, "Test child frame")); - ShowAndWaitForPaint(w.get()); + // Unfortunately showing the window is asynchronous, at least when using + // X11, so we have to wait for some time before retrieving its true + // geometry. And it's not clear how long should we wait, so we do it until + // we get the first paint event -- by then the window really should have + // its final size. + WaitForPaint waitForPaint(w.get()); + + w->Show(); + + if ( !waitForPaint.YieldUntilPainted() ) + { + WARN("Didn't get a paint event until timeout expiration"); + } const wxRect rectOrig = w->GetRect();