Factor out wxMaxWidthCalculatorBase from wxDataViewCtrl code.

This will allow reusing it elsewhere, notably in the generic wxListCtrl.

See #10326.
This commit is contained in:
Vadim Zeitlin
2015-03-11 21:45:48 +01:00
parent 1682000241
commit fa1c3ecfa6
2 changed files with 184 additions and 134 deletions

View File

@@ -49,6 +49,7 @@
#include "wx/selstore.h"
#include "wx/stopwatch.h"
#include "wx/weakref.h"
#include "wx/generic/private/widthcalc.h"
//-----------------------------------------------------------------------------
// classes
@@ -4882,6 +4883,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<wxDataViewCtrl*>(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 )
@@ -4892,146 +4947,19 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
wxDataViewRenderer *renderer =
const_cast<wxDataViewRenderer*>(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<wxDataViewCtrl*>(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 )