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 #if !USE_SHARED_LIBRARY
48 IMPLEMENT_DYNAMIC_CLASS(wxIndividualLayoutConstraint
, wxObject
)
49 IMPLEMENT_DYNAMIC_CLASS(wxLayoutConstraints
, wxObject
)
53 wxIndividualLayoutConstraint::wxIndividualLayoutConstraint()
56 relationship
= wxUnconstrained
;
62 otherWin
= (wxWindowBase
*) NULL
;
65 wxIndividualLayoutConstraint::~wxIndividualLayoutConstraint()
69 void wxIndividualLayoutConstraint::Set(wxRelationship rel
, wxWindowBase
*otherW
, wxEdge otherE
, int val
, int marg
)
78 void wxIndividualLayoutConstraint::LeftOf(wxWindowBase
*sibling
, int marg
)
80 Set(wxLeftOf
, sibling
, wxLeft
, 0, marg
);
83 void wxIndividualLayoutConstraint::RightOf(wxWindowBase
*sibling
, int marg
)
85 Set(wxRightOf
, sibling
, wxRight
, 0, marg
);
88 void wxIndividualLayoutConstraint::Above(wxWindowBase
*sibling
, int marg
)
90 Set(wxAbove
, sibling
, wxTop
, 0, marg
);
93 void wxIndividualLayoutConstraint::Below(wxWindowBase
*sibling
, int marg
)
95 Set(wxBelow
, sibling
, wxBottom
, 0, marg
);
99 // 'Same edge' alignment
101 void wxIndividualLayoutConstraint::SameAs(wxWindowBase
*otherW
, wxEdge edge
, int marg
)
103 Set(wxPercentOf
, otherW
, edge
, 0, marg
);
107 // The edge is a percentage of the other window's edge
108 void wxIndividualLayoutConstraint::PercentOf(wxWindowBase
*otherW
, wxEdge wh
, int per
)
111 relationship
= wxPercentOf
;
118 // Edge has absolute value
120 void wxIndividualLayoutConstraint::Absolute(int val
)
122 value
= val
; relationship
= wxAbsolute
;
125 // Reset constraint if it mentions otherWin
126 bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase
*otherW
)
128 if (otherW
== otherWin
)
131 relationship
= wxAsIs
;
136 otherWin
= (wxWindowBase
*) NULL
;
143 // Try to satisfy constraint
144 bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints
*constraints
, wxWindowBase
*win
)
146 if (relationship
== wxAbsolute
)
156 switch (relationship
)
160 // We can know this edge if: otherWin is win's
161 // parent, or otherWin has a satisfied constraint,
162 // or otherWin has no constraint.
163 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
166 value
= edgePos
- margin
;
175 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
178 value
= edgePos
+ margin
;
187 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
190 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
197 case wxUnconstrained
:
199 // We know the left-hand edge position if we know
200 // the right-hand edge and we know the width; OR if
201 // we know the centre and the width.
202 if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
204 value
= (constraints
->right
.GetValue() - constraints
->width
.GetValue() + margin
);
208 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
210 value
= (int)(constraints
->centreX
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
220 win
->GetPosition(&value
, &y
);
231 switch (relationship
)
235 // We can know this edge if: otherWin is win's
236 // parent, or otherWin has a satisfied constraint,
237 // or otherWin has no constraint.
238 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
241 value
= edgePos
- margin
;
250 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
253 value
= edgePos
+ margin
;
262 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
265 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
272 case wxUnconstrained
:
274 // We know the right-hand edge position if we know the
275 // left-hand edge and we know the width, OR if we know the
276 // centre edge and the width.
277 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
279 value
= (constraints
->left
.GetValue() + constraints
->width
.GetValue() - margin
);
283 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
285 value
= (int)(constraints
->centreX
.GetValue() + (constraints
->width
.GetValue()/2) - margin
);
296 win
->GetSize(&w
, &h
);
297 win
->GetPosition(&x
, &y
);
309 switch (relationship
)
313 // We can know this edge if: otherWin is win's
314 // parent, or otherWin has a satisfied constraint,
315 // or otherWin has no constraint.
316 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
319 value
= edgePos
- margin
;
328 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
331 value
= edgePos
+ margin
;
340 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
343 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
350 case wxUnconstrained
:
352 // We know the top edge position if we know the bottom edge
353 // and we know the height; OR if we know the centre edge and
355 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
357 value
= (constraints
->bottom
.GetValue() - constraints
->height
.GetValue() + margin
);
361 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
363 value
= (constraints
->centreY
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
373 win
->GetPosition(&x
, &value
);
384 switch (relationship
)
388 // We can know this edge if: otherWin is win's parent,
389 // or otherWin has a satisfied constraint, or
390 // otherWin has no constraint.
391 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
394 value
= edgePos
+ margin
;
403 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
406 value
= edgePos
- margin
;
415 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
418 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
425 case wxUnconstrained
:
427 // We know the bottom edge position if we know the top edge
428 // and we know the height; OR if we know the centre edge and
430 if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
432 value
= (constraints
->top
.GetValue() + constraints
->height
.GetValue() - margin
);
436 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
438 value
= (constraints
->centreY
.GetValue() + (constraints
->height
.GetValue()/2) - margin
);
449 win
->GetSize(&w
, &h
);
450 win
->GetPosition(&x
, &y
);
462 switch (relationship
)
466 // We can know this edge if: otherWin is win's parent, or
467 // otherWin has a satisfied constraint, or otherWin has no
469 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
472 value
= edgePos
- margin
;
481 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
484 value
= edgePos
+ margin
;
493 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
496 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
503 case wxUnconstrained
:
505 // We know the centre position if we know
506 // the left-hand edge and we know the width, OR
507 // the right-hand edge and the width
508 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
510 value
= (int)(constraints
->left
.GetValue() + (constraints
->width
.GetValue()/2) + margin
);
514 else if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
516 value
= (int)(constraints
->left
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
530 switch (relationship
)
534 // We can know this edge if: otherWin is win's parent,
535 // or otherWin has a satisfied constraint, or otherWin
536 // has no constraint.
537 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
540 value
= edgePos
- margin
;
549 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
552 value
= edgePos
+ margin
;
561 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
564 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
571 case wxUnconstrained
:
573 // We know the centre position if we know
574 // the top edge and we know the height, OR
575 // the bottom edge and the height.
576 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
578 value
= (int)(constraints
->bottom
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
582 else if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
584 value
= (int)(constraints
->top
.GetValue() + (constraints
->height
.GetValue()/2) + margin
);
598 switch (relationship
)
602 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
605 value
= (int)(edgePos
*(((float)percent
)*0.01));
617 win
->GetSize(&value
, &h
);
623 case wxUnconstrained
:
625 // We know the width if we know the left edge and the right edge, OR
626 // if we know the left edge and the centre, OR
627 // if we know the right edge and the centre
628 if (constraints
->left
.GetDone() && constraints
->right
.GetDone())
630 value
= constraints
->right
.GetValue() - constraints
->left
.GetValue();
634 else if (constraints
->centreX
.GetDone() && constraints
->left
.GetDone())
636 value
= (int)(2*(constraints
->centreX
.GetValue() - constraints
->left
.GetValue()));
640 else if (constraints
->centreX
.GetDone() && constraints
->right
.GetDone())
642 value
= (int)(2*(constraints
->right
.GetValue() - constraints
->centreX
.GetValue()));
656 switch (relationship
)
660 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
663 value
= (int)(edgePos
*(((float)percent
)*0.01));
675 win
->GetSize(&w
, &value
);
681 case wxUnconstrained
:
683 // We know the height if we know the top edge and the bottom edge, OR
684 // if we know the top edge and the centre, OR
685 // if we know the bottom edge and the centre
686 if (constraints
->top
.GetDone() && constraints
->bottom
.GetDone())
688 value
= constraints
->bottom
.GetValue() - constraints
->top
.GetValue();
692 else if (constraints
->top
.GetDone() && constraints
->centreY
.GetDone())
694 value
= (int)(2*(constraints
->centreY
.GetValue() - constraints
->top
.GetValue()));
698 else if (constraints
->bottom
.GetDone() && constraints
->centreY
.GetDone())
700 value
= (int)(2*(constraints
->bottom
.GetValue() - constraints
->centreY
.GetValue()));
718 // Get the value of this edge or dimension, or if this is not determinable, -1.
719 int wxIndividualLayoutConstraint::GetEdge(wxEdge which
,
720 wxWindowBase
*thisWin
,
721 wxWindowBase
*other
) const
723 // If the edge or dimension belongs to the parent, then we know the
724 // dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
725 // button (but the button's true parent is a panel, not the sizer)
726 if (other
->GetChildren().Find(thisWin
))
741 other
->GetClientSizeConstraint(&w
, &h
);
747 other
->GetClientSizeConstraint(&w
, &h
);
753 other
->GetClientSizeConstraint(&w
, &h
);
759 other
->GetClientSizeConstraint(&w
, &h
);
766 other
->GetClientSizeConstraint(&w
, &h
);
767 if (which
== wxCentreX
)
780 wxLayoutConstraints
*constr
= other
->GetConstraints();
781 // If no constraints, it means the window is not dependent
782 // on anything, and therefore we know its value immediately
785 if (constr
->left
.GetDone())
786 return constr
->left
.GetValue();
793 other
->GetPosition(&x
, &y
);
799 wxLayoutConstraints
*constr
= other
->GetConstraints();
800 // If no constraints, it means the window is not dependent
801 // on anything, and therefore we know its value immediately
804 if (constr
->top
.GetDone())
805 return constr
->top
.GetValue();
812 other
->GetPosition(&x
, &y
);
818 wxLayoutConstraints
*constr
= other
->GetConstraints();
819 // If no constraints, it means the window is not dependent
820 // on anything, and therefore we know its value immediately
823 if (constr
->right
.GetDone())
824 return constr
->right
.GetValue();
831 other
->GetPosition(&x
, &y
);
832 other
->GetSize(&w
, &h
);
838 wxLayoutConstraints
*constr
= other
->GetConstraints();
839 // If no constraints, it means the window is not dependent
840 // on anything, and therefore we know its value immediately
843 if (constr
->bottom
.GetDone())
844 return constr
->bottom
.GetValue();
851 other
->GetPosition(&x
, &y
);
852 other
->GetSize(&w
, &h
);
858 wxLayoutConstraints
*constr
= other
->GetConstraints();
859 // If no constraints, it means the window is not dependent
860 // on anything, and therefore we know its value immediately
863 if (constr
->width
.GetDone())
864 return constr
->width
.GetValue();
871 other
->GetSize(&w
, &h
);
877 wxLayoutConstraints
*constr
= other
->GetConstraints();
878 // If no constraints, it means the window is not dependent
879 // on anything, and therefore we know its value immediately
882 if (constr
->height
.GetDone())
883 return constr
->height
.GetValue();
890 other
->GetSize(&w
, &h
);
896 wxLayoutConstraints
*constr
= other
->GetConstraints();
897 // If no constraints, it means the window is not dependent
898 // on anything, and therefore we know its value immediately
901 if (constr
->centreX
.GetDone())
902 return constr
->centreX
.GetValue();
909 other
->GetPosition(&x
, &y
);
910 other
->GetSize(&w
, &h
);
911 return (int)(x
+ (w
/2));
916 wxLayoutConstraints
*constr
= other
->GetConstraints();
917 // If no constraints, it means the window is not dependent
918 // on anything, and therefore we know its value immediately
921 if (constr
->centreY
.GetDone())
922 return constr
->centreY
.GetValue();
929 other
->GetPosition(&x
, &y
);
930 other
->GetSize(&w
, &h
);
931 return (int)(y
+ (h
/2));
940 wxLayoutConstraints::wxLayoutConstraints()
942 left
.SetEdge(wxLeft
);
944 right
.SetEdge(wxRight
);
945 bottom
.SetEdge(wxBottom
);
946 centreX
.SetEdge(wxCentreX
);
947 centreY
.SetEdge(wxCentreY
);
948 width
.SetEdge(wxWidth
);
949 height
.SetEdge(wxHeight
);
952 wxLayoutConstraints::~wxLayoutConstraints()
956 bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase
*win
, int *nChanges
)
960 bool done
= width
.GetDone();
961 bool newDone
= (done
? TRUE
: width
.SatisfyConstraint(this, win
));
965 done
= height
.GetDone();
966 newDone
= (done
? TRUE
: height
.SatisfyConstraint(this, win
));
970 done
= left
.GetDone();
971 newDone
= (done
? TRUE
: left
.SatisfyConstraint(this, win
));
975 done
= top
.GetDone();
976 newDone
= (done
? TRUE
: top
.SatisfyConstraint(this, win
));
980 done
= right
.GetDone();
981 newDone
= (done
? TRUE
: right
.SatisfyConstraint(this, win
));
985 done
= bottom
.GetDone();
986 newDone
= (done
? TRUE
: bottom
.SatisfyConstraint(this, win
));
990 done
= centreX
.GetDone();
991 newDone
= (done
? TRUE
: centreX
.SatisfyConstraint(this, win
));
995 done
= centreY
.GetDone();
996 newDone
= (done
? TRUE
: centreY
.SatisfyConstraint(this, win
));
1000 *nChanges
= noChanges
;
1002 return AreSatisfied();
1006 * Main constrained layout algorithm. Look at all the child
1007 * windows, and their constraints (if any).
1008 * The idea is to keep iterating through the constraints
1009 * until all left, right, bottom and top edges, and widths and heights,
1010 * are known (or no change occurs and we've failed to resolve all
1013 * If the user has not specified a dimension or edge, it will be
1014 * be calculated from the other known values. E.g. If we know
1015 * the right hand edge and the left hand edge, we now know the width.
1016 * The snag here is that this means we must specify absolute dimensions
1017 * twice (in constructor and in constraint), if we wish to use the
1018 * constraint notation to just set the position, for example.
1019 * Otherwise, if we only set ONE edge and no dimension, it would never
1020 * find the other edge.
1024 Mark all constraints as not done.
1027 until no change or iterations >= max iterations
1030 Calculate all constraints
1035 Set each calculated position and size
1039 #if WXWIN_COMPATIBILITY
1040 bool wxOldDoLayout(wxWindowBase
*win
)
1042 // Make sure this isn't called recursively from below
1043 static wxList doneSoFar
;
1045 if (doneSoFar
.Member(win
))
1048 doneSoFar
.Append(win
);
1050 wxNode
*node
= win
->GetChildren().First();
1053 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1054 wxLayoutConstraints
*constr
= child
->GetConstraints();
1057 constr
->left
.SetDone(FALSE
);
1058 constr
->top
.SetDone(FALSE
);
1059 constr
->right
.SetDone(FALSE
);
1060 constr
->bottom
.SetDone(FALSE
);
1061 constr
->width
.SetDone(FALSE
);
1062 constr
->height
.SetDone(FALSE
);
1063 constr
->centreX
.SetDone(FALSE
);
1064 constr
->centreY
.SetDone(FALSE
);
1066 node
= node
->Next();
1068 int noIterations
= 0;
1069 int maxIterations
= 500;
1072 while ((noChanges
> 0) && (noIterations
< maxIterations
))
1075 wxNode
*node
= win
->GetChildren().First();
1078 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1079 wxLayoutConstraints
*constr
= child
->GetConstraints();
1082 int tempNoChanges
= 0;
1083 (void)constr
->SatisfyConstraints(child
, &tempNoChanges
);
1084 noChanges
+= tempNoChanges
;
1086 node
= node
->Next();
1091 // Would be nice to have a test here to see _which_ constraint(s)
1092 // failed, so we can print a specific diagnostic message.
1095 wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
1098 // Now set the sizes and positions of the children, and
1099 // recursively call Layout().
1100 node
= win
->GetChildren().First();
1103 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1104 wxLayoutConstraints
*constr
= child
->GetConstraints();
1105 if (constr
&& constr
->left
.GetDone() && constr
->right
.GetDone() &&
1106 constr
->width
.GetDone() && constr
->height
.GetDone())
1108 int x
= constr
->left
.GetValue();
1109 int y
= constr
->top
.GetValue();
1110 int w
= constr
->width
.GetValue();
1111 int h
= constr
->height
.GetValue();
1113 // If we don't want to resize this window, just move it...
1114 if ((constr
->width
.GetRelationship() != wxAsIs
) ||
1115 (constr
->height
.GetRelationship() != wxAsIs
))
1117 // _Should_ call Layout() recursively.
1118 child
->SetSize(x
, y
, w
, h
);
1127 node
= node
->Next();
1129 doneSoFar
.DeleteObject(win
);
1133 #endif // WXWIN_COMPATIBILITY
1135 #endif // wxUSE_CONSTRAINTS