Many changes/fixes to wxFlexGridSizer implementation (no API changes):

- fix the problems addressed by the patch 1667343:
 * only distribute extra space between growable items, not all space
 * take hidden items and gaps into account for ALL grow mode layout
 * fix rounding errors by allocating the remaining pixels to the last item(s)
- refactor the code to avoid duplication between row/column cases
- use STL-like wxList methods instead of compatibility ones


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@45583 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2007-04-22 19:20:14 +00:00
parent 9c57566224
commit 97800f6618
2 changed files with 180 additions and 154 deletions

View File

@@ -734,8 +734,7 @@ public:
protected: protected:
void AdjustForFlexDirection(); void AdjustForFlexDirection();
void AdjustForGrowables(const wxSize& sz, const wxSize& minsz, void AdjustForGrowables(const wxSize& sz);
int nrows, int ncols);
// the heights/widths of all rows/columns // the heights/widths of all rows/columns
wxArrayInt m_rowHeights, wxArrayInt m_rowHeights,

View File

@@ -1346,101 +1346,125 @@ void wxFlexGridSizer::RecalcSizes()
if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 ) if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
return; return;
wxPoint pt( GetPosition() ); const wxPoint pt(GetPosition());
wxSize sz( GetSize() ); const wxSize sz(GetSize());
AdjustForGrowables(sz, m_calculatedMinSize, nrows, ncols); AdjustForGrowables(sz);
sz = wxSize( pt.x + sz.x, pt.y + sz.y ); wxSizerItemList::const_iterator i = m_children.begin();
int y = 0;
int x = pt.x;
for (int c = 0; c < ncols; c++)
{
int y = pt.y;
for ( int r = 0; r < nrows; r++ ) for ( int r = 0; r < nrows; r++ )
{ {
int i = r * ncols + c; if ( m_rowHeights[r] == -1 )
if (i < nitems)
{ {
wxSizerItemList::compatibility_iterator node = m_children.Item( i ); // this row is entirely hidden, skip it
for ( int c = 0; c < ncols; c++ )
++i;
wxASSERT_MSG( node, _T("Failed to find node") ); continue;
}
int w = wxMax( 0, wxMin( m_colWidths[c], sz.x - x ) ); const int hrow = m_rowHeights[r];
int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) ); int h = sz.y - y; // max remaining height, don't overflow it
if ( hrow < h )
h = hrow;
SetItemBounds( node->GetData(), x, y, w, h); int x = 0;
for ( int c = 0; c < ncols; c++, ++i )
{
const int wcol = m_colWidths[c];
if ( wcol == -1 )
continue;
// check if there are any remaining children: it may happen that
// the last row is incomplete
if ( i == m_children.end() )
{
wxASSERT_MSG( r == nrows - 1, _T("too few items") );
return;
} }
if (m_rowHeights[r] != -1)
y = y + m_rowHeights[r] + m_vgap; int w = sz.x - x; // max possible value, ensure we don't overflow
if ( wcol < w )
w = wcol;
SetItemBounds(*i, pt.x + x, pt.y + y, w, h);
x += wcol + m_hgap;
} }
if (m_colWidths[c] != -1)
x = x + m_colWidths[c] + m_hgap; y += hrow + m_vgap;
} }
} }
// helper function used in CalcMin() to sum up the sizes of non-hidden items
static int SumArraySizes(const wxArrayInt& sizes, int gap)
{
// Sum total minimum size, including gaps between rows/columns.
// -1 is used as a magic number meaning empty row/column.
int total = 0;
const size_t count = sizes.size();
for ( size_t n = 0; n < count; n++ )
{
if ( sizes[n] != -1 )
{
if ( total )
total += gap; // separate from the previous column
total += sizes[n];
}
}
return total;
}
wxSize wxFlexGridSizer::CalcMin() wxSize wxFlexGridSizer::CalcMin()
{ {
int nrows, int nrows,
ncols; ncols;
size_t i, s;
// Number of rows/columns can change as items are added or removed. // Number of rows/columns can change as items are added or removed.
if ( !CalcRowsCols(nrows, ncols) ) if ( !CalcRowsCols(nrows, ncols) )
return wxSize(); return wxSize();
m_rowHeights.SetCount(nrows);
m_colWidths.SetCount(ncols);
// We have to recalcuate the sizes in case the item minimum size has // We have to recalculate the sizes in case the item minimum size has
// changed since the previous layout, or the item has been hidden using // changed since the previous layout, or the item has been hidden using
// wxSizer::Show(). If all the items in a row/column are hidden, the final // wxSizer::Show(). If all the items in a row/column are hidden, the final
// dimension of the row/column will be -1, indicating that the column // dimension of the row/column will be -1, indicating that the column
// itself is hidden. // itself is hidden.
for( s = m_rowHeights.GetCount(), i = 0; i < s; ++i ) m_rowHeights.assign(nrows, -1);
m_rowHeights[ i ] = -1; m_colWidths.assign(ncols, -1);
for( s = m_colWidths.GetCount(), i = 0; i < s; ++i )
m_colWidths[ i ] = -1;
wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); // n is the index of the item in left-to-right top-to-bottom order
size_t n = 0;
i = 0; for ( wxSizerItemList::iterator i = m_children.begin();
while (node) i != m_children.end();
++i, ++n )
{ {
wxSizerItem *item = node->GetData(); wxSizerItem * const item = *i;
if ( item->IsShown() ) if ( item->IsShown() )
{ {
wxSize sz( item->CalcMin() ); const wxSize sz(item->CalcMin());
int row = i / ncols;
int col = i % ncols;
m_rowHeights[ row ] = wxMax( wxMax( 0, sz.y ), m_rowHeights[ row ] ); const int row = n / ncols;
m_colWidths[ col ] = wxMax( wxMax( 0, sz.x ), m_colWidths[ col ] ); const int col = n % ncols;
if ( sz.y > m_rowHeights[row] )
m_rowHeights[row] = sz.y;
if ( sz.x > m_colWidths[col] )
m_colWidths[col] = sz.x;
} }
node = node->GetNext();
i++;
} }
AdjustForFlexDirection(); AdjustForFlexDirection();
// Sum total minimum size, including gaps between rows/columns. m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
// -1 is used as a magic number meaning empty column. SumArraySizes(m_rowHeights, m_vgap));
int width = 0;
for (int col = 0; col < ncols; col++)
if ( m_colWidths[ col ] != -1 )
width += m_colWidths[ col ] + m_hgap;
if (width > 0)
width -= m_hgap;
int height = 0;
for (int row = 0; row < nrows; row++)
if ( m_rowHeights[ row ] != -1 )
height += m_rowHeights[ row ] + m_vgap;
if (height > 0)
height -= m_vgap;
m_calculatedMinSize = wxSize( width, height );
return m_calculatedMinSize; return m_calculatedMinSize;
} }
@@ -1477,107 +1501,110 @@ void wxFlexGridSizer::AdjustForFlexDirection()
} }
} }
// helper of AdjustForGrowables() which is called for rows/columns separately
//
// parameters:
// delta: the extra space, we do nothing unless it's positive
// growable: indices or growable rows/cols in sizes array
// sizes: the height/widths of rows/cols to adjust
// proportions: proportions of the growable rows/cols or NULL if they all
// should be assumed to have proportion of 1
static void
DoAdjustForGrowables(int delta,
const wxArrayInt& growable,
wxArrayInt& sizes,
const wxArrayInt *proportions)
{
if ( delta <= 0 )
return;
void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz, const wxSize& minsz, // total sum of proportions of all non-hidden rows
int nrows, int ncols)
{
// what to do with the rows? by default, resize them proportionally
if ( sz.y > minsz.y && ( (m_flexDirection & wxVERTICAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) )
{
int sum_proportions = 0; int sum_proportions = 0;
int growable_space = 0;
// number of currently shown growable rows
int num = 0; int num = 0;
const int max_idx = sizes.size();
const size_t count = growable.size();
size_t idx; size_t idx;
for (idx = 0; idx < m_growableRows.GetCount(); idx++) for ( idx = 0; idx < count; idx++ )
{ {
// Since the number of rows/columns can change as items are // Since the number of rows/columns can change as items are
// inserted/deleted, we need to verify at runtime that the // inserted/deleted, we need to verify at runtime that the
// requested growable rows/columns are still valid. // requested growable rows/columns are still valid.
if (m_growableRows[idx] >= nrows) if ( growable[idx] >= max_idx )
continue; continue;
// If all items in a row/column are hidden, that row/column will // If all items in a row/column are hidden, that row/column will
// have a dimension of -1. This causes the row/column to be // have a dimension of -1. This causes the row/column to be
// hidden completely. // hidden completely.
if (m_rowHeights[ m_growableRows[idx] ] == -1) if ( sizes[growable[idx]] == -1 )
continue; continue;
sum_proportions += m_growableRowsProportions[idx];
growable_space += m_rowHeights[ m_growableRows[idx] ]; if ( proportions )
sum_proportions += (*proportions)[idx];
num++; num++;
} }
if (num > 0) if ( !num )
return;
// the remaining extra free space, adjusted during each iteration
for ( idx = 0; idx < count; idx++ )
{ {
for (idx = 0; idx < m_growableRows.GetCount(); idx++) if ( growable[idx] >= max_idx )
{
if (m_growableRows[idx] >= nrows )
continue; continue;
if (m_rowHeights[ m_growableRows[idx] ] != -1)
{ if ( sizes[ growable[idx] ] == -1 )
int delta = (sz.y - minsz.y); continue;
int cur_delta;
if ( sum_proportions == 0 ) if ( sum_proportions == 0 )
delta = (delta/num) + m_rowHeights[ m_growableRows[idx] ];
else
delta = ((delta+growable_space)*m_growableRowsProportions[idx]) / sum_proportions;
m_rowHeights[ m_growableRows[idx] ] = delta;
}
}
}
}
else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.y > minsz.y) )
{ {
// rounding problem? // no growable rows -- divide extra space evenly among all
for ( int row = 0; row < nrows; ++row ) cur_delta = delta/num;
m_rowHeights[ row ] = sz.y / nrows; num--;
}
else // allocate extra space proportionally
{
const int cur_prop = (*proportions)[idx];
cur_delta = (delta*cur_prop)/sum_proportions;
sum_proportions -= cur_prop;
} }
// the same logic as above but for the columns sizes[growable[idx]] += cur_delta;
if ( sz.x > minsz.x && ( (m_flexDirection & wxHORIZONTAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) ) delta -= cur_delta;
{ }
int sum_proportions = 0;
int growable_space = 0;
int num = 0;
size_t idx;
for (idx = 0; idx < m_growableCols.GetCount(); idx++)
{
// Since the number of rows/columns can change as items are
// inserted/deleted, we need to verify at runtime that the
// requested growable rows/columns are still valid.
if (m_growableCols[idx] >= ncols)
continue;
// If all items in a row/column are hidden, that row/column will
// have a dimension of -1. This causes the column to be hidden
// completely.
if (m_colWidths[ m_growableCols[idx] ] == -1)
continue;
sum_proportions += m_growableColsProportions[idx];
growable_space += m_colWidths[ m_growableCols[idx] ];
num++;
} }
if (num > 0) void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
{ {
for (idx = 0; idx < m_growableCols.GetCount(); idx++) if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
{ {
if (m_growableCols[idx] >= ncols ) // pass NULL instead of proportions if the grow mode is ALL as we
continue; // should treat all rows as having proportion of 1 then
if (m_colWidths[ m_growableCols[idx] ] != -1) DoAdjustForGrowables
(
sz.y - m_calculatedMinSize.y,
m_growableRows,
m_rowHeights,
m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions
: NULL
);
}
if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
{ {
int delta = (sz.x - minsz.x); DoAdjustForGrowables
if (sum_proportions == 0) (
delta = (delta/num) + m_colWidths[ m_growableCols[idx] ]; sz.x - m_calculatedMinSize.x,
else m_growableCols,
delta = ((delta+growable_space)*m_growableColsProportions[idx])/sum_proportions; m_colWidths,
m_colWidths[ m_growableCols[idx] ] = delta; m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
} : NULL
} );
}
}
else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.x > minsz.x) )
{
for ( int col=0; col < ncols; ++col )
m_colWidths[ col ] = sz.x / ncols;
} }
} }