Factor out wxMaxWidthCalculatorBase from wxDataViewCtrl code.
This will allow reusing it elsewhere, notably in the generic wxListCtrl. See #10326.
This commit is contained in:
122
include/wx/generic/private/widthcalc.h
Normal file
122
include/wx/generic/private/widthcalc.h
Normal file
@@ -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_
|
@@ -49,6 +49,7 @@
|
|||||||
#include "wx/selstore.h"
|
#include "wx/selstore.h"
|
||||||
#include "wx/stopwatch.h"
|
#include "wx/stopwatch.h"
|
||||||
#include "wx/weakref.h"
|
#include "wx/weakref.h"
|
||||||
|
#include "wx/generic/private/widthcalc.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// classes
|
// classes
|
||||||
@@ -4882,6 +4883,60 @@ int wxDataViewCtrl::GetModelColumnIndex( unsigned int model_column ) const
|
|||||||
return wxNOT_FOUND;
|
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
|
unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
|
||||||
{
|
{
|
||||||
if ( m_colsBestWidths[idx].width != 0 )
|
if ( m_colsBestWidths[idx].width != 0 )
|
||||||
@@ -4892,146 +4947,19 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
|
|||||||
wxDataViewRenderer *renderer =
|
wxDataViewRenderer *renderer =
|
||||||
const_cast<wxDataViewRenderer*>(column->GetRenderer());
|
const_cast<wxDataViewRenderer*>(column->GetRenderer());
|
||||||
|
|
||||||
class MaxWidthCalculator
|
wxDataViewMaxWidthCalculator calculator(this, m_clientArea, renderer,
|
||||||
{
|
GetModel(), column->GetModelColumn(),
|
||||||
public:
|
m_clientArea->GetRowHeight());
|
||||||
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());
|
|
||||||
|
|
||||||
calculator.UpdateWithWidth(column->GetMinWidth());
|
calculator.UpdateWithWidth(column->GetMinWidth());
|
||||||
|
|
||||||
if ( m_headerArea )
|
if ( m_headerArea )
|
||||||
calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column));
|
calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column));
|
||||||
|
|
||||||
// The code below deserves some explanation. For very large controls, we
|
const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0));
|
||||||
// simply can't afford to calculate sizes for all items, it takes too
|
calculator.ComputeBestColumnWidth(count,
|
||||||
// long. So the best we can do is to check the first and the last N/2
|
m_clientArea->GetLineAt(origin.y),
|
||||||
// items in the control for some sufficiently large N and calculate best
|
m_clientArea->GetLineAt(origin.y + GetClientSize().y));
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_width = calculator.GetMaxWidth();
|
int max_width = calculator.GetMaxWidth();
|
||||||
if ( max_width > 0 )
|
if ( max_width > 0 )
|
||||||
|
Reference in New Issue
Block a user