1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Constraint layout system classes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
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
)
71 // If Set is called by the user with wxSameAs then call SameAs to do
72 // it since it will actually use wxPercent instead.
73 SameAs(otherW
, otherE
, marg
);
81 if ( rel
== wxPercentOf
)
93 void wxIndividualLayoutConstraint::LeftOf(wxWindowBase
*sibling
, int marg
)
95 Set(wxLeftOf
, sibling
, wxLeft
, 0, marg
);
98 void wxIndividualLayoutConstraint::RightOf(wxWindowBase
*sibling
, int marg
)
100 Set(wxRightOf
, sibling
, wxRight
, 0, marg
);
103 void wxIndividualLayoutConstraint::Above(wxWindowBase
*sibling
, int marg
)
105 Set(wxAbove
, sibling
, wxTop
, 0, marg
);
108 void wxIndividualLayoutConstraint::Below(wxWindowBase
*sibling
, int marg
)
110 Set(wxBelow
, sibling
, wxBottom
, 0, marg
);
114 // 'Same edge' alignment
116 void wxIndividualLayoutConstraint::SameAs(wxWindowBase
*otherW
, wxEdge edge
, int marg
)
118 Set(wxPercentOf
, otherW
, edge
, 100, marg
);
121 // The edge is a percentage of the other window's edge
122 void wxIndividualLayoutConstraint::PercentOf(wxWindowBase
*otherW
, wxEdge wh
, int per
)
124 Set(wxPercentOf
, otherW
, wh
, per
);
128 // Edge has absolute value
130 void wxIndividualLayoutConstraint::Absolute(int val
)
133 relationship
= wxAbsolute
;
136 // Reset constraint if it mentions otherWin
137 bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase
*otherW
)
139 if (otherW
== otherWin
)
142 relationship
= wxAsIs
;
147 otherWin
= (wxWindowBase
*) NULL
;
154 // Try to satisfy constraint
155 bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints
*constraints
, wxWindowBase
*win
)
157 if (relationship
== wxAbsolute
)
167 switch (relationship
)
171 // We can know this edge if: otherWin is win's
172 // parent, or otherWin has a satisfied constraint,
173 // or otherWin has no constraint.
174 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
177 value
= edgePos
- margin
;
186 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
189 value
= edgePos
+ margin
;
198 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
201 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
208 case wxUnconstrained
:
210 // We know the left-hand edge position if we know
211 // the right-hand edge and we know the width; OR if
212 // we know the centre and the width.
213 if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
215 value
= (constraints
->right
.GetValue() - constraints
->width
.GetValue() + margin
);
219 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
221 value
= (int)(constraints
->centreX
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
231 win
->GetPosition(&value
, &y
);
242 switch (relationship
)
246 // We can know this edge if: otherWin is win's
247 // parent, or otherWin has a satisfied constraint,
248 // or otherWin has no constraint.
249 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
252 value
= edgePos
- margin
;
261 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
264 value
= edgePos
+ margin
;
273 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
276 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
283 case wxUnconstrained
:
285 // We know the right-hand edge position if we know the
286 // left-hand edge and we know the width, OR if we know the
287 // centre edge and the width.
288 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
290 value
= (constraints
->left
.GetValue() + constraints
->width
.GetValue() - margin
);
294 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
296 value
= (int)(constraints
->centreX
.GetValue() + (constraints
->width
.GetValue()/2) - margin
);
307 win
->GetSize(&w
, &h
);
308 win
->GetPosition(&x
, &y
);
320 switch (relationship
)
324 // We can know this edge if: otherWin is win's
325 // parent, or otherWin has a satisfied constraint,
326 // or otherWin has no constraint.
327 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
330 value
= edgePos
- margin
;
339 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
342 value
= edgePos
+ margin
;
351 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
354 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
361 case wxUnconstrained
:
363 // We know the top edge position if we know the bottom edge
364 // and we know the height; OR if we know the centre edge and
366 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
368 value
= (constraints
->bottom
.GetValue() - constraints
->height
.GetValue() + margin
);
372 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
374 value
= (constraints
->centreY
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
384 win
->GetPosition(&x
, &value
);
395 switch (relationship
)
399 // We can know this edge if: otherWin is win's parent,
400 // or otherWin has a satisfied constraint, or
401 // otherWin has no constraint.
402 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
405 value
= edgePos
+ margin
;
414 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
417 value
= edgePos
- margin
;
426 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
429 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
436 case wxUnconstrained
:
438 // We know the bottom edge position if we know the top edge
439 // and we know the height; OR if we know the centre edge and
441 if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
443 value
= (constraints
->top
.GetValue() + constraints
->height
.GetValue() - margin
);
447 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
449 value
= (constraints
->centreY
.GetValue() + (constraints
->height
.GetValue()/2) - margin
);
460 win
->GetSize(&w
, &h
);
461 win
->GetPosition(&x
, &y
);
473 switch (relationship
)
477 // We can know this edge if: otherWin is win's parent, or
478 // otherWin has a satisfied constraint, or otherWin has no
480 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
483 value
= edgePos
- margin
;
492 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
495 value
= edgePos
+ margin
;
504 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
507 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
514 case wxUnconstrained
:
516 // We know the centre position if we know
517 // the left-hand edge and we know the width, OR
518 // the right-hand edge and the width
519 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
521 value
= (int)(constraints
->left
.GetValue() + (constraints
->width
.GetValue()/2) + margin
);
525 else if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
527 value
= (int)(constraints
->left
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
541 switch (relationship
)
545 // We can know this edge if: otherWin is win's parent,
546 // or otherWin has a satisfied constraint, or otherWin
547 // has no constraint.
548 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
551 value
= edgePos
- margin
;
560 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
563 value
= edgePos
+ margin
;
572 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
575 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
582 case wxUnconstrained
:
584 // We know the centre position if we know
585 // the top edge and we know the height, OR
586 // the bottom edge and the height.
587 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
589 value
= (int)(constraints
->bottom
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
593 else if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
595 value
= (int)(constraints
->top
.GetValue() + (constraints
->height
.GetValue()/2) + margin
);
609 switch (relationship
)
613 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
616 value
= (int)(edgePos
*(((float)percent
)*0.01));
628 win
->GetSize(&value
, &h
);
634 case wxUnconstrained
:
636 // We know the width if we know the left edge and the right edge, OR
637 // if we know the left edge and the centre, OR
638 // if we know the right edge and the centre
639 if (constraints
->left
.GetDone() && constraints
->right
.GetDone())
641 value
= constraints
->right
.GetValue() - constraints
->left
.GetValue();
645 else if (constraints
->centreX
.GetDone() && constraints
->left
.GetDone())
647 value
= (int)(2*(constraints
->centreX
.GetValue() - constraints
->left
.GetValue()));
651 else if (constraints
->centreX
.GetDone() && constraints
->right
.GetDone())
653 value
= (int)(2*(constraints
->right
.GetValue() - constraints
->centreX
.GetValue()));
667 switch (relationship
)
671 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
674 value
= (int)(edgePos
*(((float)percent
)*0.01));
686 win
->GetSize(&w
, &value
);
692 case wxUnconstrained
:
694 // We know the height if we know the top edge and the bottom edge, OR
695 // if we know the top edge and the centre, OR
696 // if we know the bottom edge and the centre
697 if (constraints
->top
.GetDone() && constraints
->bottom
.GetDone())
699 value
= constraints
->bottom
.GetValue() - constraints
->top
.GetValue();
703 else if (constraints
->top
.GetDone() && constraints
->centreY
.GetDone())
705 value
= (int)(2*(constraints
->centreY
.GetValue() - constraints
->top
.GetValue()));
709 else if (constraints
->bottom
.GetDone() && constraints
->centreY
.GetDone())
711 value
= (int)(2*(constraints
->bottom
.GetValue() - constraints
->centreY
.GetValue()));
729 // Get the value of this edge or dimension, or if this is not determinable, -1.
730 int wxIndividualLayoutConstraint::GetEdge(wxEdge which
,
731 wxWindowBase
*thisWin
,
732 wxWindowBase
*other
) const
734 // If the edge or dimension belongs to the parent, then we know the
735 // dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
736 // button (but the button's true parent is a panel, not the sizer)
737 if (other
->GetChildren().Find((wxWindow
*)thisWin
))
752 other
->GetClientSizeConstraint(&w
, &h
);
758 other
->GetClientSizeConstraint(&w
, &h
);
764 other
->GetClientSizeConstraint(&w
, &h
);
770 other
->GetClientSizeConstraint(&w
, &h
);
777 other
->GetClientSizeConstraint(&w
, &h
);
778 if (which
== wxCentreX
)
791 wxLayoutConstraints
*constr
= other
->GetConstraints();
792 // If no constraints, it means the window is not dependent
793 // on anything, and therefore we know its value immediately
796 if (constr
->left
.GetDone())
797 return constr
->left
.GetValue();
804 other
->GetPosition(&x
, &y
);
810 wxLayoutConstraints
*constr
= other
->GetConstraints();
811 // If no constraints, it means the window is not dependent
812 // on anything, and therefore we know its value immediately
815 if (constr
->top
.GetDone())
816 return constr
->top
.GetValue();
823 other
->GetPosition(&x
, &y
);
829 wxLayoutConstraints
*constr
= other
->GetConstraints();
830 // If no constraints, it means the window is not dependent
831 // on anything, and therefore we know its value immediately
834 if (constr
->right
.GetDone())
835 return constr
->right
.GetValue();
842 other
->GetPosition(&x
, &y
);
843 other
->GetSize(&w
, &h
);
849 wxLayoutConstraints
*constr
= other
->GetConstraints();
850 // If no constraints, it means the window is not dependent
851 // on anything, and therefore we know its value immediately
854 if (constr
->bottom
.GetDone())
855 return constr
->bottom
.GetValue();
862 other
->GetPosition(&x
, &y
);
863 other
->GetSize(&w
, &h
);
869 wxLayoutConstraints
*constr
= other
->GetConstraints();
870 // If no constraints, it means the window is not dependent
871 // on anything, and therefore we know its value immediately
874 if (constr
->width
.GetDone())
875 return constr
->width
.GetValue();
882 other
->GetSize(&w
, &h
);
888 wxLayoutConstraints
*constr
= other
->GetConstraints();
889 // If no constraints, it means the window is not dependent
890 // on anything, and therefore we know its value immediately
893 if (constr
->height
.GetDone())
894 return constr
->height
.GetValue();
901 other
->GetSize(&w
, &h
);
907 wxLayoutConstraints
*constr
= other
->GetConstraints();
908 // If no constraints, it means the window is not dependent
909 // on anything, and therefore we know its value immediately
912 if (constr
->centreX
.GetDone())
913 return constr
->centreX
.GetValue();
920 other
->GetPosition(&x
, &y
);
921 other
->GetSize(&w
, &h
);
922 return (int)(x
+ (w
/2));
927 wxLayoutConstraints
*constr
= other
->GetConstraints();
928 // If no constraints, it means the window is not dependent
929 // on anything, and therefore we know its value immediately
932 if (constr
->centreY
.GetDone())
933 return constr
->centreY
.GetValue();
940 other
->GetPosition(&x
, &y
);
941 other
->GetSize(&w
, &h
);
942 return (int)(y
+ (h
/2));
951 wxLayoutConstraints::wxLayoutConstraints()
953 left
.SetEdge(wxLeft
);
955 right
.SetEdge(wxRight
);
956 bottom
.SetEdge(wxBottom
);
957 centreX
.SetEdge(wxCentreX
);
958 centreY
.SetEdge(wxCentreY
);
959 width
.SetEdge(wxWidth
);
960 height
.SetEdge(wxHeight
);
963 wxLayoutConstraints::~wxLayoutConstraints()
967 bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase
*win
, int *nChanges
)
971 bool done
= width
.GetDone();
972 bool newDone
= (done
? TRUE
: width
.SatisfyConstraint(this, win
));
976 done
= height
.GetDone();
977 newDone
= (done
? TRUE
: height
.SatisfyConstraint(this, win
));
981 done
= left
.GetDone();
982 newDone
= (done
? TRUE
: left
.SatisfyConstraint(this, win
));
986 done
= top
.GetDone();
987 newDone
= (done
? TRUE
: top
.SatisfyConstraint(this, win
));
991 done
= right
.GetDone();
992 newDone
= (done
? TRUE
: right
.SatisfyConstraint(this, win
));
996 done
= bottom
.GetDone();
997 newDone
= (done
? TRUE
: bottom
.SatisfyConstraint(this, win
));
1001 done
= centreX
.GetDone();
1002 newDone
= (done
? TRUE
: centreX
.SatisfyConstraint(this, win
));
1003 if (newDone
!= done
)
1006 done
= centreY
.GetDone();
1007 newDone
= (done
? TRUE
: centreY
.SatisfyConstraint(this, win
));
1008 if (newDone
!= done
)
1011 *nChanges
= noChanges
;
1013 return AreSatisfied();
1017 * Main constrained layout algorithm. Look at all the child
1018 * windows, and their constraints (if any).
1019 * The idea is to keep iterating through the constraints
1020 * until all left, right, bottom and top edges, and widths and heights,
1021 * are known (or no change occurs and we've failed to resolve all
1024 * If the user has not specified a dimension or edge, it will be
1025 * be calculated from the other known values. E.g. If we know
1026 * the right hand edge and the left hand edge, we now know the width.
1027 * The snag here is that this means we must specify absolute dimensions
1028 * twice (in constructor and in constraint), if we wish to use the
1029 * constraint notation to just set the position, for example.
1030 * Otherwise, if we only set ONE edge and no dimension, it would never
1031 * find the other edge.
1035 Mark all constraints as not done.
1038 until no change or iterations >= max iterations
1041 Calculate all constraints
1046 Set each calculated position and size
1050 #if WXWIN_COMPATIBILITY
1051 bool wxOldDoLayout(wxWindowBase
*win
)
1053 // Make sure this isn't called recursively from below
1054 static wxList doneSoFar
;
1056 if (doneSoFar
.Member(win
))
1059 doneSoFar
.Append(win
);
1061 wxNode
*node
= win
->GetChildren().First();
1064 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1065 wxLayoutConstraints
*constr
= child
->GetConstraints();
1068 constr
->left
.SetDone(FALSE
);
1069 constr
->top
.SetDone(FALSE
);
1070 constr
->right
.SetDone(FALSE
);
1071 constr
->bottom
.SetDone(FALSE
);
1072 constr
->width
.SetDone(FALSE
);
1073 constr
->height
.SetDone(FALSE
);
1074 constr
->centreX
.SetDone(FALSE
);
1075 constr
->centreY
.SetDone(FALSE
);
1077 node
= node
->Next();
1079 int noIterations
= 0;
1080 int maxIterations
= 500;
1083 while ((noChanges
> 0) && (noIterations
< maxIterations
))
1086 wxNode
*node
= win
->GetChildren().First();
1089 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1090 wxLayoutConstraints
*constr
= child
->GetConstraints();
1093 int tempNoChanges
= 0;
1094 (void)constr
->SatisfyConstraints(child
, &tempNoChanges
);
1095 noChanges
+= tempNoChanges
;
1097 node
= node
->Next();
1102 // Would be nice to have a test here to see _which_ constraint(s)
1103 // failed, so we can print a specific diagnostic message.
1106 wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
1109 // Now set the sizes and positions of the children, and
1110 // recursively call Layout().
1111 node
= win
->GetChildren().First();
1114 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1115 wxLayoutConstraints
*constr
= child
->GetConstraints();
1116 if (constr
&& constr
->left
.GetDone() && constr
->right
.GetDone() &&
1117 constr
->width
.GetDone() && constr
->height
.GetDone())
1119 int x
= constr
->left
.GetValue();
1120 int y
= constr
->top
.GetValue();
1121 int w
= constr
->width
.GetValue();
1122 int h
= constr
->height
.GetValue();
1124 // If we don't want to resize this window, just move it...
1125 if ((constr
->width
.GetRelationship() != wxAsIs
) ||
1126 (constr
->height
.GetRelationship() != wxAsIs
))
1128 // _Should_ call Layout() recursively.
1129 child
->SetSize(x
, y
, w
, h
);
1138 node
= node
->Next();
1140 doneSoFar
.DeleteObject(win
);
1144 #endif // WXWIN_COMPATIBILITY
1146 #endif // wxUSE_CONSTRAINTS