Added wxWrapSizer (modified patch: [1826950] Wrapping Sizer) from Arne Steinarson

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@50568 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robert Roebling 2007-12-08 11:37:17 +00:00
parent a0b5751160
commit 15f7c30516
9 changed files with 615 additions and 40 deletions

View File

@ -187,6 +187,7 @@ These are the classes relevant to sizer-based layout.
\twocolitem{\helpref{wxGridBagSizer}{wxgridbagsizer}}{Another grid sizer that lets you specify the cell an item is in, and items can span rows and/or columns.}
\twocolitem{\helpref{wxBoxSizer}{wxboxsizer}}{A sizer for laying out windows in a row or column}
\twocolitem{\helpref{wxStaticBoxSizer}{wxstaticboxsizer}}{Same as wxBoxSizer, but with a surrounding static box}
\twocolitem{\helpref{wxWrapSizer}{wxwrapsizer}}{A sizer which wraps its child controls as size permits}
\end{twocollist}
\overview{Constraints overview}{constraintsoverview} describes constraints-based layout.

View File

@ -458,6 +458,7 @@
\input vscrolledwindow.tex
\input window.tex
\input wupdlock.tex
\input wrapsizer.tex
\input createevt.tex
\input windowdc.tex
\input destroyevt.tex

View File

@ -4,8 +4,10 @@ wxSizer is the abstract base class used for laying out subwindows in a window. Y
cannot use wxSizer directly; instead, you will have to use one of the sizer
classes derived from it. Currently there are \helpref{wxBoxSizer}{wxboxsizer},
\helpref{wxStaticBoxSizer}{wxstaticboxsizer},
\helpref{wxGridSizer}{wxgridsizer}
\helpref{wxFlexGridSizer}{wxflexgridsizer} and \helpref{wxGridBagSizer}{wxgridbagsizer}.
\helpref{wxGridSizer}{wxgridsizer},
\helpref{wxFlexGridSizer}{wxflexgridsizer},
\helpref{wxWrapSizer}{wxwrapsizer}
and \helpref{wxGridBagSizer}{wxgridbagsizer}.
The layout algorithm used by sizers in wxWidgets is closely related to layout
in other GUI toolkits, such as Java's AWT, the GTK toolkit or the Qt toolkit. It is

View File

@ -0,0 +1,53 @@
\section{\class{wxWrapSizer}}\label{wxwrapsizer}
A wrap sizer lays out its items in a single line, like a box sizer - as long
as there is space available in that direction. Once all available space in
the primary direction has been used a new line is added an items are added there.
So a wrap sizer has a primary orientation for adding items, and adds lines
as needed in the secondary direction.
\wxheading{Derived from}
\helpref{wxBoxSizer}{wxboxsizer}\\
\helpref{wxSizer}{wxsizer}\\
\helpref{wxObject}{wxobject}
\wxheading{Include files}
<wx/wrapsizer.h>
\wxheading{Library}
\helpref{wxCore}{librarieslist}
\wxheading{See also}
\helpref{wxBoxSizer}{wxboxsizer}, \helpref{wxSizer}{wxsizer}, \helpref{Sizer overview}{sizeroverview}
\latexignore{\rtfignore{\wxheading{Members}}}
\membersection{wxWrapSizer::wxWrapSizer}\label{wxwrapsizerwxwrapsizer}
\func{}{wxWrapSizer}{\param{int }{orient}, \param{int }{flags}}
Constructor for a wxWrapSizer. {\it orient} determines the primary direction of
the sizer (the most common case being wxHORIZONTAL). The flags parameter may have
the value wxEXTEND_LAST_ON_EACH_LINE. This will cause the last item one each line
to use any remaining space on that line.
\membersection{wxWrapSizer::InformFirstDirection}\label{wxwrapsizerinformfirstdirection}
\func{bool}{InformFirstDirection}{\param{int }{direction}, \param{int }{size},
\param{int }{availableOtherDir }}
Not used by an application. This is the mechanism by which sizers can inform
sub-items of the first determined size component. The sub-item can then better
determine its size requirements.
Returns true if the information was used (and the sub-item min size was updated).

View File

@ -345,6 +345,12 @@ public:
wxPoint GetPosition() const
{ return m_pos; }
// Called once the first component of an item has been decided. This is
// used in algorithms that depend on knowing the size in one direction
// before the min size in the other direction can be known.
// Returns true if it made use of the information (and min size was changed).
bool InformFirstDirection( int direction, int size, int availableOtherDir=-1 );
// these functions delete the current contents of the item if it's a sizer
// or a spacer but not if it is a window
void AssignWindow(wxWindow *window)
@ -551,6 +557,11 @@ public:
virtual void Clear( bool delete_windows = false );
virtual void DeleteWindows();
// Inform sizer about the first direction that has been decided (by parent item)
// Returns true if it made use of the informtion (and recalculated min size)
virtual bool InformFirstDirection( int WXUNUSED(direction), int WXUNUSED(size), int WXUNUSED(availableOtherDir) )
{ return false; }
void SetMinSize( int width, int height )
{ DoSetMinSize( width, height ); }
void SetMinSize( const wxSize& size )
@ -748,6 +759,7 @@ public:
protected:
void AdjustForFlexDirection();
void AdjustForGrowables(const wxSize& sz);
void FindWidthsAndHeights(int nrows, int ncols);
// the heights/widths of all rows/columns
wxArrayInt m_rowHeights,
@ -860,6 +872,37 @@ private:
DECLARE_CLASS(wxBoxSizer)
};
//---------------------------------------------------------------------------
// wxWrapSizer - A box sizer that can wrap items on several lines when
// widths exceed available width.
//---------------------------------------------------------------------------
// Borrow unused flag value
#define wxEXTEND_LAST_ON_EACH_LINE wxFULL_REPAINT_ON_RESIZE
class WXDLLEXPORT wxWrapSizer: public wxBoxSizer
{
public:
wxWrapSizer( int orient=wxHORIZONTAL, int flags=wxEXTEND_LAST_ON_EACH_LINE );
virtual ~wxWrapSizer();
virtual void RecalcSizes();
virtual wxSize CalcMin();
virtual bool InformFirstDirection( int direction, int size, int availableOtherDir );
protected:
int m_prim_size_last; // Size in primary direction last time
int m_n_line; // Number of lines
wxBoxSizer m_rows; // Rows of items
int m_flags;
void AdjustPropLastItem(wxSizer *psz, wxSizerItem *itemLast);
private:
DECLARE_DYNAMIC_CLASS(wxWrapSizer);
};
//---------------------------------------------------------------------------
// wxStaticBoxSizer
//---------------------------------------------------------------------------

View File

@ -500,6 +500,14 @@ public:
// components of the result respectively
virtual wxSize GetWindowBorderSize() const;
// wxSizer and friends use this to give a chance to a component to recalc
// its min size once one of the final size components is known. Override
// this function when that is useful (such as for wxStaticText which can
// stretch over several lines). Parameter availableOtherDir
// tells the item how much more space there is available in the opposite
// direction (-1 if unknown).
virtual bool InformFirstDirection( int WXUNUSED(direction), int WXUNUSED(size), int WXUNUSED(availableOtherDir) )
{ return false; }
// window state
// ------------

View File

@ -66,6 +66,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(LAYOUT_TEST_GB_SIZER, MyFrame::TestGridBagSizer)
EVT_MENU(LAYOUT_TEST_SET_MINIMAL, MyFrame::TestSetMinimal)
EVT_MENU(LAYOUT_TEST_NESTED, MyFrame::TestNested)
EVT_MENU(LAYOUT_TEST_WRAP, MyFrame::TestWrap)
END_EVENT_TABLE()
// Define my frame constructor
@ -83,6 +84,7 @@ MyFrame::MyFrame()
file_menu->Append(LAYOUT_TEST_GB_SIZER, _T("Test &gridbag sizer...\tF4"));
file_menu->Append(LAYOUT_TEST_SET_MINIMAL, _T("Test Set&ItemMinSize...\tF5"));
file_menu->Append(LAYOUT_TEST_NESTED, _T("Test nested sizer in a wxPanel...\tF6"));
file_menu->Append(LAYOUT_TEST_WRAP, _T("Test wrap sizers...\tF6"));
file_menu->AppendSeparator();
file_menu->Append(LAYOUT_QUIT, _T("E&xit"), _T("Quit program"));
@ -211,6 +213,12 @@ void MyFrame::TestNested(wxCommandEvent& WXUNUSED(event) )
newFrame->Show(true);
}
void MyFrame::TestWrap(wxCommandEvent& WXUNUSED(event) )
{
MyWrapSizerFrame *newFrame = new MyWrapSizerFrame(_T("Wrap Sizer Test Frame"), 50, 50);
newFrame->Show(true);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
{
@ -306,7 +314,7 @@ void MyFlexSizerFrame::InitFlexSizer(wxFlexGridSizer *sizer, wxWindow* parent)
}
}
MyFlexSizerFrame::MyFlexSizerFrame(const wxChar *title, int x, int y )
MyFlexSizerFrame::MyFlexSizerFrame(const wxString &title, int x, int y )
: wxFrame(NULL, wxID_ANY, title, wxPoint(x, y) )
{
wxFlexGridSizer *sizerFlex;
@ -387,7 +395,7 @@ MyFlexSizerFrame::MyFlexSizerFrame(const wxChar *title, int x, int y )
// MySizerDialog
// ----------------------------------------------------------------------------
MySizerDialog::MySizerDialog(wxWindow *parent, const wxChar *title)
MySizerDialog::MySizerDialog(wxWindow *parent, const wxString &title)
: wxDialog(parent, wxID_ANY, wxString(title))
{
// Begin with first hierarchy: a notebook at the top and
@ -461,7 +469,7 @@ BEGIN_EVENT_TABLE(MyGridBagSizerFrame, wxFrame)
END_EVENT_TABLE()
MyGridBagSizerFrame::MyGridBagSizerFrame(const wxChar *title, int x, int y )
MyGridBagSizerFrame::MyGridBagSizerFrame(const wxString &title, int x, int y )
: wxFrame( NULL, wxID_ANY, title, wxPoint(x, y) )
{
wxPanel* p = new wxPanel(this, wxID_ANY);
@ -575,7 +583,7 @@ BEGIN_EVENT_TABLE(MySimpleSizerFrame, wxFrame)
EVT_MENU( ID_SET_BIG, MySimpleSizerFrame::OnSetBigSize)
END_EVENT_TABLE()
MySimpleSizerFrame::MySimpleSizerFrame(const wxChar *title, int x, int y )
MySimpleSizerFrame::MySimpleSizerFrame(const wxString &title, int x, int y )
: wxFrame( NULL, wxID_ANY, title, wxPoint(x, y) )
{
wxMenu *menu = new wxMenu;
@ -621,7 +629,7 @@ void MySimpleSizerFrame::OnSetBigSize( wxCommandEvent& WXUNUSED(event))
// ----------------------------------------------------------------------------
MyNestedSizerFrame::MyNestedSizerFrame(const wxChar *title, int x, int y )
MyNestedSizerFrame::MyNestedSizerFrame(const wxString &title, int x, int y )
: wxFrame( NULL, wxID_ANY, title, wxPoint(x, y) )
{
wxMenu *menu = new wxMenu;
@ -661,3 +669,47 @@ MyNestedSizerFrame::MyNestedSizerFrame(const wxChar *title, int x, int y )
GetSizer()->SetSizeHints( this );
}
// ----------------------------------------------------------------------------
// MyWrapSizerFrame
// ----------------------------------------------------------------------------
MyWrapSizerFrame::MyWrapSizerFrame(const wxString &title, int x, int y )
: wxFrame( NULL, wxID_ANY, title, wxPoint(x, y) )
{
wxMenu *menu = new wxMenu;
menu->Append(wxID_ABOUT, "Do nothing");
wxMenuBar *menu_bar = new wxMenuBar;
menu_bar->Append(menu, "&File");
SetMenuBar( menu_bar );
wxBoxSizer *root = new wxBoxSizer( wxVERTICAL );
// A number of checkboxes inside a wrap sizer
wxSizer *ps_mid = new wxStaticBoxSizer( wxVERTICAL, this, "Wrapping check-boxes" );
wxSizer *ps_mid_wrap = new wxWrapSizer(wxHORIZONTAL);
ps_mid->Add( ps_mid_wrap, 100, wxEXPAND );
for( int ix=0; ix<6; ix++ )
ps_mid_wrap->Add( new wxCheckBox(this,wxID_ANY,wxString::Format("Option %d",ix+1)), 0, wxALIGN_CENTRE|wxALIGN_CENTER_VERTICAL, 5 );
root->Add( ps_mid, 0, wxEXPAND | wxALL, 5 );
// A shaped item inside a box sizer
wxSizer *ps_bottom = new wxStaticBoxSizer( wxVERTICAL, this, "With wxSHAPED item" );
wxSizer *ps_bottom_box = new wxBoxSizer(wxHORIZONTAL);
ps_bottom->Add( ps_bottom_box, 100, wxEXPAND );
ps_bottom_box->Add( new wxListBox(this,wxID_ANY,wxPoint(0,0),wxSize(70,70)), 0, wxEXPAND|wxSHAPED );
ps_bottom_box->Add( 10,10 );
ps_bottom_box->Add( new wxCheckBox(this,wxID_ANY,"A much longer option..."), 100, 0, 5 );
root->Add( ps_bottom, 1, wxEXPAND | wxALL, 5 );
// Set sizer for window
SetSizer( root );
root->Fit( this );
root->SetSizeHints( this );
}

View File

@ -29,6 +29,7 @@ public:
void TestGridBagSizer(wxCommandEvent& event);
void TestNested(wxCommandEvent& event);
void TestSetMinimal(wxCommandEvent& event);
void TestWrap(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
@ -57,7 +58,7 @@ protected:
class MyFlexSizerFrame : public wxFrame
{
public:
MyFlexSizerFrame(const wxChar *title, int x, int y );
MyFlexSizerFrame(const wxString &title, int x, int y );
private:
void InitFlexSizer(wxFlexGridSizer *sizer, wxWindow* parent);
@ -68,7 +69,7 @@ private:
class MySizerDialog : public wxDialog
{
public:
MySizerDialog(wxWindow *parent, const wxChar *title);
MySizerDialog(wxWindow *parent, const wxString &title );
};
@ -76,7 +77,7 @@ public:
class MyGridBagSizerFrame : public wxFrame
{
public:
MyGridBagSizerFrame(const wxChar *title, int x, int y );
MyGridBagSizerFrame(const wxString &title, int x, int y );
void OnHideBtn(wxCommandEvent&);
void OnShowBtn(wxCommandEvent&);
@ -101,7 +102,7 @@ private:
class MySimpleSizerFrame : public wxFrame
{
public:
MySimpleSizerFrame(const wxChar *title, int x, int y );
MySimpleSizerFrame(const wxString &title, int x, int y );
void OnSetSmallSize( wxCommandEvent &event);
void OnSetBigSize( wxCommandEvent &event);
@ -119,13 +120,21 @@ private:
class MyNestedSizerFrame : public wxFrame
{
public:
MyNestedSizerFrame(const wxChar *title, int x, int y );
MyNestedSizerFrame(const wxString &title, int x, int y );
private:
wxTextCtrl *m_target;
};
// a frame with several wrapping sizers
class MyWrapSizerFrame: public wxFrame
{
public:
MyWrapSizerFrame(const wxString &title, int x, int y );
};
// controls and menu constants
enum
{
@ -135,6 +144,7 @@ enum
LAYOUT_TEST_PROPORTIONS,
LAYOUT_TEST_SET_MINIMAL,
LAYOUT_TEST_NESTED,
LAYOUT_TEST_WRAP,
LAYOUT_QUIT = wxID_EXIT,
LAYOUT_ABOUT = wxID_ABOUT
};

View File

@ -272,6 +272,73 @@ wxSize wxSizerItem::GetSize() const
return ret;
}
bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOtherDir)
{
// The size that come here will be including borders. Child items should get it
// without borders.
if( size>0 )
{
if( direction==wxHORIZONTAL )
{
if (m_flag & wxWEST)
size -= m_border;
if (m_flag & wxEAST)
size -= m_border;
}
else if( direction==wxVERTICAL )
{
if (m_flag & wxNORTH)
size -= m_border;
if (m_flag & wxSOUTH)
size -= m_border;
}
}
bool didUse = false;
// Pass the information along to the held object
if (IsSizer())
{
didUse = GetSizer()->InformFirstDirection(direction,size,availableOtherDir);
if (didUse)
m_minSize = GetSizer()->CalcMin();
}
else if (IsWindow())
{
didUse = GetWindow()->InformFirstDirection(direction,size,availableOtherDir);
if (didUse)
m_minSize = m_window->GetEffectiveMinSize();
// This information is useful for items with wxSHAPED flag, since
// we can request an optimal min size for such an item. Even if
// we overwrite the m_minSize member here, we can read it back from
// the owned window (happens automatically).
if( (m_flag & wxSHAPED) && (m_flag & wxEXPAND) && direction )
{
if( !wxIsNullDouble(m_ratio) )
{
wxCHECK_MSG( (m_proportion==0), false, _T("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
if( direction==wxHORIZONTAL && !wxIsNullDouble(m_ratio) )
{
// Clip size so that we don't take too much
if( availableOtherDir>=0 && int(size/m_ratio)-m_minSize.y>availableOtherDir )
size = int((availableOtherDir+m_minSize.y)*m_ratio);
m_minSize = wxSize(size,int(size/m_ratio));
}
else if( direction==wxVERTICAL )
{
// Clip size so that we don't take too much
if( availableOtherDir>=0 && int(size*m_ratio)-m_minSize.x>availableOtherDir )
size = int((availableOtherDir+m_minSize.x)/m_ratio);
m_minSize = wxSize(int(size*m_ratio),size);
}
didUse = true;
}
}
}
return didUse;
}
wxSize wxSizerItem::CalcMin()
{
if (IsSizer())
@ -1314,6 +1381,35 @@ wxSize wxGridSizer::CalcMin()
node = node->GetNext();
}
// In case we have a nested sizer with a two step algo , give it
// a chance to adjust to that (we give it width component)
node = m_children.GetFirst();
bool didChangeMinSize = false;
while (node)
{
wxSizerItem *item = node->GetData();
didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 );
node = node->GetNext();
}
// And redo iteration in case min size changed
if( didChangeMinSize )
{
node = m_children.GetFirst();
w = h = 0;
while (node)
{
wxSizerItem *item = node->GetData();
wxSize sz( item->GetMinSizeWithBorder() );
w = wxMax( w, sz.x );
h = wxMax( h, sz.y );
node = node->GetNext();
}
}
return wxSize( ncols * w + (ncols-1) * m_hgap,
nrows * h + (nrows-1) * m_vgap );
}
@ -1456,6 +1552,45 @@ static int SumArraySizes(const wxArrayInt& sizes, int gap)
return total;
}
void wxFlexGridSizer::FindWidthsAndHeights(int nrows, int ncols)
{
// 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.
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 )
{
wxSizerItem * const item = *i;
if ( item->IsShown() )
{
// NOTE: Not doing the calculation here, this is just
// for finding max values.
const wxSize sz(item->GetMinSizeWithBorder());
const int row = n / ncols;
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;
}
}
AdjustForFlexDirection();
m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
SumArraySizes(m_rowHeights, m_vgap));
}
wxSize wxFlexGridSizer::CalcMin()
{
int nrows,
@ -1483,22 +1618,13 @@ wxSize wxFlexGridSizer::CalcMin()
wxSizerItem * const item = *i;
if ( item->IsShown() )
{
const wxSize sz(item->CalcMin());
const int row = n / ncols;
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;
item->CalcMin();
}
}
AdjustForFlexDirection();
m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
SumArraySizes(m_rowHeights, m_vgap));
// The stage of looking for max values in each row/column has been
// made a separate function, since it's reused in AdjustForGrowables.
FindWidthsAndHeights(nrows,ncols);
return m_calculatedMinSize;
}
@ -1616,6 +1742,47 @@ DoAdjustForGrowables(int delta,
void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
{
if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
{
DoAdjustForGrowables
(
sz.x - m_calculatedMinSize.x,
m_growableCols,
m_colWidths,
m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
: NULL
);
// This gives nested objects that benefit from knowing one size
// component in advance the chance to use that.
bool didAdjustMinSize = false;
int nrows, ncols;
CalcRowsCols(nrows, ncols);
// Iterate over all items and inform about column width
size_t n = 0;
for ( wxSizerItemList::iterator i = m_children.begin();
i != m_children.end();
++i, ++n )
{
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
);
}
}
if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
{
// pass NULL instead of proportions if the grow mode is ALL as we
@ -1629,18 +1796,6 @@ void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
: NULL
);
}
if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
{
DoAdjustForGrowables
(
sz.x - m_calculatedMinSize.x,
m_growableCols,
m_colWidths,
m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
: NULL
);
}
}
@ -1693,15 +1848,47 @@ void wxBoxSizer::RecalcSizes()
if ( m_children.empty() )
return;
const wxCoord totalMinorSize = SizeInMinorDir(m_size);
// the amount of free space which we should redistribute among the
// stretchable items (i.e. those with non zero proportion)
int delta = SizeInMajorDir(m_size) - SizeInMajorDir(m_minSize);
// Inform child items about the size in minor direction, that can
// change how much free space we have in major dir and how to distribute it.
int majorMinSum = 0;
for ( wxSizerItemList::const_iterator i = m_children.begin();
i != m_children.end();
++i )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
wxSize szMinPrev = item->GetMinSizeWithBorder();
item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta);
wxSize szMin = item->GetMinSizeWithBorder();
int deltaChange = SizeInMajorDir(szMin-szMinPrev);
if( deltaChange )
{
// Since we passed available space along to the item, it should not
// take too much ,so delat should not become negative.
delta -= deltaChange;
}
majorMinSum += SizeInMajorDir(item->GetMinSizeWithBorder());
}
// And update our min size
SizeInMajorDir(m_minSize) = majorMinSum;
// might have a new delta now
delta = SizeInMajorDir(m_size) - SizeInMajorDir(m_minSize);
// the position at which we put the next child
wxPoint pt(m_position);
const wxCoord totalMinorSize = SizeInMinorDir(m_size);
int totalProportion = m_totalProportion;
for ( wxSizerItemList::const_iterator i = m_children.begin();
i != m_children.end();
@ -1711,6 +1898,7 @@ void wxBoxSizer::RecalcSizes()
if ( !item->IsShown() )
continue;
#ifndef __DMC__
// DMC doesn't distinguish between
// int SizeInMajorDir(const wxSize& sz) const
@ -1719,7 +1907,6 @@ void wxBoxSizer::RecalcSizes()
#endif
wxSize sizeThis(item->GetMinSizeWithBorder());
// adjust the size in the major direction using the proportion
wxCoord majorSize = SizeInMajorDir(sizeThis);
const int propItem = item->GetProportion();
@ -1802,6 +1989,224 @@ 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 = SizeInMajorDir(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 = SizeInMajorDir(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 = SizeInMajorDir(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 = SizeInMajorDir(minSz);
int szMinor = SizeInMinorDir(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
//---------------------------------------------------------------------------