diff --git a/include/wx/generic/private/widthcalc.h b/include/wx/generic/private/widthcalc.h new file mode 100644 index 0000000000..05621dcf1d --- /dev/null +++ b/include/wx/generic/private/widthcalc.h @@ -0,0 +1,122 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/generic/private/widthcalc.h +// Purpose: wxMaxWidthCalculatorBase helper class. +// Author: Václav Slavík, Kinaou Hervé +// Copyright: (c) 2015 wxWidgets team +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_GENERIC_PRIVATE_WIDTHCALC_H_ +#define _WX_GENERIC_PRIVATE_WIDTHCALC_H_ + +#include "wx/defs.h" + +#if wxUSE_DATAVIEWCTRL + +#include "wx/timer.h" + +// ---------------------------------------------------------------------------- +// wxMaxWidthCalculatorBase: base class for calculating max column width +// ---------------------------------------------------------------------------- + +class wxMaxWidthCalculatorBase +{ +public: + // column of which calculate the width + explicit wxMaxWidthCalculatorBase(size_t column) + : m_column(column), + m_width(0) + { + } + + void UpdateWithWidth(int width) + { + m_width = wxMax(m_width, width); + } + + // Update the max with for the expected row + virtual void UpdateWithRow(int row) = 0; + + int GetMaxWidth() const { return m_width; } + size_t GetColumn() const { return m_column; } + + void + ComputeBestColumnWidth(size_t count, + size_t first_visible, + size_t last_visible) + { + // The code below deserves some explanation. For very large controls, we + // simply can't afford to calculate sizes for all items, it takes too + // long. So the best we can do is to check the first and the last N/2 + // items in the control for some sufficiently large N and calculate best + // sizes from that. That can result in the calculated best width being too + // small for some outliers, but it's better to get slightly imperfect + // result than to wait several seconds after every update. To avoid highly + // visible miscalculations, we also include all currently visible items + // no matter what. Finally, the value of N is determined dynamically by + // measuring how much time we spent on the determining item widths so far. + +#if wxUSE_STOPWATCH + size_t top_part_end = count; + static const long CALC_TIMEOUT = 20/*ms*/; + // don't call wxStopWatch::Time() too often + static const unsigned CALC_CHECK_FREQ = 100; + wxStopWatch timer; +#else + // use some hard-coded limit, that's the best we can do without timer + size_t top_part_end = wxMin(500, count); +#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH + + size_t row = 0; + + for ( row = 0; row < top_part_end; row++ ) + { +#if wxUSE_STOPWATCH + if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 && + timer.Time() > CALC_TIMEOUT ) + break; +#endif // wxUSE_STOPWATCH + UpdateWithRow(row); + } + + // row is the first unmeasured item now; that's our value of N/2 + if ( row < count ) + { + top_part_end = row; + + // add bottom N/2 items now: + const size_t bottom_part_start = wxMax(row, count - row); + for ( row = bottom_part_start; row < count; row++ ) + { + UpdateWithRow(row); + } + + // finally, include currently visible items in the calculation: + first_visible = wxMax(first_visible, top_part_end); + last_visible = wxMin(bottom_part_start, last_visible); + + for ( row = first_visible; row < last_visible; row++ ) + { + UpdateWithRow(row); + } + + wxLogTrace("items container", + "determined best size from %d top, %d bottom " + "plus %d more visible items out of %d total", + top_part_end, + count - bottom_part_start, + wxMax(0, last_visible - first_visible), + count); + } + } + +private: + const size_t m_column; + int m_width; + + wxDECLARE_NO_COPY_CLASS(wxMaxWidthCalculatorBase); +}; + +#endif // wxUSE_DATAVIEWCTRL + +#endif // _WX_GENERIC_PRIVATE_WIDTHCALC_H_ diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 8757128dfb..2dd77b47c5 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -48,6 +48,7 @@ #include "wx/dnd.h" #include "wx/stopwatch.h" #include "wx/weakref.h" +#include "wx/generic/private/widthcalc.h" //----------------------------------------------------------------------------- // classes @@ -4851,6 +4852,60 @@ int wxDataViewCtrl::GetModelColumnIndex( unsigned int model_column ) const return wxNOT_FOUND; } +class wxDataViewMaxWidthCalculator : public wxMaxWidthCalculatorBase +{ +public: + wxDataViewMaxWidthCalculator(const wxDataViewCtrl *dvc, + wxDataViewMainWindow *clientArea, + wxDataViewRenderer *renderer, + const wxDataViewModel *model, + size_t model_column, + int expanderSize) + : wxMaxWidthCalculatorBase(model_column), + m_dvc(dvc), + m_clientArea(clientArea), + m_renderer(renderer), + m_model(model), + m_expanderSize(expanderSize) + { + int index = dvc->GetModelColumnIndex( model_column ); + wxDataViewColumn* column = index == wxNOT_FOUND ? NULL : dvc->GetColumn(index); + m_isExpanderCol = + !clientArea->IsList() && + (column == 0 || + GetExpanderColumnOrFirstOne(const_cast(dvc)) == column ); + } + + virtual void UpdateWithRow(int row) wxOVERRIDE + { + int indent = 0; + wxDataViewItem item; + + if ( m_isExpanderCol ) + { + wxDataViewTreeNode *node = m_clientArea->GetTreeNodeByRow(row); + item = node->GetItem(); + indent = m_dvc->GetIndent() * node->GetIndentLevel() + m_expanderSize; + } + else + { + item = m_clientArea->GetItemByRow(row); + } + + m_renderer->PrepareForItem(m_model, item, GetColumn()); + UpdateWithWidth(m_renderer->GetSize().x + indent); + } + +private: + const wxDataViewCtrl *m_dvc; + wxDataViewMainWindow *m_clientArea; + wxDataViewRenderer *m_renderer; + const wxDataViewModel *m_model; + bool m_isExpanderCol; + int m_expanderSize; +}; + + unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const { if ( m_colsBestWidths[idx].width != 0 ) @@ -4861,146 +4916,19 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const wxDataViewRenderer *renderer = const_cast(column->GetRenderer()); - class MaxWidthCalculator - { - public: - MaxWidthCalculator(const wxDataViewCtrl *dvc, - wxDataViewMainWindow *clientArea, - wxDataViewRenderer *renderer, - const wxDataViewModel *model, - unsigned int model_column, - int expanderSize) - : m_width(0), - m_dvc(dvc), - m_clientArea(clientArea), - m_renderer(renderer), - m_model(model), - m_model_column(model_column), - m_expanderSize(expanderSize) - - { - int index = dvc->GetModelColumnIndex( model_column ); - wxDataViewColumn* column = index == wxNOT_FOUND ? NULL : dvc->GetColumn(index); - m_isExpanderCol = - !clientArea->IsList() && - (column == 0 || - GetExpanderColumnOrFirstOne(const_cast(dvc)) == column ); - } - - void UpdateWithWidth(int width) - { - m_width = wxMax(m_width, width); - } - - void UpdateWithRow(int row) - { - int indent = 0; - wxDataViewItem item; - - if ( m_isExpanderCol ) - { - wxDataViewTreeNode *node = m_clientArea->GetTreeNodeByRow(row); - item = node->GetItem(); - indent = m_dvc->GetIndent() * node->GetIndentLevel() + m_expanderSize; - } - else - { - item = m_clientArea->GetItemByRow(row); - } - - m_renderer->PrepareForItem(m_model, item, m_model_column); - m_width = wxMax(m_width, m_renderer->GetSize().x + indent); - } - - int GetMaxWidth() const { return m_width; } - - private: - int m_width; - const wxDataViewCtrl *m_dvc; - wxDataViewMainWindow *m_clientArea; - wxDataViewRenderer *m_renderer; - const wxDataViewModel *m_model; - unsigned m_model_column; - bool m_isExpanderCol; - int m_expanderSize; - }; - - MaxWidthCalculator calculator(this, m_clientArea, renderer, - GetModel(), column->GetModelColumn(), - m_clientArea->GetRowHeight()); + wxDataViewMaxWidthCalculator calculator(this, m_clientArea, renderer, + GetModel(), column->GetModelColumn(), + m_clientArea->GetRowHeight()); calculator.UpdateWithWidth(column->GetMinWidth()); if ( m_headerArea ) calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column)); - // The code below deserves some explanation. For very large controls, we - // simply can't afford to calculate sizes for all items, it takes too - // long. So the best we can do is to check the first and the last N/2 - // items in the control for some sufficiently large N and calculate best - // sizes from that. That can result in the calculated best width being too - // small for some outliers, but it's better to get slightly imperfect - // result than to wait several seconds after every update. To avoid highly - // visible miscalculations, we also include all currently visible items - // no matter what. Finally, the value of N is determined dynamically by - // measuring how much time we spent on the determining item widths so far. - -#if wxUSE_STOPWATCH - int top_part_end = count; - static const long CALC_TIMEOUT = 20/*ms*/; - // don't call wxStopWatch::Time() too often - static const unsigned CALC_CHECK_FREQ = 100; - wxStopWatch timer; -#else - // use some hard-coded limit, that's the best we can do without timer - int top_part_end = wxMin(500, count); -#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH - - int row = 0; - - for ( row = 0; row < top_part_end; row++ ) - { -#if wxUSE_STOPWATCH - if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 && - timer.Time() > CALC_TIMEOUT ) - break; -#endif // wxUSE_STOPWATCH - calculator.UpdateWithRow(row); - } - - // row is the first unmeasured item now; that's our value of N/2 - - if ( row < count ) - { - top_part_end = row; - - // add bottom N/2 items now: - const int bottom_part_start = wxMax(row, count - row); - for ( row = bottom_part_start; row < count; row++ ) - { - calculator.UpdateWithRow(row); - } - - // finally, include currently visible items in the calculation: - const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0)); - int first_visible = m_clientArea->GetLineAt(origin.y); - int last_visible = m_clientArea->GetLineAt(origin.y + GetClientSize().y); - - first_visible = wxMax(first_visible, top_part_end); - last_visible = wxMin(bottom_part_start, last_visible); - - for ( row = first_visible; row < last_visible; row++ ) - { - calculator.UpdateWithRow(row); - } - - wxLogTrace("dataview", - "determined best size from %d top, %d bottom plus %d more visible items out of %d total", - top_part_end, - count - bottom_part_start, - wxMax(0, last_visible - first_visible), - count); - } + const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0)); + calculator.ComputeBestColumnWidth(count, + m_clientArea->GetLineAt(origin.y), + m_clientArea->GetLineAt(origin.y + GetClientSize().y)); int max_width = calculator.GetMaxWidth(); if ( max_width > 0 )