From: Vadim Zeitlin Date: Sun, 22 Apr 2007 19:20:14 +0000 (+0000) Subject: Many changes/fixes to wxFlexGridSizer implementation (no API changes): X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/97800f661840d18c39e38ef821a3310f70fb767e 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 --- diff --git a/include/wx/sizer.h b/include/wx/sizer.h index cb1bf05439..ea5803beba 100644 --- a/include/wx/sizer.h +++ b/include/wx/sizer.h @@ -734,8 +734,7 @@ public: protected: void AdjustForFlexDirection(); - void AdjustForGrowables(const wxSize& sz, const wxSize& minsz, - int nrows, int ncols); + void AdjustForGrowables(const wxSize& sz); // the heights/widths of all rows/columns wxArrayInt m_rowHeights, diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index 3b5c0f105d..4c54b27d06 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -1346,101 +1346,125 @@ void wxFlexGridSizer::RecalcSizes() if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 ) return; - wxPoint pt( GetPosition() ); - wxSize sz( GetSize() ); - - AdjustForGrowables(sz, m_calculatedMinSize, nrows, ncols); + const wxPoint pt(GetPosition()); + const wxSize sz(GetSize()); - sz = wxSize( pt.x + sz.x, pt.y + sz.y ); + AdjustForGrowables(sz); - int x = pt.x; - for (int c = 0; c < ncols; c++) + wxSizerItemList::const_iterator i = m_children.begin(); + int y = 0; + for ( int r = 0; r < nrows; r++ ) { - int y = pt.y; - for (int r = 0; r < nrows; r++) + if ( m_rowHeights[r] == -1 ) { - int i = r * ncols + c; - 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 ) ); - int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) ); + const int hrow = m_rowHeights[r]; + 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; + } + + 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]; } - if (m_colWidths[c] != -1) - x = x + m_colWidths[c] + m_hgap; } + + return total; } wxSize wxFlexGridSizer::CalcMin() { - int nrows, - ncols; - size_t i, s; + int nrows, + ncols; // Number of rows/columns can change as items are added or removed. if ( !CalcRowsCols(nrows, ncols) ) 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 // 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 // itself is hidden. - for( s = m_rowHeights.GetCount(), i = 0; i < s; ++i ) - m_rowHeights[ i ] = -1; - for( s = m_colWidths.GetCount(), i = 0; i < s; ++i ) - m_colWidths[ i ] = -1; - - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + m_rowHeights.assign(nrows, -1); + m_colWidths.assign(ncols, -1); - i = 0; - while (node) + // n is the index of the item in left-to-right top-to-bottom order + size_t n = 0; + for ( wxSizerItemList::iterator i = m_children.begin(); + i != m_children.end(); + ++i, ++n ) { - wxSizerItem *item = node->GetData(); + wxSizerItem * const item = *i; if ( item->IsShown() ) { - wxSize sz( item->CalcMin() ); - int row = i / ncols; - int col = i % ncols; + const wxSize sz(item->CalcMin()); - m_rowHeights[ row ] = wxMax( wxMax( 0, sz.y ), m_rowHeights[ row ] ); - m_colWidths[ col ] = wxMax( wxMax( 0, sz.x ), m_colWidths[ col ] ); - } + const int row = n / ncols; + const int col = n % ncols; - node = node->GetNext(); - i++; + if ( sz.y > m_rowHeights[row] ) + m_rowHeights[row] = sz.y; + if ( sz.x > m_colWidths[col] ) + m_colWidths[col] = sz.x; + } } AdjustForFlexDirection(); - // Sum total minimum size, including gaps between rows/columns. - // -1 is used as a magic number meaning empty column. - 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 ); + m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap), + SumArraySizes(m_rowHeights, m_vgap)); + return m_calculatedMinSize; } @@ -1477,107 +1501,110 @@ void wxFlexGridSizer::AdjustForFlexDirection() } } - -void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz, const wxSize& minsz, - int nrows, int ncols) +// 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) { - // 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 growable_space = 0; - int num = 0; - size_t idx; - for (idx = 0; idx < m_growableRows.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_growableRows[idx] >= nrows) - continue; + if ( delta <= 0 ) + return; - // 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 - // hidden completely. - if (m_rowHeights[ m_growableRows[idx] ] == -1) - continue; - sum_proportions += m_growableRowsProportions[idx]; - growable_space += m_rowHeights[ m_growableRows[idx] ]; - num++; - } + // total sum of proportions of all non-hidden rows + int sum_proportions = 0; - if (num > 0) - { - for (idx = 0; idx < m_growableRows.GetCount(); idx++) - { - if (m_growableRows[idx] >= nrows ) - continue; - if (m_rowHeights[ m_growableRows[idx] ] != -1) - { - int delta = (sz.y - minsz.y); - 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) ) + // number of currently shown growable rows + int num = 0; + + const int max_idx = sizes.size(); + + const size_t count = growable.size(); + size_t idx; + for ( idx = 0; idx < count; idx++ ) { - // rounding problem? - for ( int row = 0; row < nrows; ++row ) - m_rowHeights[ row ] = sz.y / nrows; + // 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 ( growable[idx] >= max_idx ) + continue; + + // 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 + // hidden completely. + if ( sizes[growable[idx]] == -1 ) + continue; + + if ( proportions ) + sum_proportions += (*proportions)[idx]; + + num++; } - // the same logic as above but for the columns - if ( sz.x > minsz.x && ( (m_flexDirection & wxHORIZONTAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) ) + if ( !num ) + return; + + // the remaining extra free space, adjusted during each iteration + for ( idx = 0; idx < count; idx++ ) { - 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 ( growable[idx] >= max_idx ) + 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 ( sizes[ growable[idx] ] == -1 ) + continue; - if (num > 0) + int cur_delta; + if ( sum_proportions == 0 ) { - for (idx = 0; idx < m_growableCols.GetCount(); idx++) - { - if (m_growableCols[idx] >= ncols ) - continue; - if (m_colWidths[ m_growableCols[idx] ] != -1) - { - int delta = (sz.x - minsz.x); - if (sum_proportions == 0) - delta = (delta/num) + m_colWidths[ m_growableCols[idx] ]; - else - delta = ((delta+growable_space)*m_growableColsProportions[idx])/sum_proportions; - m_colWidths[ m_growableCols[idx] ] = delta; - } - } + // no growable rows -- divide extra space evenly among all + cur_delta = delta/num; + num--; + } + else // allocate extra space proportionally + { + const int cur_prop = (*proportions)[idx]; + cur_delta = (delta*cur_prop)/sum_proportions; + sum_proportions -= cur_prop; } + + sizes[growable[idx]] += cur_delta; + delta -= cur_delta; } - else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.x > minsz.x) ) +} + +void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz) +{ + if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) ) + { + // pass NULL instead of proportions if the grow mode is ALL as we + // should treat all rows as having proportion of 1 then + 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) ) { - for ( int col=0; col < ncols; ++col ) - m_colWidths[ col ] = sz.x / ncols; + DoAdjustForGrowables + ( + sz.x - m_calculatedMinSize.x, + m_growableCols, + m_colWidths, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions + : NULL + ); } }