// Modified by:
// Created: 04/01/98
// RCS-ID: $Id$
-// Copyright: (c) Julian Smart and Markus Holzem
-// Licence: wxWindows license
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// =============================================================================
// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "layout.h"
#endif
#include "wx/layout.h"
-#if !USE_SHARED_LIBRARY
IMPLEMENT_DYNAMIC_CLASS(wxIndividualLayoutConstraint, wxObject)
IMPLEMENT_DYNAMIC_CLASS(wxLayoutConstraints, wxObject)
- IMPLEMENT_DYNAMIC_CLASS(wxSizer, wxObject)
- IMPLEMENT_DYNAMIC_CLASS(wxRowColSizer, wxSizer)
- IMPLEMENT_DYNAMIC_CLASS(wxSpacingSizer, wxSizer)
-#endif
-
-/*
-TODO:
- - Non shrink-to-fit row-col behaviour.
- - Give justification styles, so can e.g. centre
- the rows & cols, distribute the available space...
- - Shrink-to-fit: should resize outer window (e.g. dialog box)
- if directly associated with this kind of window.
- - How to deal with a rowcol that stretches in one direction
- but shrinks-to-fit in other. E.g. a horizontal toolbar: the width
- stretches to fit the frame, but the height is constant
- or wraps around contents. The algorithm currently assumes
- both dimensions have the same behaviour. Could assume a constant
- height (absolute value).
- - rowcol where each row or column is aligned (length of
- largest element determines spacing)
- - Groupbox sizer
- - Analyze aesthetic dialog boxes and implement using sizers.
- - What reuseable components can we provide? E.g. Ok/Cancel/Help
- group of buttons.
- - use wxStaticItems for aesthetic dialogs.
-
-*/
-
-// Find margin sizes if a sizer, or zero otherwise
-int wxSizerMarginX(wxWindowBase *win)
-{
- if ( win->IsKindOf(CLASSINFO(wxSizer)) )
- {
- wxSizer *sizer = (wxSizer *)win;
- return sizer->GetBorderX();
- }
- else
- return 0;
-}
-
-int wxSizerMarginY(wxWindowBase *win)
-{
- if ( win->IsKindOf(CLASSINFO(wxSizer)) )
- {
- wxSizer *sizer = (wxSizer *)win;
- return sizer->GetBorderY();
- }
- else
- return 0;
-}
wxIndividualLayoutConstraint::wxIndividualLayoutConstraint()
void wxIndividualLayoutConstraint::Set(wxRelationship rel, wxWindowBase *otherW, wxEdge otherE, int val, int marg)
{
- relationship = rel;
- otherWin = otherW;
- otherEdge = otherE;
- value = val;
- margin = marg;
+ if (rel == wxSameAs)
+ {
+ // If Set is called by the user with wxSameAs then call SameAs to do
+ // it since it will actually use wxPercent instead.
+ SameAs(otherW, otherE, marg);
+ return;
+ }
+
+ relationship = rel;
+ otherWin = otherW;
+ otherEdge = otherE;
+
+ if ( rel == wxPercentOf )
+ {
+ percent = val;
+ }
+ else
+ {
+ value = val;
+ }
+
+ margin = marg;
}
void wxIndividualLayoutConstraint::LeftOf(wxWindowBase *sibling, int marg)
// 'Same edge' alignment
//
void wxIndividualLayoutConstraint::SameAs(wxWindowBase *otherW, wxEdge edge, int marg)
-{
- Set(wxPercentOf, otherW, edge, 0, marg);
- percent = 100;
+{
+ Set(wxPercentOf, otherW, edge, 100, marg);
}
// The edge is a percentage of the other window's edge
void wxIndividualLayoutConstraint::PercentOf(wxWindowBase *otherW, wxEdge wh, int per)
-{
- otherWin = otherW;
- relationship = wxPercentOf;
- percent = per;
-
- otherEdge = wh;
+{
+ Set(wxPercentOf, otherW, wh, per);
}
//
//
void wxIndividualLayoutConstraint::Absolute(int val)
{
- value = val; relationship = wxAbsolute;
+ value = val;
+ relationship = wxAbsolute;
}
// Reset constraint if it mentions otherWin
otherWin = (wxWindowBase *) NULL;
return TRUE;
}
- else
- return FALSE;
+
+ return FALSE;
}
// Try to satisfy constraint
// If the edge or dimension belongs to the parent, then we know the
// dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
// button (but the button's true parent is a panel, not the sizer)
- if (other->GetChildren().Find(thisWin))
+ if (other->GetChildren().Find((wxWindow*)thisWin))
{
switch (which)
{
case wxLeft:
{
- return wxSizerMarginX(other);
+ return 0;
}
case wxTop:
{
- return wxSizerMarginY(other);
+ return 0;
}
case wxRight:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
- return w - wxSizerMarginX(other);
+ return w;
}
case wxBottom:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
- return h - wxSizerMarginY(other);
+ return h;
}
case wxWidth:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
- return w - 2*wxSizerMarginX(other);
+ return w;
}
case wxHeight:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
- return h - 2*wxSizerMarginY(other);
+ return h;
}
case wxCentreX:
case wxCentreY:
return AreSatisfied();
}
-/*
- * Main constrained layout algorithm. Look at all the child
- * windows, and their constraints (if any).
- * The idea is to keep iterating through the constraints
- * until all left, right, bottom and top edges, and widths and heights,
- * are known (or no change occurs and we've failed to resolve all
- * constraints).
- *
- * If the user has not specified a dimension or edge, it will be
- * be calculated from the other known values. E.g. If we know
- * the right hand edge and the left hand edge, we now know the width.
- * The snag here is that this means we must specify absolute dimensions
- * twice (in constructor and in constraint), if we wish to use the
- * constraint notation to just set the position, for example.
- * Otherwise, if we only set ONE edge and no dimension, it would never
- * find the other edge.
- *
- * Algorithm:
-
- Mark all constraints as not done.
-
- iterations = 0;
- until no change or iterations >= max iterations
- For each child:
- {
- Calculate all constraints
- }
- iterations ++;
-
- For each child
- Set each calculated position and size
-
- */
-
-#if WXWIN_COMPATIBILITY
-bool wxOldDoLayout(wxWindowBase *win)
-{
- // Make sure this isn't called recursively from below
- static wxList doneSoFar;
-
- if (doneSoFar.Member(win))
- return TRUE;
-
- doneSoFar.Append(win);
-
- wxNode *node = win->GetChildren().First();
- while (node)
- {
- wxWindowBase *child = (wxWindowBase *)node->Data();
- wxLayoutConstraints *constr = child->GetConstraints();
- if (constr)
- {
- constr->left.SetDone(FALSE);
- constr->top.SetDone(FALSE);
- constr->right.SetDone(FALSE);
- constr->bottom.SetDone(FALSE);
- constr->width.SetDone(FALSE);
- constr->height.SetDone(FALSE);
- constr->centreX.SetDone(FALSE);
- constr->centreY.SetDone(FALSE);
- }
- node = node->Next();
- }
- int noIterations = 0;
- int maxIterations = 500;
- int noChanges = 1;
-
- while ((noChanges > 0) && (noIterations < maxIterations))
- {
- noChanges = 0;
- wxNode *node = win->GetChildren().First();
- while (node)
- {
- wxWindowBase *child = (wxWindowBase *)node->Data();
- wxLayoutConstraints *constr = child->GetConstraints();
- if (constr)
- {
- int tempNoChanges = 0;
- (void)constr->SatisfyConstraints(child, &tempNoChanges);
- noChanges += tempNoChanges;
- }
- node = node->Next();
- }
- noIterations ++;
- }
-/*
- // Would be nice to have a test here to see _which_ constraint(s)
- // failed, so we can print a specific diagnostic message.
- if (noFailures > 0)
- {
- wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
- }
-*/
- // Now set the sizes and positions of the children, and
- // recursively call Layout().
- node = win->GetChildren().First();
- while (node)
- {
- wxWindowBase *child = (wxWindowBase *)node->Data();
- wxLayoutConstraints *constr = child->GetConstraints();
- if (constr && constr->left.GetDone() && constr->right.GetDone() &&
- constr->width.GetDone() && constr->height.GetDone())
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- int w = constr->width.GetValue();
- int h = constr->height.GetValue();
-
- // If we don't want to resize this window, just move it...
- if ((constr->width.GetRelationship() != wxAsIs) ||
- (constr->height.GetRelationship() != wxAsIs))
- {
- // _Should_ call Layout() recursively.
- child->SetSize(x, y, w, h);
- }
- else
- {
- child->Move(x, y);
- }
- }
- else
- child->Layout();
- node = node->Next();
- }
- doneSoFar.DeleteObject(win);
-
- return TRUE;
-}
-#endif // WXWIN_COMPATIBILITY
-
-wxSizer::wxSizer()
-{
- sizerBehaviour = wxSizerNone;
- borderX = 2;
- borderY = 2;
- sizerX = 0;
- sizerY = 0;
- sizerWidth = 0;
- sizerHeight = 0;
-}
-
-wxSizer::wxSizer(wxWindowBase *parent, wxSizerBehaviour behav)
-{
- Create(parent, behav);
-}
-
-bool wxSizer::Create(wxWindowBase *parent, wxSizerBehaviour behav)
-{
- sizerBehaviour = behav;
- borderX = 2;
- borderY = 2;
- m_sizerParent = parent;
- sizerX = 0;
- sizerY = 0;
- sizerWidth = 0;
- sizerHeight = 0;
-
- // A normal window can have just one top-level sizer
- // associated with it.
- if (!parent->IsKindOf(CLASSINFO(wxSizer)))
- {
- parent->SetSizer(this);
- }
- else
- ((wxSizer *)parent)->AddSizerChild(this);
-
- switch (sizerBehaviour)
- {
- case wxSizerExpand:
- {
- // Defines a set of constraints
- // to expand the sizer to fit the parent window
- wxLayoutConstraints *c = new wxLayoutConstraints;
-
- c->left.SameAs(parent, wxLeft, 0);
- c->top.SameAs(parent, wxTop, 0);
- c->right.SameAs(parent, wxRight, 0);
- c->bottom.SameAs(parent, wxBottom, 0);
-
- SetConstraints(c);
- break;
- }
- case wxSizerShrink:
- case wxSizerNone:
- default:
- {
- }
- }
- return TRUE;
-}
-
-wxSizer::~wxSizer()
-{
- // Remove all children without deleting them,
- // or ~wxbWindow will delete proper windows _twice_
- wxNode *node = GetChildren().First();
- while (node)
- {
- wxNode *next = node->Next();
- wxWindowBase *win = (wxWindowBase *)node->Data();
- if (!win->IsKindOf(CLASSINFO(wxSizer)))
- {
- delete node;
- win->SetSizerParent((wxWindowBase *) NULL);
- }
- else
- {
- RemoveSizerChild(win);
- delete win;
- }
- node = next;
- }
-
- if (m_sizerParent) // && !m_sizerParent->IsKindOf(CLASSINFO(wxSizer)))
- {
- m_sizerParent->SetSizer((wxSizer *) NULL);
- m_sizerParent = (wxWindowBase *) NULL;
- }
-
-}
-
-void wxSizer::SetBorder(int x, int y)
-{
- borderX = x;
- borderY = y;
- /* No: the margin is for inside, not outside (expansion)
-
- if ( GetConstraints() )
- {
- GetConstraints()->left.SetMargin(x);
- GetConstraints()->right.SetMargin(x);
- GetConstraints()->top.SetMargin(y);
- GetConstraints()->bottom.SetMargin(y);
- }
- */
-
-}
-
-void wxSizer::AddSizerChild(wxWindowBase *child)
-{
- child->SetSizerParent(this);
- GetChildren().Append(child);
-
- // Add some constraints for the purpose of storing
- // the relative position of the window/sizer
- // during layout calculations.
- if (!child->GetConstraints())
- {
- wxLayoutConstraints *c = new wxLayoutConstraints;
- c->left.AsIs();
- c->top.AsIs();
- c->width.AsIs();
- c->height.AsIs();
- int w, h;
- child->GetSize(&w, &h);
- c->width.SetValue(w);
- c->height.SetValue(h);
-
- child->SetConstraints(c);
- }
-}
-
-void wxSizer::RemoveSizerChild(wxWindowBase *child)
-{
- GetChildren().DeleteObject(child);
-}
-
-void wxSizer::DoSetSize(int x, int y, int w, int h, int WXUNUSED(flags))
-{
- wxLayoutConstraints *constr = GetConstraints();
- if (x != -1)
- {
- sizerX = x;
- if (constr)
- constr->left.SetValue(x);
- }
- if (y != -1)
- {
- sizerY = y;
- if (constr)
- constr->top.SetValue(y);
- }
- if (w != -1)
- {
- sizerWidth = w;
- if (constr)
- constr->width.SetValue(w);
- }
- if (h != -1)
- {
- sizerHeight = h;
- if (constr)
- constr->height.SetValue(h);
- }
-}
-
-void wxSizer::DoGetSize(int *w, int *h) const
-{
- *w = sizerWidth;
- *h = sizerHeight;
-}
-
-void wxSizer::DoGetPosition(int *x, int *y) const
-{
- *x = sizerX;
- *y = sizerY;
-}
-
-bool wxSizer::LayoutPhase1(int *noChanges)
-{
- *noChanges = 0;
- switch (sizerBehaviour)
- {
- case wxSizerExpand:
- {
- if (!m_sizerParent)
- {
- wxMessageBox(_("wxExpandSizer has no parent!"), _("Sizer error"), wxOK);
- return TRUE;
- }
-
- // Set the size to fill the parent client area
- int pw, ph;
- m_sizerParent->GetClientSize(&pw, &ph);
- SetSize(GetBorderX(), GetBorderY(), pw - 2*GetBorderX(), ph - 2*GetBorderY());
- wxLayoutConstraints *constr = GetConstraints();
-
- // Fill in the constraints
- if (constr)
- {
- constr->left.SetValue(0); constr->left.SetDone(TRUE);
- constr->top.SetValue(0); constr->right.SetDone(TRUE);
- constr->width.SetValue(pw); constr->width.SetDone(TRUE);
- constr->height.SetValue(ph); constr->height.SetDone(TRUE);
- }
-
- return TRUE;
- break;
- }
- case wxSizerShrink:
- {
- wxLayoutConstraints *constr = GetConstraints();
-
- if (constr)
- {
- // Force the constraint to have as-is width and height
- // if we're in shrink-to-fit mode, because if left unconstrained,
- // SatisfyConstraints will fail. The shrink-to-fit option
- // essentially specifies the width and height as 'whatever I calculate'.
- constr->width.AsIs();
- constr->height.AsIs();
- }
- DoPhase(1);
- DoPhase(2);
- // Find the bounding box and set own size
- int maxX = 0;
- int maxY = 0;
-
- wxNode *node = GetChildren().First();
- while (node)
- {
- int x, y, width, height;
- wxWindowBase *win = (wxWindowBase *)node->Data();
- win->GetSizeConstraint(&width, &height);
- win->GetPositionConstraint(&x, &y);
- if ((x+width) > maxX)
- maxX = (x + width);
- if ((y+height) > maxY)
- maxY = (y + height);
-
- node = node->Next();
- }
- SetSize(GetBorderX(), GetBorderY(), maxX, maxY);
-
- // If this is the only sizer for the parent, size the parent to this sizer.
- if ( m_sizerParent && (m_sizerParent->GetSizer() == this) )
- m_sizerParent->SetClientSize(maxX + 2*GetBorderX(), maxY + 2*GetBorderY());
-
- return TRUE;
- break;
- }
- case wxSizerNone:
- {
- wxLayoutConstraints *constr = GetConstraints();
- if (constr)
- {
- bool success = constr->SatisfyConstraints(this, noChanges);
- if (success)
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- int w = constr->width.GetValue();
- int h = constr->height.GetValue();
- SetSize(x, y, w, h);
- }
- return success;
- }
- else
- return TRUE;
- break;
- }
- }
- return TRUE;
-
-}
-
-bool wxSizer::LayoutPhase2(int *noChanges)
-{
- *noChanges = 0;
-
- switch (sizerBehaviour)
- {
- case wxSizerExpand:
- {
- // Layout children
- DoPhase(1);
- DoPhase(2);
- return TRUE;
- }
- case wxSizerShrink:
- {
- wxLayoutConstraints *constr = GetConstraints();
- if (constr)
- {
- bool success = constr->SatisfyConstraints(this, noChanges);
- if (success)
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- Move(x, y);
- }
- return success;
- }
- break;
- }
- case wxSizerNone:
- {
- // Layout children
- DoPhase(1);
- DoPhase(2);
-
- // Is this a dumb fix for lack of constraint evaluation?
- wxLayoutConstraints *constr = GetConstraints();
- if (constr)
- {
- bool success = constr->SatisfyConstraints(this, noChanges);
- if (success)
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- int w = constr->width.GetValue();
- int h = constr->height.GetValue();
- SetSize(x, y, w, h);
- }
- return success;
- }
- else
- return TRUE;
- }
- }
- return TRUE;
-}
-
-/*
- * wxRowColSizer
- */
-
-wxRowColSizer::wxRowColSizer()
-{
- rowOrCol = TRUE;
- rowOrColSize = 20;
- xSpacing = 2;
- ySpacing = 2;
-}
-
-wxRowColSizer::wxRowColSizer(wxWindowBase *parent, bool rc, int n, wxSizerBehaviour behav)
-{
- Create(parent, rc, n, behav);
-}
-
-bool wxRowColSizer::Create(wxWindowBase *parent, bool rc, int n, wxSizerBehaviour behav)
-{
- wxSizer::Create(parent, behav);
-
- rowOrCol = rc;
- rowOrColSize = n;
- xSpacing = 2;
- ySpacing = 2;
-
- return TRUE;
-}
-
-wxRowColSizer::~wxRowColSizer()
-{
-}
-
-bool wxRowColSizer::LayoutPhase1(int *noChanges)
-{
- *noChanges = 0;
- wxLayoutConstraints *constr = GetConstraints();
-
- if (constr)
- {
- // Force the constraint to have as-is width and height
- // if we're in shrink-to-fit mode, because if left unconstrained,
- // SatisfyConstraints will fail. The shrink-to-fit option
- // essentially specifies the width and height as 'whatever I calculate'.
- if (sizerBehaviour == wxSizerShrink)
- {
- constr->width.AsIs();
- constr->height.AsIs();
- }
-
- // Only evaluate the constraints FIRST if we're NOT
- // in shrink-to-fit mode, i.e. we want to size the rowcol
- // first, then lay the children out in the space we've calculated.
- if (sizerBehaviour != wxSizerShrink)
- {
- bool success = constr->SatisfyConstraints(this, noChanges);
- if (success)
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- int w = constr->width.GetValue();
- int h = constr->height.GetValue();
- SetSize(x, y, w, h);
- }
- else
- return FALSE;
-
- // Continue to do the rest of the phase when the constraints have been
- // satisfied, i.e. we're on the last iteration of phase 1 and
- // can now do the actual rowcol laying out.
- }
- }
-
- // If we ARE in shrink-to-fit mode, we must now
- // calculate the child sizes BEFORE laying out in rows or columns.
- if (sizerBehaviour == wxSizerShrink)
- {
- DoPhase(1);
- DoPhase(2);
-
- // WILL THE WINDOW BE SIZED CORRECTLY AT THIS POINT?
- // CHECK CONSTRAINTS IF ANY...
- int noRows = 0;
- int noCols = 0;
- int currentX = borderX;
- int currentY = borderY;
- int maxX = currentX;
- int maxY = currentY;
-
- wxNode *node = GetChildren().First();
- while (node)
- {
- wxWindowBase *win = (wxWindowBase *)node->Data();
- int childWidth, childHeight;
- if (win->GetConstraints() &&
- win->GetConstraints()->width.GetDone() &&
- win->GetConstraints()->height.GetDone())
- {
- childWidth = win->GetConstraints()->width.GetValue();
- childHeight = win->GetConstraints()->height.GetValue();
- }
- else
- win->GetSize(&childWidth, &childHeight);
-
- win->MoveConstraint(currentX, currentY);
-
- if ((currentX + childWidth) > maxX)
- maxX = (currentX + childWidth);
- if ((currentY + childHeight) > maxY)
- maxY = (currentY + childHeight);
-
- if (rowOrCol)
- {
- currentX += childWidth + xSpacing;
- noCols ++;
-
- // Reset to start of row
- if (noCols == rowOrColSize)
- {
- currentX = borderX;
- currentY += childHeight + ySpacing;
- noCols = 0;
- }
- }
- else
- {
- currentY += childHeight + ySpacing;
- noRows ++;
-
- // Reset to start of col
- if (noRows == rowOrColSize)
- {
- currentY = borderY;
- currentX += childWidth + xSpacing;
- noRows = 0;
- }
- }
-
- node = node->Next();
- }
- maxX += borderX;
- maxY += borderY;
-
- SetSize(-1, -1, maxX, maxY);
- }
- return TRUE;
-}
-
-bool wxRowColSizer::LayoutPhase2(int *noChanges)
-{
- *noChanges = 0;
-
- // If shrink-to-fit, it's only at Phase 2 that we know the size of
- // the wxRowColSizer, and now we can evaluate the
- // constraints and pass result back up to parent.
- // This implements a depth-first strategy
- if (sizerBehaviour == wxSizerShrink)
- {
- wxLayoutConstraints *constr = GetConstraints();
- if (constr)
- {
- bool success = constr->SatisfyConstraints(this, noChanges);
- if (success)
- {
- int x = constr->left.GetValue();
- int y = constr->top.GetValue();
- Move(x, y);
- }
- return success;
- }
- else return TRUE;
- }
- else
- {
- // Lay out the children: breadth-first strategy.
- DoPhase(1);
- DoPhase(2);
-
- // Space them
- }
- return TRUE;
-}
-
-
-/*
- * wxSpacingSizer
- */
-
-wxSpacingSizer::wxSpacingSizer()
-{
-}
-
-wxSpacingSizer::wxSpacingSizer(wxWindowBase *parent)
-{
- Create(parent);
-}
-
-wxSpacingSizer::wxSpacingSizer(wxWindowBase *parent, wxRelationship rel, wxWindowBase *other, int spacing)
-{
- Create(parent, rel, other, spacing);
-}
-
-bool wxSpacingSizer::Create(wxWindowBase *parent)
-{
- wxSizer::Create(parent);
- return TRUE;
-}
-
-bool wxSpacingSizer::Create(wxWindowBase *parent, wxRelationship rel, wxWindowBase *other, int spacing)
-{
- wxLayoutConstraints *c = new wxLayoutConstraints;
-
- wxSizer::Create(parent);
-
- switch ( rel )
- {
- case wxLeftOf :
- c->width.Absolute (spacing);
- c->top.SameAs (other, wxTop);
- c->bottom.SameAs (other, wxBottom);
- c->right.LeftOf (other);
- break;
- case wxRightOf :
- c->width.Absolute (spacing);
- c->top.SameAs (other, wxTop);
- c->bottom.SameAs (other, wxBottom);
- c->left.RightOf (other);
- break;
- case wxBelow :
- c->height.Absolute (spacing);
- c->left.SameAs (other, wxLeft);
- c->right.SameAs (other, wxRight);
- c->top.Below (other);
- break;
- case wxAbove :
- c->height.Absolute (spacing);
- c->left.SameAs (other, wxLeft);
- c->right.SameAs (other, wxRight);
- c->bottom.Above (other);
- break;
-
- default :
- break;
- }
- SetConstraints(c);
-
- return TRUE;
-}
-
-wxSpacingSizer::~wxSpacingSizer()
-{
-}
-
#endif // wxUSE_CONSTRAINTS