X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f944aec0cf131c1be0b8c127ba1ecdbc8410c9b2..1483e5db8d719803c2d04984fd5ad85176964e16:/src/common/sizer.cpp diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index a358717876..375f5c2824 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -17,8 +17,8 @@ #pragma hdrstop #endif -#include "wx/display.h" #include "wx/sizer.h" +#include "wx/private/flagscheck.h" #ifndef WX_PRECOMP #include "wx/string.h" @@ -31,6 +31,7 @@ #include "wx/toplevel.h" #endif // WX_PRECOMP +#include "wx/display.h" #include "wx/listimpl.cpp" @@ -87,6 +88,31 @@ WX_DEFINE_EXPORTED_LIST( wxSizerItemList ) // wxSizerItem // ---------------------------------------------------------------------------- +// check for flags conflicts +static const int SIZER_FLAGS_MASK = + wxADD_FLAG(wxCENTRE, + wxADD_FLAG(wxHORIZONTAL, + wxADD_FLAG(wxVERTICAL, + wxADD_FLAG(wxLEFT, + wxADD_FLAG(wxRIGHT, + wxADD_FLAG(wxUP, + wxADD_FLAG(wxDOWN, + wxADD_FLAG(wxALIGN_NOT, + wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL, + wxADD_FLAG(wxALIGN_RIGHT, + wxADD_FLAG(wxALIGN_BOTTOM, + wxADD_FLAG(wxALIGN_CENTER_VERTICAL, + wxADD_FLAG(wxFIXED_MINSIZE, + wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN, + wxADD_FLAG(wxSTRETCH_NOT, + wxADD_FLAG(wxSHRINK, + wxADD_FLAG(wxGROW, + wxADD_FLAG(wxSHAPED, + 0)))))))))))))))))); + +#define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK) + + void wxSizerItem::Init(const wxSizerFlags& flags) { Init(); @@ -94,6 +120,8 @@ void wxSizerItem::Init(const wxSizerFlags& flags) m_proportion = flags.GetProportion(); m_flag = flags.GetFlags(); m_border = flags.GetBorderInPixels(); + + ASSERT_VALID_SIZER_FLAGS( m_flag ); } wxSizerItem::wxSizerItem() @@ -136,6 +164,8 @@ wxSizerItem::wxSizerItem(wxWindow *window, m_id(wxID_NONE), m_userData(userData) { + ASSERT_VALID_SIZER_FLAGS( m_flag ); + DoSetWindow(window); } @@ -160,6 +190,8 @@ wxSizerItem::wxSizerItem(wxSizer *sizer, m_ratio(0.0), m_userData(userData) { + ASSERT_VALID_SIZER_FLAGS( m_flag ); + DoSetSizer(sizer); // m_minSize is set later @@ -189,6 +221,8 @@ wxSizerItem::wxSizerItem(int width, m_id(wxID_NONE), m_userData(userData) { + ASSERT_VALID_SIZER_FLAGS( m_flag ); + DoSetSpacer(wxSize(width, height)); } @@ -451,7 +485,7 @@ void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ ) break; case Item_Sizer: - m_sizer->SetDimension(pos.x, pos.y, size.x, size.y); + m_sizer->SetDimension(pos, size); break; case Item_Spacer: @@ -522,6 +556,9 @@ void wxSizerItem::Show( bool show ) bool wxSizerItem::IsShown() const { + if ( m_flag & wxRESERVE_SPACE_EVEN_IF_HIDDEN ) + return true; + switch ( m_kind ) { case Item_None: @@ -834,11 +871,13 @@ void wxSizer::DeleteWindows() } } -wxSize wxSizer::Fit( wxWindow *window ) +wxSize wxSizer::ComputeFittingClientSize(wxWindow *window) { + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); + // take the min size by default and limit it by max size - wxSize size = GetMinWindowSize(window); - wxSize sizeMax = GetMaxWindowSize(window); + wxSize size = GetMinClientSize(window); + wxSize sizeMax; wxTopLevelWindow *tlw = wxDynamicCast(window, wxTopLevelWindow); if ( tlw ) @@ -846,31 +885,51 @@ wxSize wxSizer::Fit( wxWindow *window ) // hack for small screen devices where TLWs are always full screen if ( tlw->IsAlwaysMaximized() ) { - size = tlw->GetSize(); + return tlw->GetClientSize(); } - else // normal situation - { - // limit the window to the size of the display it is on - int disp = wxDisplay::GetFromWindow(window); - if ( disp == wxNOT_FOUND ) - { - // or, if we don't know which one it is, of the main one - disp = 0; - } - sizeMax = wxDisplay(disp).GetClientArea().GetSize(); + // limit the window to the size of the display it is on + int disp = wxDisplay::GetFromWindow(window); + if ( disp == wxNOT_FOUND ) + { + // or, if we don't know which one it is, of the main one + disp = 0; } + + sizeMax = wxDisplay(disp).GetClientArea().GetSize(); + + // space for decorations and toolbars etc. + sizeMax = tlw->WindowToClientSize(sizeMax); + } + else + { + sizeMax = GetMaxClientSize(window); } if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x ) - size.x = sizeMax.x; + size.x = sizeMax.x; if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y ) - size.y = sizeMax.y; + size.y = sizeMax.y; + return size; +} - window->SetSize( size ); +wxSize wxSizer::ComputeFittingWindowSize(wxWindow *window) +{ + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); - return size; + return window->ClientToWindowSize(ComputeFittingClientSize(window)); +} + +wxSize wxSizer::Fit( wxWindow *window ) +{ + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); + + // set client size + window->SetClientSize(ComputeFittingClientSize(window)); + + // return entire size + return window->GetSize(); } void wxSizer::FitInside( wxWindow *window ) @@ -899,12 +958,16 @@ void wxSizer::SetSizeHints( wxWindow *window ) // Preserve the window's max size hints, but set the // lower bound according to the sizer calculations. - wxSize size = Fit( window ); + // This is equivalent to calling Fit(), except that we need to set + // the size hints _in between_ the two steps performed by Fit + // (1. ComputeFittingClientSize, 2. SetClientSize). That's because + // otherwise SetClientSize() could have no effect if there already are + // size hints in effect that forbid requested client size. - window->SetSizeHints( size.x, - size.y, - window->GetMaxWidth(), - window->GetMaxHeight() ); + const wxSize clientSize = ComputeFittingClientSize(window); + + window->SetMinClientSize(clientSize); + window->SetClientSize(clientSize); } #if WXWIN_COMPATIBILITY_2_8 @@ -914,38 +977,12 @@ void wxSizer::SetVirtualSizeHints( wxWindow *window ) } #endif // WXWIN_COMPATIBILITY_2_8 -wxSize wxSizer::GetMaxWindowSize( wxWindow *window ) const -{ - return window->GetMaxSize(); -} - -wxSize wxSizer::GetMinWindowSize( wxWindow *window ) -{ - wxSize minSize( GetMinSize() ); - wxSize size( window->GetSize() ); - wxSize client_size( window->GetClientSize() ); - - return wxSize( minSize.x+size.x-client_size.x, - minSize.y+size.y-client_size.y ); -} - // TODO on mac we need a function that determines how much free space this // min size contains, in order to make sure that we have 20 pixels of free // space around the controls wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const { - wxSize maxSize( window->GetMaxSize() ); - - if ( maxSize != wxDefaultSize ) - { - wxSize size( window->GetSize() ); - wxSize client_size( window->GetClientSize() ); - - return wxSize( maxSize.x + client_size.x - size.x, - maxSize.y + client_size.y - size.y ); - } - else - return wxDefaultSize; + return window->WindowToClientSize(window->GetMaxSize()); } wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) ) @@ -968,15 +1005,6 @@ wxSize wxSizer::VirtualFitSize( wxWindow *window ) return size; } -void wxSizer::SetDimension( int x, int y, int width, int height ) -{ - m_position.x = x; - m_position.y = y; - m_size.x = width; - m_size.y = height; - Layout(); -} - wxSize wxSizer::GetMinSize() { wxSize ret( CalcMin() ); @@ -1296,25 +1324,33 @@ wxGridSizer::wxGridSizer( int cols, int vgap, int hgap ) int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const { - int nitems = m_children.GetCount(); - if ( nitems) + const int nitems = m_children.GetCount(); + if ( m_cols && m_rows ) { - if ( m_cols ) - { - ncols = m_cols; - nrows = (nitems + m_cols - 1) / m_cols; - } - else if ( m_rows ) - { - ncols = (nitems + m_rows - 1) / m_rows; - nrows = m_rows; - } - else // 0 columns, 0 rows? - { - wxFAIL_MSG( _T("grid sizer must have either rows or columns fixed") ); + // if both rows and columns are specified by user, use the provided + // values even if we don't have enough items but check that we don't + // have too many of them as this is going to result in problems later + ncols = m_cols; + nrows = m_rows; - nrows = ncols = 0; - } + wxASSERT_MSG( ncols*nrows >= nitems, "too many items in grid sizer" ); + } + else if ( m_cols ) + { + ncols = m_cols; + nrows = (nitems + m_cols - 1) / m_cols; + } + else if ( m_rows ) + { + ncols = (nitems + m_rows - 1) / m_rows; + nrows = m_rows; + } + else // 0 columns, 0 rows? + { + wxFAIL_MSG( _T("grid sizer must have either rows or columns fixed") ); + + nrows = + ncols = 0; } return nitems; @@ -1603,11 +1639,9 @@ wxSize wxFlexGridSizer::CalcMin() m_rowHeights.assign(nrows, -1); m_colWidths.assign(ncols, -1); - // 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 ) + ++i) { wxSizerItem * const item = *i; if ( item->IsShown() ) @@ -1761,21 +1795,21 @@ void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz) { const int col = n % ncols; didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y); - } + } // Only redo if info was actually used if( didAdjustMinSize ) - { - DoAdjustForGrowables - ( - sz.x - m_calculatedMinSize.x, - m_growableCols, - m_colWidths, - m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions - : NULL - ); + { + DoAdjustForGrowables + ( + sz.x - m_calculatedMinSize.x, + m_growableCols, + m_colWidths, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions + : NULL + ); + } } -} if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) ) { @@ -1792,15 +1826,36 @@ void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz) } } +bool wxFlexGridSizer::IsRowGrowable( size_t idx ) +{ + return m_growableRows.Index( idx ) != wxNOT_FOUND; +} + +bool wxFlexGridSizer::IsColGrowable( size_t idx ) +{ + return m_growableCols.Index( idx ) != wxNOT_FOUND; +} void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion ) { + int nrows, ncols; + CalcRowsCols(nrows, ncols); + wxCHECK_RET( idx < (size_t)nrows, "invalid row index" ); + + wxASSERT_MSG( !IsRowGrowable( idx ), + "AddGrowableRow() called for growable row" ); m_growableRows.Add( idx ); m_growableRowsProportions.Add( proportion ); } void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion ) { + int nrows, ncols; + CalcRowsCols(nrows, ncols); + wxCHECK_RET( idx < (size_t)ncols, "invalid column index" ); + + wxASSERT_MSG( !IsColGrowable( idx ), + "AddGrowableCol() called for growable column" ); m_growableCols.Add( idx ); m_growableColsProportions.Add( proportion ); } @@ -1898,15 +1953,22 @@ void wxBoxSizer::RecalcSizes() // adjust the size in the major direction using the proportion wxCoord majorSize = GetSizeInMajorDir(sizeThis); - const int propItem = item->GetProportion(); - if ( propItem ) + + // if there is not enough space, don't try to distribute negative space + // among the children, this would result in overlapping windows which + // we don't want + if ( delta > 0 ) { - const int deltaItem = (delta * propItem) / totalProportion; + const int propItem = item->GetProportion(); + if ( propItem ) + { + const int deltaItem = (delta * propItem) / totalProportion; - majorSize += deltaItem; + majorSize += deltaItem; - delta -= deltaItem; - totalProportion -= propItem; + delta -= deltaItem; + totalProportion -= propItem; + } } @@ -1925,7 +1987,7 @@ void wxBoxSizer::RecalcSizes() } // NB: wxCENTRE is used here only for backwards compatibility, // wxALIGN_CENTRE should be used in new code - else if ( flag & (wxCENTER | wxALIGN_CENTRE) ) + else if ( flag & (wxCENTER | (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL : wxALIGN_CENTRE_VERTICAL))) { PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2; } @@ -1975,224 +2037,6 @@ wxSize wxBoxSizer::CalcMin() return m_minSize; } -//--------------------------------------------------------------------------- -// wxWrapSizer -//--------------------------------------------------------------------------- - -#define wxDEFAULT_PROPORTION_LAST 1000000 - -// User data to hold old proportion for last item on line -// (which might be extended) -struct wxPropHolder : public wxObject -{ - wxPropHolder( ) : m_item(0), m_propOld(0) { } - void Init( wxSizerItem *item, int propOld ) { m_item=item; m_propOld=propOld; } - - wxSizerItem *m_item; - int m_propOld; -}; - -IMPLEMENT_DYNAMIC_CLASS(wxWrapSizer, wxBoxSizer); - -wxWrapSizer::wxWrapSizer( int orient, int flags ) - : wxBoxSizer(orient), - m_prim_size_last( -1 ), - m_rows(orient^wxBOTH), - m_flags(flags) -{ -} - -wxWrapSizer::~wxWrapSizer() -{ - // Have to clear grand child items so that they're not deleted twice - for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- ) - { - wxSizer *psz = m_rows.GetItem((size_t)ix)->GetSizer(); - wxSizerItemList &sl = psz->GetChildren(); - while( sl.GetLast() ) - sl.Erase( sl.GetLast() ); - } -} - - -bool wxWrapSizer::InformFirstDirection( int direction, int size, int WXUNUSED(availableOtherDir) ) -{ - if( !direction ) - { - // Better to keep value, then CalcMin will work better - //m_prim_size_last = -1; - return false; - } - if( direction==m_orient ) - { - // The direction is same as our primary, so we can make use of it - m_prim_size_last = size; - return true; - } - else - return false; -} - - -void wxWrapSizer::AdjustPropLastItem(wxSizer *psz, wxSizerItem *itemLast) -{ - wxSizerItem *psi = m_rows.GetItem(psz); - wxASSERT(psi); - wxPropHolder *pph = (wxPropHolder*)psi->GetUserData(); - if ( !pph ) - psi->SetUserData( pph=new wxPropHolder ); - - pph->Init( itemLast, itemLast->GetProportion() ); - itemLast->SetProportion( wxDEFAULT_PROPORTION_LAST ); -} - -void wxWrapSizer::RecalcSizes() -{ - wxASSERT( m_orient&wxBOTH ); - if (m_children.GetCount() == 0) - return; - - // What we do here is to put our items into child box sizers, - // as many of them as we have lines. - - // Empty all items in all rows in owned sizer. - // We have to access the list directly, since we don't want to - // destroy the wxSizerItems. - for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- ){ - wxSizerItem *psi = m_rows.GetItem( (size_t)ix ); - - // Restore proportion for last item on line (if item has not been deleted) - wxPropHolder *pph = (wxPropHolder*)psi->GetUserData(); - if( pph && GetChildren().Find(pph->m_item) ) - pph->m_item->SetProportion(pph->m_propOld); - - wxSizer *psz = psi->GetSizer(); - wxASSERT( psz ); - wxSizerItemList &sl = psz->GetChildren(); - while( sl.GetLast() ) - sl.Erase( sl.GetLast() ); - } - - int lineSumMajor = 0; - int majorSize = GetSizeInMajorDir(m_size); - - // Make sure we have at least one child sizer - m_n_line = 1; - if( !m_rows.GetChildren().GetCount() ) - m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND ); - - // The sizer where to insert items in - wxSizer *psz = m_rows.GetItem((size_t)0)->GetSizer(); - wxASSERT( psz ); - - // Now put our child items into child sizers instead - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - wxSizerItem *item = NULL, *itemLast=NULL; - while (node) - { - item = node->GetData(); - if ( item->IsShown() ) - { - wxSize minSz = item->GetMinSize(); - int minSzMajor = GetSizeInMajorDir(minSz); - - // More space on this line? - if( !lineSumMajor || lineSumMajor+minSzMajor<=majorSize ) - { - lineSumMajor += minSzMajor; - } - else - { - lineSumMajor = minSzMajor; - // Get a new empty sizer to insert into - if( (int)m_rows.GetChildren().GetCount()<=m_n_line ) - m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND ); - - // If we have extend-last-on-each-line mode, then do so now - // Note: We must store old proportion value then. - if( m_flags&wxEXTEND_LAST_ON_EACH_LINE ) - AdjustPropLastItem(psz,itemLast); - - // The sizer where to insert items in - psz = m_rows.GetItem(m_n_line++)->GetSizer(); - } - itemLast = item; - psz->Add( item ); - // If item is a window, it now has a pointer to the child sizer, - // which is wrong. Set it to point to us. - if( item->GetWindow() ) - item->GetWindow()->SetContainingSizer( this ); - } - node = node->GetNext(); - } - - // If we have extend-last-on-each-line mode, then do so now - if( m_flags&wxEXTEND_LAST_ON_EACH_LINE ) - AdjustPropLastItem(psz,itemLast); - - // If we have more sizers than lines, remove them - while( (int)m_rows.GetChildren().GetCount()>m_n_line ) - m_rows.Remove( m_n_line ); - - // Now do layout on row sizer - m_rows.SetDimension( m_position.x, m_position.y, m_size.x, m_size.y ); - - // Remember this to next time (will be overridden by InformFirstDirection if used) - m_prim_size_last = GetSizeInMajorDir(m_size); -} - - -wxSize wxWrapSizer::CalcMin() -{ - if (m_children.GetCount() == 0) - return wxSize(); - - // Algorithm for calculating min size: (assuming horizontal orientation) - // X: Max width of all members - // Y: Based on last X, calculate how many lines needed - // First time around, assume all items fits on one line - - int maxMajor = 0; - int minorSum = 0; - int lineMaxMinor = 0; - int lineSumMajor = 0; - m_n_line = 0; - - // precalc item minsizes and fit on lines (preliminary) - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - while (node) - { - wxSizerItem *item = node->GetData(); - if ( item->IsShown() ) - { - wxSize minSz = item->CalcMin(); - int szMajor = GetSizeInMajorDir(minSz); - int szMinor = GetSizeInMinorDir(minSz); - if( szMajor>maxMajor ) maxMajor = szMajor; - // More space on this line? - if( m_prim_size_last<0 || !lineSumMajor || - lineSumMajor+szMajor<=m_prim_size_last ) - { - lineSumMajor += szMajor; - if( szMinor>lineMaxMinor ) - lineMaxMinor = szMinor; - } - else - { - minorSum += lineMaxMinor; // Add height of highest item on last line - m_n_line++; - lineMaxMinor = szMinor; - lineSumMajor = szMajor; - } - } - node = node->GetNext(); - } - minorSum += lineMaxMinor; // Add height of highest item on last line - - m_minSize = SizeFromMajorMinor(maxMajor, minorSum); - return m_minSize; -} - //--------------------------------------------------------------------------- // wxStaticBoxSizer //---------------------------------------------------------------------------