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