1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Constraint layout system classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // =============================================================================
14 // =============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "layout.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
38 #include "wx/window.h"
40 #include "wx/dialog.h"
41 #include "wx/msgdlg.h"
45 #include "wx/layout.h"
47 IMPLEMENT_DYNAMIC_CLASS(wxIndividualLayoutConstraint, wxObject)
48 IMPLEMENT_DYNAMIC_CLASS(wxLayoutConstraints, wxObject)
51 wxIndividualLayoutConstraint::wxIndividualLayoutConstraint()
54 relationship = wxUnconstrained;
60 otherWin = (wxWindowBase *) NULL;
63 wxIndividualLayoutConstraint::~wxIndividualLayoutConstraint()
67 void wxIndividualLayoutConstraint::Set(wxRelationship rel, wxWindowBase *otherW, wxEdge otherE, int val, int marg)
73 if ( rel == wxPercentOf )
85 void wxIndividualLayoutConstraint::LeftOf(wxWindowBase *sibling, int marg)
87 Set(wxLeftOf, sibling, wxLeft, 0, marg);
90 void wxIndividualLayoutConstraint::RightOf(wxWindowBase *sibling, int marg)
92 Set(wxRightOf, sibling, wxRight, 0, marg);
95 void wxIndividualLayoutConstraint::Above(wxWindowBase *sibling, int marg)
97 Set(wxAbove, sibling, wxTop, 0, marg);
100 void wxIndividualLayoutConstraint::Below(wxWindowBase *sibling, int marg)
102 Set(wxBelow, sibling, wxBottom, 0, marg);
106 // 'Same edge' alignment
108 void wxIndividualLayoutConstraint::SameAs(wxWindowBase *otherW, wxEdge edge, int marg)
110 Set(wxPercentOf, otherW, edge, 100, marg);
113 // The edge is a percentage of the other window's edge
114 void wxIndividualLayoutConstraint::PercentOf(wxWindowBase *otherW, wxEdge wh, int per)
116 Set(wxPercentOf, otherW, wh, per);
120 // Edge has absolute value
122 void wxIndividualLayoutConstraint::Absolute(int val)
125 relationship = wxAbsolute;
128 // Reset constraint if it mentions otherWin
129 bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase *otherW)
131 if (otherW == otherWin)
134 relationship = wxAsIs;
139 otherWin = (wxWindowBase *) NULL;
146 // Try to satisfy constraint
147 bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints *constraints, wxWindowBase *win)
149 if (relationship == wxAbsolute)
159 switch (relationship)
163 // We can know this edge if: otherWin is win's
164 // parent, or otherWin has a satisfied constraint,
165 // or otherWin has no constraint.
166 int edgePos = GetEdge(otherEdge, win, otherWin);
169 value = edgePos - margin;
178 int edgePos = GetEdge(otherEdge, win, otherWin);
181 value = edgePos + margin;
190 int edgePos = GetEdge(otherEdge, win, otherWin);
193 value = (int)(edgePos*(((float)percent)*0.01) + margin);
200 case wxUnconstrained:
202 // We know the left-hand edge position if we know
203 // the right-hand edge and we know the width; OR if
204 // we know the centre and the width.
205 if (constraints->right.GetDone() && constraints->width.GetDone())
207 value = (constraints->right.GetValue() - constraints->width.GetValue() + margin);
211 else if (constraints->centreX.GetDone() && constraints->width.GetDone())
213 value = (int)(constraints->centreX.GetValue() - (constraints->width.GetValue()/2) + margin);
223 win->GetPosition(&value, &y);
234 switch (relationship)
238 // We can know this edge if: otherWin is win's
239 // parent, or otherWin has a satisfied constraint,
240 // or otherWin has no constraint.
241 int edgePos = GetEdge(otherEdge, win, otherWin);
244 value = edgePos - margin;
253 int edgePos = GetEdge(otherEdge, win, otherWin);
256 value = edgePos + margin;
265 int edgePos = GetEdge(otherEdge, win, otherWin);
268 value = (int)(edgePos*(((float)percent)*0.01) - margin);
275 case wxUnconstrained:
277 // We know the right-hand edge position if we know the
278 // left-hand edge and we know the width, OR if we know the
279 // centre edge and the width.
280 if (constraints->left.GetDone() && constraints->width.GetDone())
282 value = (constraints->left.GetValue() + constraints->width.GetValue() - margin);
286 else if (constraints->centreX.GetDone() && constraints->width.GetDone())
288 value = (int)(constraints->centreX.GetValue() + (constraints->width.GetValue()/2) - margin);
299 win->GetSize(&w, &h);
300 win->GetPosition(&x, &y);
312 switch (relationship)
316 // We can know this edge if: otherWin is win's
317 // parent, or otherWin has a satisfied constraint,
318 // or otherWin has no constraint.
319 int edgePos = GetEdge(otherEdge, win, otherWin);
322 value = edgePos - margin;
331 int edgePos = GetEdge(otherEdge, win, otherWin);
334 value = edgePos + margin;
343 int edgePos = GetEdge(otherEdge, win, otherWin);
346 value = (int)(edgePos*(((float)percent)*0.01) + margin);
353 case wxUnconstrained:
355 // We know the top edge position if we know the bottom edge
356 // and we know the height; OR if we know the centre edge and
358 if (constraints->bottom.GetDone() && constraints->height.GetDone())
360 value = (constraints->bottom.GetValue() - constraints->height.GetValue() + margin);
364 else if (constraints->centreY.GetDone() && constraints->height.GetDone())
366 value = (constraints->centreY.GetValue() - (constraints->height.GetValue()/2) + margin);
376 win->GetPosition(&x, &value);
387 switch (relationship)
391 // We can know this edge if: otherWin is win's parent,
392 // or otherWin has a satisfied constraint, or
393 // otherWin has no constraint.
394 int edgePos = GetEdge(otherEdge, win, otherWin);
397 value = edgePos + margin;
406 int edgePos = GetEdge(otherEdge, win, otherWin);
409 value = edgePos - margin;
418 int edgePos = GetEdge(otherEdge, win, otherWin);
421 value = (int)(edgePos*(((float)percent)*0.01) - margin);
428 case wxUnconstrained:
430 // We know the bottom edge position if we know the top edge
431 // and we know the height; OR if we know the centre edge and
433 if (constraints->top.GetDone() && constraints->height.GetDone())
435 value = (constraints->top.GetValue() + constraints->height.GetValue() - margin);
439 else if (constraints->centreY.GetDone() && constraints->height.GetDone())
441 value = (constraints->centreY.GetValue() + (constraints->height.GetValue()/2) - margin);
452 win->GetSize(&w, &h);
453 win->GetPosition(&x, &y);
465 switch (relationship)
469 // We can know this edge if: otherWin is win's parent, or
470 // otherWin has a satisfied constraint, or otherWin has no
472 int edgePos = GetEdge(otherEdge, win, otherWin);
475 value = edgePos - margin;
484 int edgePos = GetEdge(otherEdge, win, otherWin);
487 value = edgePos + margin;
496 int edgePos = GetEdge(otherEdge, win, otherWin);
499 value = (int)(edgePos*(((float)percent)*0.01) + margin);
506 case wxUnconstrained:
508 // We know the centre position if we know
509 // the left-hand edge and we know the width, OR
510 // the right-hand edge and the width
511 if (constraints->left.GetDone() && constraints->width.GetDone())
513 value = (int)(constraints->left.GetValue() + (constraints->width.GetValue()/2) + margin);
517 else if (constraints->right.GetDone() && constraints->width.GetDone())
519 value = (int)(constraints->left.GetValue() - (constraints->width.GetValue()/2) + margin);
533 switch (relationship)
537 // We can know this edge if: otherWin is win's parent,
538 // or otherWin has a satisfied constraint, or otherWin
539 // has no constraint.
540 int edgePos = GetEdge(otherEdge, win, otherWin);
543 value = edgePos - margin;
552 int edgePos = GetEdge(otherEdge, win, otherWin);
555 value = edgePos + margin;
564 int edgePos = GetEdge(otherEdge, win, otherWin);
567 value = (int)(edgePos*(((float)percent)*0.01) + margin);
574 case wxUnconstrained:
576 // We know the centre position if we know
577 // the top edge and we know the height, OR
578 // the bottom edge and the height.
579 if (constraints->bottom.GetDone() && constraints->height.GetDone())
581 value = (int)(constraints->bottom.GetValue() - (constraints->height.GetValue()/2) + margin);
585 else if (constraints->top.GetDone() && constraints->height.GetDone())
587 value = (int)(constraints->top.GetValue() + (constraints->height.GetValue()/2) + margin);
601 switch (relationship)
605 int edgePos = GetEdge(otherEdge, win, otherWin);
608 value = (int)(edgePos*(((float)percent)*0.01));
620 win->GetSize(&value, &h);
626 case wxUnconstrained:
628 // We know the width if we know the left edge and the right edge, OR
629 // if we know the left edge and the centre, OR
630 // if we know the right edge and the centre
631 if (constraints->left.GetDone() && constraints->right.GetDone())
633 value = constraints->right.GetValue() - constraints->left.GetValue();
637 else if (constraints->centreX.GetDone() && constraints->left.GetDone())
639 value = (int)(2*(constraints->centreX.GetValue() - constraints->left.GetValue()));
643 else if (constraints->centreX.GetDone() && constraints->right.GetDone())
645 value = (int)(2*(constraints->right.GetValue() - constraints->centreX.GetValue()));
659 switch (relationship)
663 int edgePos = GetEdge(otherEdge, win, otherWin);
666 value = (int)(edgePos*(((float)percent)*0.01));
678 win->GetSize(&w, &value);
684 case wxUnconstrained:
686 // We know the height if we know the top edge and the bottom edge, OR
687 // if we know the top edge and the centre, OR
688 // if we know the bottom edge and the centre
689 if (constraints->top.GetDone() && constraints->bottom.GetDone())
691 value = constraints->bottom.GetValue() - constraints->top.GetValue();
695 else if (constraints->top.GetDone() && constraints->centreY.GetDone())
697 value = (int)(2*(constraints->centreY.GetValue() - constraints->top.GetValue()));
701 else if (constraints->bottom.GetDone() && constraints->centreY.GetDone())
703 value = (int)(2*(constraints->bottom.GetValue() - constraints->centreY.GetValue()));
721 // Get the value of this edge or dimension, or if this is not determinable, -1.
722 int wxIndividualLayoutConstraint::GetEdge(wxEdge which,
723 wxWindowBase *thisWin,
724 wxWindowBase *other) const
726 // If the edge or dimension belongs to the parent, then we know the
727 // dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
728 // button (but the button's true parent is a panel, not the sizer)
729 if (other->GetChildren().Find(thisWin))
744 other->GetClientSizeConstraint(&w, &h);
750 other->GetClientSizeConstraint(&w, &h);
756 other->GetClientSizeConstraint(&w, &h);
762 other->GetClientSizeConstraint(&w, &h);
769 other->GetClientSizeConstraint(&w, &h);
770 if (which == wxCentreX)
783 wxLayoutConstraints *constr = other->GetConstraints();
784 // If no constraints, it means the window is not dependent
785 // on anything, and therefore we know its value immediately
788 if (constr->left.GetDone())
789 return constr->left.GetValue();
796 other->GetPosition(&x, &y);
802 wxLayoutConstraints *constr = other->GetConstraints();
803 // If no constraints, it means the window is not dependent
804 // on anything, and therefore we know its value immediately
807 if (constr->top.GetDone())
808 return constr->top.GetValue();
815 other->GetPosition(&x, &y);
821 wxLayoutConstraints *constr = other->GetConstraints();
822 // If no constraints, it means the window is not dependent
823 // on anything, and therefore we know its value immediately
826 if (constr->right.GetDone())
827 return constr->right.GetValue();
834 other->GetPosition(&x, &y);
835 other->GetSize(&w, &h);
841 wxLayoutConstraints *constr = other->GetConstraints();
842 // If no constraints, it means the window is not dependent
843 // on anything, and therefore we know its value immediately
846 if (constr->bottom.GetDone())
847 return constr->bottom.GetValue();
854 other->GetPosition(&x, &y);
855 other->GetSize(&w, &h);
861 wxLayoutConstraints *constr = other->GetConstraints();
862 // If no constraints, it means the window is not dependent
863 // on anything, and therefore we know its value immediately
866 if (constr->width.GetDone())
867 return constr->width.GetValue();
874 other->GetSize(&w, &h);
880 wxLayoutConstraints *constr = other->GetConstraints();
881 // If no constraints, it means the window is not dependent
882 // on anything, and therefore we know its value immediately
885 if (constr->height.GetDone())
886 return constr->height.GetValue();
893 other->GetSize(&w, &h);
899 wxLayoutConstraints *constr = other->GetConstraints();
900 // If no constraints, it means the window is not dependent
901 // on anything, and therefore we know its value immediately
904 if (constr->centreX.GetDone())
905 return constr->centreX.GetValue();
912 other->GetPosition(&x, &y);
913 other->GetSize(&w, &h);
914 return (int)(x + (w/2));
919 wxLayoutConstraints *constr = other->GetConstraints();
920 // If no constraints, it means the window is not dependent
921 // on anything, and therefore we know its value immediately
924 if (constr->centreY.GetDone())
925 return constr->centreY.GetValue();
932 other->GetPosition(&x, &y);
933 other->GetSize(&w, &h);
934 return (int)(y + (h/2));
943 wxLayoutConstraints::wxLayoutConstraints()
945 left.SetEdge(wxLeft);
947 right.SetEdge(wxRight);
948 bottom.SetEdge(wxBottom);
949 centreX.SetEdge(wxCentreX);
950 centreY.SetEdge(wxCentreY);
951 width.SetEdge(wxWidth);
952 height.SetEdge(wxHeight);
955 wxLayoutConstraints::~wxLayoutConstraints()
959 bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase *win, int *nChanges)
963 bool done = width.GetDone();
964 bool newDone = (done ? TRUE : width.SatisfyConstraint(this, win));
968 done = height.GetDone();
969 newDone = (done ? TRUE : height.SatisfyConstraint(this, win));
973 done = left.GetDone();
974 newDone = (done ? TRUE : left.SatisfyConstraint(this, win));
978 done = top.GetDone();
979 newDone = (done ? TRUE : top.SatisfyConstraint(this, win));
983 done = right.GetDone();
984 newDone = (done ? TRUE : right.SatisfyConstraint(this, win));
988 done = bottom.GetDone();
989 newDone = (done ? TRUE : bottom.SatisfyConstraint(this, win));
993 done = centreX.GetDone();
994 newDone = (done ? TRUE : centreX.SatisfyConstraint(this, win));
998 done = centreY.GetDone();
999 newDone = (done ? TRUE : centreY.SatisfyConstraint(this, win));
1000 if (newDone != done)
1003 *nChanges = noChanges;
1005 return AreSatisfied();
1009 * Main constrained layout algorithm. Look at all the child
1010 * windows, and their constraints (if any).
1011 * The idea is to keep iterating through the constraints
1012 * until all left, right, bottom and top edges, and widths and heights,
1013 * are known (or no change occurs and we've failed to resolve all
1016 * If the user has not specified a dimension or edge, it will be
1017 * be calculated from the other known values. E.g. If we know
1018 * the right hand edge and the left hand edge, we now know the width.
1019 * The snag here is that this means we must specify absolute dimensions
1020 * twice (in constructor and in constraint), if we wish to use the
1021 * constraint notation to just set the position, for example.
1022 * Otherwise, if we only set ONE edge and no dimension, it would never
1023 * find the other edge.
1027 Mark all constraints as not done.
1030 until no change or iterations >= max iterations
1033 Calculate all constraints
1038 Set each calculated position and size
1042 #if WXWIN_COMPATIBILITY
1043 bool wxOldDoLayout(wxWindowBase *win)
1045 // Make sure this isn't called recursively from below
1046 static wxList doneSoFar;
1048 if (doneSoFar.Member(win))
1051 doneSoFar.Append(win);
1053 wxNode *node = win->GetChildren().First();
1056 wxWindowBase *child = (wxWindowBase *)node->Data();
1057 wxLayoutConstraints *constr = child->GetConstraints();
1060 constr->left.SetDone(FALSE);
1061 constr->top.SetDone(FALSE);
1062 constr->right.SetDone(FALSE);
1063 constr->bottom.SetDone(FALSE);
1064 constr->width.SetDone(FALSE);
1065 constr->height.SetDone(FALSE);
1066 constr->centreX.SetDone(FALSE);
1067 constr->centreY.SetDone(FALSE);
1069 node = node->Next();
1071 int noIterations = 0;
1072 int maxIterations = 500;
1075 while ((noChanges > 0) && (noIterations < maxIterations))
1078 wxNode *node = win->GetChildren().First();
1081 wxWindowBase *child = (wxWindowBase *)node->Data();
1082 wxLayoutConstraints *constr = child->GetConstraints();
1085 int tempNoChanges = 0;
1086 (void)constr->SatisfyConstraints(child, &tempNoChanges);
1087 noChanges += tempNoChanges;
1089 node = node->Next();
1094 // Would be nice to have a test here to see _which_ constraint(s)
1095 // failed, so we can print a specific diagnostic message.
1098 wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
1101 // Now set the sizes and positions of the children, and
1102 // recursively call Layout().
1103 node = win->GetChildren().First();
1106 wxWindowBase *child = (wxWindowBase *)node->Data();
1107 wxLayoutConstraints *constr = child->GetConstraints();
1108 if (constr && constr->left.GetDone() && constr->right.GetDone() &&
1109 constr->width.GetDone() && constr->height.GetDone())
1111 int x = constr->left.GetValue();
1112 int y = constr->top.GetValue();
1113 int w = constr->width.GetValue();
1114 int h = constr->height.GetValue();
1116 // If we don't want to resize this window, just move it...
1117 if ((constr->width.GetRelationship() != wxAsIs) ||
1118 (constr->height.GetRelationship() != wxAsIs))
1120 // _Should_ call Layout() recursively.
1121 child->SetSize(x, y, w, h);
1130 node = node->Next();
1132 doneSoFar.DeleteObject(win);
1136 #endif // WXWIN_COMPATIBILITY
1138 #endif // wxUSE_CONSTRAINTS