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
)
76 void wxIndividualLayoutConstraint::LeftOf(wxWindowBase
*sibling
, int marg
)
78 Set(wxLeftOf
, sibling
, wxLeft
, 0, marg
);
81 void wxIndividualLayoutConstraint::RightOf(wxWindowBase
*sibling
, int marg
)
83 Set(wxRightOf
, sibling
, wxRight
, 0, marg
);
86 void wxIndividualLayoutConstraint::Above(wxWindowBase
*sibling
, int marg
)
88 Set(wxAbove
, sibling
, wxTop
, 0, marg
);
91 void wxIndividualLayoutConstraint::Below(wxWindowBase
*sibling
, int marg
)
93 Set(wxBelow
, sibling
, wxBottom
, 0, marg
);
97 // 'Same edge' alignment
99 void wxIndividualLayoutConstraint::SameAs(wxWindowBase
*otherW
, wxEdge edge
, int marg
)
101 Set(wxPercentOf
, otherW
, edge
, 0, marg
);
105 // The edge is a percentage of the other window's edge
106 void wxIndividualLayoutConstraint::PercentOf(wxWindowBase
*otherW
, wxEdge wh
, int per
)
109 relationship
= wxPercentOf
;
116 // Edge has absolute value
118 void wxIndividualLayoutConstraint::Absolute(int val
)
120 value
= val
; relationship
= wxAbsolute
;
123 // Reset constraint if it mentions otherWin
124 bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase
*otherW
)
126 if (otherW
== otherWin
)
129 relationship
= wxAsIs
;
134 otherWin
= (wxWindowBase
*) NULL
;
141 // Try to satisfy constraint
142 bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints
*constraints
, wxWindowBase
*win
)
144 if (relationship
== wxAbsolute
)
154 switch (relationship
)
158 // We can know this edge if: otherWin is win's
159 // parent, or otherWin has a satisfied constraint,
160 // or otherWin has no constraint.
161 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
164 value
= edgePos
- margin
;
173 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
176 value
= edgePos
+ margin
;
185 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
188 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
195 case wxUnconstrained
:
197 // We know the left-hand edge position if we know
198 // the right-hand edge and we know the width; OR if
199 // we know the centre and the width.
200 if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
202 value
= (constraints
->right
.GetValue() - constraints
->width
.GetValue() + margin
);
206 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
208 value
= (int)(constraints
->centreX
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
218 win
->GetPosition(&value
, &y
);
229 switch (relationship
)
233 // We can know this edge if: otherWin is win's
234 // parent, or otherWin has a satisfied constraint,
235 // or otherWin has no constraint.
236 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
239 value
= edgePos
- margin
;
248 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
251 value
= edgePos
+ margin
;
260 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
263 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
270 case wxUnconstrained
:
272 // We know the right-hand edge position if we know the
273 // left-hand edge and we know the width, OR if we know the
274 // centre edge and the width.
275 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
277 value
= (constraints
->left
.GetValue() + constraints
->width
.GetValue() - margin
);
281 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
283 value
= (int)(constraints
->centreX
.GetValue() + (constraints
->width
.GetValue()/2) - margin
);
294 win
->GetSize(&w
, &h
);
295 win
->GetPosition(&x
, &y
);
307 switch (relationship
)
311 // We can know this edge if: otherWin is win's
312 // parent, or otherWin has a satisfied constraint,
313 // or otherWin has no constraint.
314 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
317 value
= edgePos
- margin
;
326 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
329 value
= edgePos
+ margin
;
338 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
341 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
348 case wxUnconstrained
:
350 // We know the top edge position if we know the bottom edge
351 // and we know the height; OR if we know the centre edge and
353 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
355 value
= (constraints
->bottom
.GetValue() - constraints
->height
.GetValue() + margin
);
359 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
361 value
= (constraints
->centreY
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
371 win
->GetPosition(&x
, &value
);
382 switch (relationship
)
386 // We can know this edge if: otherWin is win's parent,
387 // or otherWin has a satisfied constraint, or
388 // otherWin has no constraint.
389 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
392 value
= edgePos
+ margin
;
401 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
404 value
= edgePos
- margin
;
413 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
416 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
423 case wxUnconstrained
:
425 // We know the bottom edge position if we know the top edge
426 // and we know the height; OR if we know the centre edge and
428 if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
430 value
= (constraints
->top
.GetValue() + constraints
->height
.GetValue() - margin
);
434 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
436 value
= (constraints
->centreY
.GetValue() + (constraints
->height
.GetValue()/2) - margin
);
447 win
->GetSize(&w
, &h
);
448 win
->GetPosition(&x
, &y
);
460 switch (relationship
)
464 // We can know this edge if: otherWin is win's parent, or
465 // otherWin has a satisfied constraint, or otherWin has no
467 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
470 value
= edgePos
- margin
;
479 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
482 value
= edgePos
+ margin
;
491 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
494 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
501 case wxUnconstrained
:
503 // We know the centre position if we know
504 // the left-hand edge and we know the width, OR
505 // the right-hand edge and the width
506 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
508 value
= (int)(constraints
->left
.GetValue() + (constraints
->width
.GetValue()/2) + margin
);
512 else if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
514 value
= (int)(constraints
->left
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
528 switch (relationship
)
532 // We can know this edge if: otherWin is win's parent,
533 // or otherWin has a satisfied constraint, or otherWin
534 // has no constraint.
535 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
538 value
= edgePos
- margin
;
547 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
550 value
= edgePos
+ margin
;
559 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
562 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
569 case wxUnconstrained
:
571 // We know the centre position if we know
572 // the top edge and we know the height, OR
573 // the bottom edge and the height.
574 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
576 value
= (int)(constraints
->bottom
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
580 else if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
582 value
= (int)(constraints
->top
.GetValue() + (constraints
->height
.GetValue()/2) + margin
);
596 switch (relationship
)
600 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
603 value
= (int)(edgePos
*(((float)percent
)*0.01));
615 win
->GetSize(&value
, &h
);
621 case wxUnconstrained
:
623 // We know the width if we know the left edge and the right edge, OR
624 // if we know the left edge and the centre, OR
625 // if we know the right edge and the centre
626 if (constraints
->left
.GetDone() && constraints
->right
.GetDone())
628 value
= constraints
->right
.GetValue() - constraints
->left
.GetValue();
632 else if (constraints
->centreX
.GetDone() && constraints
->left
.GetDone())
634 value
= (int)(2*(constraints
->centreX
.GetValue() - constraints
->left
.GetValue()));
638 else if (constraints
->centreX
.GetDone() && constraints
->right
.GetDone())
640 value
= (int)(2*(constraints
->right
.GetValue() - constraints
->centreX
.GetValue()));
654 switch (relationship
)
658 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
661 value
= (int)(edgePos
*(((float)percent
)*0.01));
673 win
->GetSize(&w
, &value
);
679 case wxUnconstrained
:
681 // We know the height if we know the top edge and the bottom edge, OR
682 // if we know the top edge and the centre, OR
683 // if we know the bottom edge and the centre
684 if (constraints
->top
.GetDone() && constraints
->bottom
.GetDone())
686 value
= constraints
->bottom
.GetValue() - constraints
->top
.GetValue();
690 else if (constraints
->top
.GetDone() && constraints
->centreY
.GetDone())
692 value
= (int)(2*(constraints
->centreY
.GetValue() - constraints
->top
.GetValue()));
696 else if (constraints
->bottom
.GetDone() && constraints
->centreY
.GetDone())
698 value
= (int)(2*(constraints
->bottom
.GetValue() - constraints
->centreY
.GetValue()));
716 // Get the value of this edge or dimension, or if this is not determinable, -1.
717 int wxIndividualLayoutConstraint::GetEdge(wxEdge which
,
718 wxWindowBase
*thisWin
,
719 wxWindowBase
*other
) const
721 // If the edge or dimension belongs to the parent, then we know the
722 // dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
723 // button (but the button's true parent is a panel, not the sizer)
724 if (other
->GetChildren().Find(thisWin
))
739 other
->GetClientSizeConstraint(&w
, &h
);
745 other
->GetClientSizeConstraint(&w
, &h
);
751 other
->GetClientSizeConstraint(&w
, &h
);
757 other
->GetClientSizeConstraint(&w
, &h
);
764 other
->GetClientSizeConstraint(&w
, &h
);
765 if (which
== wxCentreX
)
778 wxLayoutConstraints
*constr
= other
->GetConstraints();
779 // If no constraints, it means the window is not dependent
780 // on anything, and therefore we know its value immediately
783 if (constr
->left
.GetDone())
784 return constr
->left
.GetValue();
791 other
->GetPosition(&x
, &y
);
797 wxLayoutConstraints
*constr
= other
->GetConstraints();
798 // If no constraints, it means the window is not dependent
799 // on anything, and therefore we know its value immediately
802 if (constr
->top
.GetDone())
803 return constr
->top
.GetValue();
810 other
->GetPosition(&x
, &y
);
816 wxLayoutConstraints
*constr
= other
->GetConstraints();
817 // If no constraints, it means the window is not dependent
818 // on anything, and therefore we know its value immediately
821 if (constr
->right
.GetDone())
822 return constr
->right
.GetValue();
829 other
->GetPosition(&x
, &y
);
830 other
->GetSize(&w
, &h
);
836 wxLayoutConstraints
*constr
= other
->GetConstraints();
837 // If no constraints, it means the window is not dependent
838 // on anything, and therefore we know its value immediately
841 if (constr
->bottom
.GetDone())
842 return constr
->bottom
.GetValue();
849 other
->GetPosition(&x
, &y
);
850 other
->GetSize(&w
, &h
);
856 wxLayoutConstraints
*constr
= other
->GetConstraints();
857 // If no constraints, it means the window is not dependent
858 // on anything, and therefore we know its value immediately
861 if (constr
->width
.GetDone())
862 return constr
->width
.GetValue();
869 other
->GetSize(&w
, &h
);
875 wxLayoutConstraints
*constr
= other
->GetConstraints();
876 // If no constraints, it means the window is not dependent
877 // on anything, and therefore we know its value immediately
880 if (constr
->height
.GetDone())
881 return constr
->height
.GetValue();
888 other
->GetSize(&w
, &h
);
894 wxLayoutConstraints
*constr
= other
->GetConstraints();
895 // If no constraints, it means the window is not dependent
896 // on anything, and therefore we know its value immediately
899 if (constr
->centreX
.GetDone())
900 return constr
->centreX
.GetValue();
907 other
->GetPosition(&x
, &y
);
908 other
->GetSize(&w
, &h
);
909 return (int)(x
+ (w
/2));
914 wxLayoutConstraints
*constr
= other
->GetConstraints();
915 // If no constraints, it means the window is not dependent
916 // on anything, and therefore we know its value immediately
919 if (constr
->centreY
.GetDone())
920 return constr
->centreY
.GetValue();
927 other
->GetPosition(&x
, &y
);
928 other
->GetSize(&w
, &h
);
929 return (int)(y
+ (h
/2));
938 wxLayoutConstraints::wxLayoutConstraints()
940 left
.SetEdge(wxLeft
);
942 right
.SetEdge(wxRight
);
943 bottom
.SetEdge(wxBottom
);
944 centreX
.SetEdge(wxCentreX
);
945 centreY
.SetEdge(wxCentreY
);
946 width
.SetEdge(wxWidth
);
947 height
.SetEdge(wxHeight
);
950 wxLayoutConstraints::~wxLayoutConstraints()
954 bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase
*win
, int *nChanges
)
958 bool done
= width
.GetDone();
959 bool newDone
= (done
? TRUE
: width
.SatisfyConstraint(this, win
));
963 done
= height
.GetDone();
964 newDone
= (done
? TRUE
: height
.SatisfyConstraint(this, win
));
968 done
= left
.GetDone();
969 newDone
= (done
? TRUE
: left
.SatisfyConstraint(this, win
));
973 done
= top
.GetDone();
974 newDone
= (done
? TRUE
: top
.SatisfyConstraint(this, win
));
978 done
= right
.GetDone();
979 newDone
= (done
? TRUE
: right
.SatisfyConstraint(this, win
));
983 done
= bottom
.GetDone();
984 newDone
= (done
? TRUE
: bottom
.SatisfyConstraint(this, win
));
988 done
= centreX
.GetDone();
989 newDone
= (done
? TRUE
: centreX
.SatisfyConstraint(this, win
));
993 done
= centreY
.GetDone();
994 newDone
= (done
? TRUE
: centreY
.SatisfyConstraint(this, win
));
998 *nChanges
= noChanges
;
1000 return AreSatisfied();
1004 * Main constrained layout algorithm. Look at all the child
1005 * windows, and their constraints (if any).
1006 * The idea is to keep iterating through the constraints
1007 * until all left, right, bottom and top edges, and widths and heights,
1008 * are known (or no change occurs and we've failed to resolve all
1011 * If the user has not specified a dimension or edge, it will be
1012 * be calculated from the other known values. E.g. If we know
1013 * the right hand edge and the left hand edge, we now know the width.
1014 * The snag here is that this means we must specify absolute dimensions
1015 * twice (in constructor and in constraint), if we wish to use the
1016 * constraint notation to just set the position, for example.
1017 * Otherwise, if we only set ONE edge and no dimension, it would never
1018 * find the other edge.
1022 Mark all constraints as not done.
1025 until no change or iterations >= max iterations
1028 Calculate all constraints
1033 Set each calculated position and size
1037 #if WXWIN_COMPATIBILITY
1038 bool wxOldDoLayout(wxWindowBase
*win
)
1040 // Make sure this isn't called recursively from below
1041 static wxList doneSoFar
;
1043 if (doneSoFar
.Member(win
))
1046 doneSoFar
.Append(win
);
1048 wxNode
*node
= win
->GetChildren().First();
1051 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1052 wxLayoutConstraints
*constr
= child
->GetConstraints();
1055 constr
->left
.SetDone(FALSE
);
1056 constr
->top
.SetDone(FALSE
);
1057 constr
->right
.SetDone(FALSE
);
1058 constr
->bottom
.SetDone(FALSE
);
1059 constr
->width
.SetDone(FALSE
);
1060 constr
->height
.SetDone(FALSE
);
1061 constr
->centreX
.SetDone(FALSE
);
1062 constr
->centreY
.SetDone(FALSE
);
1064 node
= node
->Next();
1066 int noIterations
= 0;
1067 int maxIterations
= 500;
1070 while ((noChanges
> 0) && (noIterations
< maxIterations
))
1073 wxNode
*node
= win
->GetChildren().First();
1076 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1077 wxLayoutConstraints
*constr
= child
->GetConstraints();
1080 int tempNoChanges
= 0;
1081 (void)constr
->SatisfyConstraints(child
, &tempNoChanges
);
1082 noChanges
+= tempNoChanges
;
1084 node
= node
->Next();
1089 // Would be nice to have a test here to see _which_ constraint(s)
1090 // failed, so we can print a specific diagnostic message.
1093 wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
1096 // Now set the sizes and positions of the children, and
1097 // recursively call Layout().
1098 node
= win
->GetChildren().First();
1101 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1102 wxLayoutConstraints
*constr
= child
->GetConstraints();
1103 if (constr
&& constr
->left
.GetDone() && constr
->right
.GetDone() &&
1104 constr
->width
.GetDone() && constr
->height
.GetDone())
1106 int x
= constr
->left
.GetValue();
1107 int y
= constr
->top
.GetValue();
1108 int w
= constr
->width
.GetValue();
1109 int h
= constr
->height
.GetValue();
1111 // If we don't want to resize this window, just move it...
1112 if ((constr
->width
.GetRelationship() != wxAsIs
) ||
1113 (constr
->height
.GetRelationship() != wxAsIs
))
1115 // _Should_ call Layout() recursively.
1116 child
->SetSize(x
, y
, w
, h
);
1125 node
= node
->Next();
1127 doneSoFar
.DeleteObject(win
);
1131 #endif // WXWIN_COMPATIBILITY
1133 #endif // wxUSE_CONSTRAINTS