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:
parent
a0b5751160
commit
15f7c30516
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
53
docs/latex/wx/wrapsizer.tex
Normal file
53
docs/latex/wx/wrapsizer.tex
Normal 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).
|
||||
|
||||
|
||||
|
@ -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
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
// ------------
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
//---------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user