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
)
124 value
= val
; relationship
= wxAbsolute
;
127 // Reset constraint if it mentions otherWin
128 bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase
*otherW
)
130 if (otherW
== otherWin
)
133 relationship
= wxAsIs
;
138 otherWin
= (wxWindowBase
*) NULL
;
145 // Try to satisfy constraint
146 bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints
*constraints
, wxWindowBase
*win
)
148 if (relationship
== wxAbsolute
)
158 switch (relationship
)
162 // We can know this edge if: otherWin is win's
163 // parent, or otherWin has a satisfied constraint,
164 // or otherWin has no constraint.
165 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
168 value
= edgePos
- margin
;
177 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
180 value
= edgePos
+ margin
;
189 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
192 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
199 case wxUnconstrained
:
201 // We know the left-hand edge position if we know
202 // the right-hand edge and we know the width; OR if
203 // we know the centre and the width.
204 if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
206 value
= (constraints
->right
.GetValue() - constraints
->width
.GetValue() + margin
);
210 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
212 value
= (int)(constraints
->centreX
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
222 win
->GetPosition(&value
, &y
);
233 switch (relationship
)
237 // We can know this edge if: otherWin is win's
238 // parent, or otherWin has a satisfied constraint,
239 // or otherWin has no constraint.
240 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
243 value
= edgePos
- margin
;
252 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
255 value
= edgePos
+ margin
;
264 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
267 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
274 case wxUnconstrained
:
276 // We know the right-hand edge position if we know the
277 // left-hand edge and we know the width, OR if we know the
278 // centre edge and the width.
279 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
281 value
= (constraints
->left
.GetValue() + constraints
->width
.GetValue() - margin
);
285 else if (constraints
->centreX
.GetDone() && constraints
->width
.GetDone())
287 value
= (int)(constraints
->centreX
.GetValue() + (constraints
->width
.GetValue()/2) - margin
);
298 win
->GetSize(&w
, &h
);
299 win
->GetPosition(&x
, &y
);
311 switch (relationship
)
315 // We can know this edge if: otherWin is win's
316 // parent, or otherWin has a satisfied constraint,
317 // or otherWin has no constraint.
318 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
321 value
= edgePos
- margin
;
330 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
333 value
= edgePos
+ margin
;
342 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
345 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
352 case wxUnconstrained
:
354 // We know the top edge position if we know the bottom edge
355 // and we know the height; OR if we know the centre edge and
357 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
359 value
= (constraints
->bottom
.GetValue() - constraints
->height
.GetValue() + margin
);
363 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
365 value
= (constraints
->centreY
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
375 win
->GetPosition(&x
, &value
);
386 switch (relationship
)
390 // We can know this edge if: otherWin is win's parent,
391 // or otherWin has a satisfied constraint, or
392 // otherWin has no constraint.
393 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
396 value
= edgePos
+ margin
;
405 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
408 value
= edgePos
- margin
;
417 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
420 value
= (int)(edgePos
*(((float)percent
)*0.01) - margin
);
427 case wxUnconstrained
:
429 // We know the bottom edge position if we know the top edge
430 // and we know the height; OR if we know the centre edge and
432 if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
434 value
= (constraints
->top
.GetValue() + constraints
->height
.GetValue() - margin
);
438 else if (constraints
->centreY
.GetDone() && constraints
->height
.GetDone())
440 value
= (constraints
->centreY
.GetValue() + (constraints
->height
.GetValue()/2) - margin
);
451 win
->GetSize(&w
, &h
);
452 win
->GetPosition(&x
, &y
);
464 switch (relationship
)
468 // We can know this edge if: otherWin is win's parent, or
469 // otherWin has a satisfied constraint, or otherWin has no
471 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
474 value
= edgePos
- margin
;
483 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
486 value
= edgePos
+ margin
;
495 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
498 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
505 case wxUnconstrained
:
507 // We know the centre position if we know
508 // the left-hand edge and we know the width, OR
509 // the right-hand edge and the width
510 if (constraints
->left
.GetDone() && constraints
->width
.GetDone())
512 value
= (int)(constraints
->left
.GetValue() + (constraints
->width
.GetValue()/2) + margin
);
516 else if (constraints
->right
.GetDone() && constraints
->width
.GetDone())
518 value
= (int)(constraints
->left
.GetValue() - (constraints
->width
.GetValue()/2) + margin
);
532 switch (relationship
)
536 // We can know this edge if: otherWin is win's parent,
537 // or otherWin has a satisfied constraint, or otherWin
538 // has no constraint.
539 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
542 value
= edgePos
- margin
;
551 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
554 value
= edgePos
+ margin
;
563 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
566 value
= (int)(edgePos
*(((float)percent
)*0.01) + margin
);
573 case wxUnconstrained
:
575 // We know the centre position if we know
576 // the top edge and we know the height, OR
577 // the bottom edge and the height.
578 if (constraints
->bottom
.GetDone() && constraints
->height
.GetDone())
580 value
= (int)(constraints
->bottom
.GetValue() - (constraints
->height
.GetValue()/2) + margin
);
584 else if (constraints
->top
.GetDone() && constraints
->height
.GetDone())
586 value
= (int)(constraints
->top
.GetValue() + (constraints
->height
.GetValue()/2) + margin
);
600 switch (relationship
)
604 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
607 value
= (int)(edgePos
*(((float)percent
)*0.01));
619 win
->GetSize(&value
, &h
);
625 case wxUnconstrained
:
627 // We know the width if we know the left edge and the right edge, OR
628 // if we know the left edge and the centre, OR
629 // if we know the right edge and the centre
630 if (constraints
->left
.GetDone() && constraints
->right
.GetDone())
632 value
= constraints
->right
.GetValue() - constraints
->left
.GetValue();
636 else if (constraints
->centreX
.GetDone() && constraints
->left
.GetDone())
638 value
= (int)(2*(constraints
->centreX
.GetValue() - constraints
->left
.GetValue()));
642 else if (constraints
->centreX
.GetDone() && constraints
->right
.GetDone())
644 value
= (int)(2*(constraints
->right
.GetValue() - constraints
->centreX
.GetValue()));
658 switch (relationship
)
662 int edgePos
= GetEdge(otherEdge
, win
, otherWin
);
665 value
= (int)(edgePos
*(((float)percent
)*0.01));
677 win
->GetSize(&w
, &value
);
683 case wxUnconstrained
:
685 // We know the height if we know the top edge and the bottom edge, OR
686 // if we know the top edge and the centre, OR
687 // if we know the bottom edge and the centre
688 if (constraints
->top
.GetDone() && constraints
->bottom
.GetDone())
690 value
= constraints
->bottom
.GetValue() - constraints
->top
.GetValue();
694 else if (constraints
->top
.GetDone() && constraints
->centreY
.GetDone())
696 value
= (int)(2*(constraints
->centreY
.GetValue() - constraints
->top
.GetValue()));
700 else if (constraints
->bottom
.GetDone() && constraints
->centreY
.GetDone())
702 value
= (int)(2*(constraints
->bottom
.GetValue() - constraints
->centreY
.GetValue()));
720 // Get the value of this edge or dimension, or if this is not determinable, -1.
721 int wxIndividualLayoutConstraint::GetEdge(wxEdge which
,
722 wxWindowBase
*thisWin
,
723 wxWindowBase
*other
) const
725 // If the edge or dimension belongs to the parent, then we know the
726 // dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
727 // button (but the button's true parent is a panel, not the sizer)
728 if (other
->GetChildren().Find(thisWin
))
743 other
->GetClientSizeConstraint(&w
, &h
);
749 other
->GetClientSizeConstraint(&w
, &h
);
755 other
->GetClientSizeConstraint(&w
, &h
);
761 other
->GetClientSizeConstraint(&w
, &h
);
768 other
->GetClientSizeConstraint(&w
, &h
);
769 if (which
== wxCentreX
)
782 wxLayoutConstraints
*constr
= other
->GetConstraints();
783 // If no constraints, it means the window is not dependent
784 // on anything, and therefore we know its value immediately
787 if (constr
->left
.GetDone())
788 return constr
->left
.GetValue();
795 other
->GetPosition(&x
, &y
);
801 wxLayoutConstraints
*constr
= other
->GetConstraints();
802 // If no constraints, it means the window is not dependent
803 // on anything, and therefore we know its value immediately
806 if (constr
->top
.GetDone())
807 return constr
->top
.GetValue();
814 other
->GetPosition(&x
, &y
);
820 wxLayoutConstraints
*constr
= other
->GetConstraints();
821 // If no constraints, it means the window is not dependent
822 // on anything, and therefore we know its value immediately
825 if (constr
->right
.GetDone())
826 return constr
->right
.GetValue();
833 other
->GetPosition(&x
, &y
);
834 other
->GetSize(&w
, &h
);
840 wxLayoutConstraints
*constr
= other
->GetConstraints();
841 // If no constraints, it means the window is not dependent
842 // on anything, and therefore we know its value immediately
845 if (constr
->bottom
.GetDone())
846 return constr
->bottom
.GetValue();
853 other
->GetPosition(&x
, &y
);
854 other
->GetSize(&w
, &h
);
860 wxLayoutConstraints
*constr
= other
->GetConstraints();
861 // If no constraints, it means the window is not dependent
862 // on anything, and therefore we know its value immediately
865 if (constr
->width
.GetDone())
866 return constr
->width
.GetValue();
873 other
->GetSize(&w
, &h
);
879 wxLayoutConstraints
*constr
= other
->GetConstraints();
880 // If no constraints, it means the window is not dependent
881 // on anything, and therefore we know its value immediately
884 if (constr
->height
.GetDone())
885 return constr
->height
.GetValue();
892 other
->GetSize(&w
, &h
);
898 wxLayoutConstraints
*constr
= other
->GetConstraints();
899 // If no constraints, it means the window is not dependent
900 // on anything, and therefore we know its value immediately
903 if (constr
->centreX
.GetDone())
904 return constr
->centreX
.GetValue();
911 other
->GetPosition(&x
, &y
);
912 other
->GetSize(&w
, &h
);
913 return (int)(x
+ (w
/2));
918 wxLayoutConstraints
*constr
= other
->GetConstraints();
919 // If no constraints, it means the window is not dependent
920 // on anything, and therefore we know its value immediately
923 if (constr
->centreY
.GetDone())
924 return constr
->centreY
.GetValue();
931 other
->GetPosition(&x
, &y
);
932 other
->GetSize(&w
, &h
);
933 return (int)(y
+ (h
/2));
942 wxLayoutConstraints::wxLayoutConstraints()
944 left
.SetEdge(wxLeft
);
946 right
.SetEdge(wxRight
);
947 bottom
.SetEdge(wxBottom
);
948 centreX
.SetEdge(wxCentreX
);
949 centreY
.SetEdge(wxCentreY
);
950 width
.SetEdge(wxWidth
);
951 height
.SetEdge(wxHeight
);
954 wxLayoutConstraints::~wxLayoutConstraints()
958 bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase
*win
, int *nChanges
)
962 bool done
= width
.GetDone();
963 bool newDone
= (done
? TRUE
: width
.SatisfyConstraint(this, win
));
967 done
= height
.GetDone();
968 newDone
= (done
? TRUE
: height
.SatisfyConstraint(this, win
));
972 done
= left
.GetDone();
973 newDone
= (done
? TRUE
: left
.SatisfyConstraint(this, win
));
977 done
= top
.GetDone();
978 newDone
= (done
? TRUE
: top
.SatisfyConstraint(this, win
));
982 done
= right
.GetDone();
983 newDone
= (done
? TRUE
: right
.SatisfyConstraint(this, win
));
987 done
= bottom
.GetDone();
988 newDone
= (done
? TRUE
: bottom
.SatisfyConstraint(this, win
));
992 done
= centreX
.GetDone();
993 newDone
= (done
? TRUE
: centreX
.SatisfyConstraint(this, win
));
997 done
= centreY
.GetDone();
998 newDone
= (done
? TRUE
: centreY
.SatisfyConstraint(this, win
));
1002 *nChanges
= noChanges
;
1004 return AreSatisfied();
1008 * Main constrained layout algorithm. Look at all the child
1009 * windows, and their constraints (if any).
1010 * The idea is to keep iterating through the constraints
1011 * until all left, right, bottom and top edges, and widths and heights,
1012 * are known (or no change occurs and we've failed to resolve all
1015 * If the user has not specified a dimension or edge, it will be
1016 * be calculated from the other known values. E.g. If we know
1017 * the right hand edge and the left hand edge, we now know the width.
1018 * The snag here is that this means we must specify absolute dimensions
1019 * twice (in constructor and in constraint), if we wish to use the
1020 * constraint notation to just set the position, for example.
1021 * Otherwise, if we only set ONE edge and no dimension, it would never
1022 * find the other edge.
1026 Mark all constraints as not done.
1029 until no change or iterations >= max iterations
1032 Calculate all constraints
1037 Set each calculated position and size
1041 #if WXWIN_COMPATIBILITY
1042 bool wxOldDoLayout(wxWindowBase
*win
)
1044 // Make sure this isn't called recursively from below
1045 static wxList doneSoFar
;
1047 if (doneSoFar
.Member(win
))
1050 doneSoFar
.Append(win
);
1052 wxNode
*node
= win
->GetChildren().First();
1055 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1056 wxLayoutConstraints
*constr
= child
->GetConstraints();
1059 constr
->left
.SetDone(FALSE
);
1060 constr
->top
.SetDone(FALSE
);
1061 constr
->right
.SetDone(FALSE
);
1062 constr
->bottom
.SetDone(FALSE
);
1063 constr
->width
.SetDone(FALSE
);
1064 constr
->height
.SetDone(FALSE
);
1065 constr
->centreX
.SetDone(FALSE
);
1066 constr
->centreY
.SetDone(FALSE
);
1068 node
= node
->Next();
1070 int noIterations
= 0;
1071 int maxIterations
= 500;
1074 while ((noChanges
> 0) && (noIterations
< maxIterations
))
1077 wxNode
*node
= win
->GetChildren().First();
1080 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1081 wxLayoutConstraints
*constr
= child
->GetConstraints();
1084 int tempNoChanges
= 0;
1085 (void)constr
->SatisfyConstraints(child
, &tempNoChanges
);
1086 noChanges
+= tempNoChanges
;
1088 node
= node
->Next();
1093 // Would be nice to have a test here to see _which_ constraint(s)
1094 // failed, so we can print a specific diagnostic message.
1097 wxDebugMsg(_("wxWindowBase::Layout() failed.\n"));
1100 // Now set the sizes and positions of the children, and
1101 // recursively call Layout().
1102 node
= win
->GetChildren().First();
1105 wxWindowBase
*child
= (wxWindowBase
*)node
->Data();
1106 wxLayoutConstraints
*constr
= child
->GetConstraints();
1107 if (constr
&& constr
->left
.GetDone() && constr
->right
.GetDone() &&
1108 constr
->width
.GetDone() && constr
->height
.GetDone())
1110 int x
= constr
->left
.GetValue();
1111 int y
= constr
->top
.GetValue();
1112 int w
= constr
->width
.GetValue();
1113 int h
= constr
->height
.GetValue();
1115 // If we don't want to resize this window, just move it...
1116 if ((constr
->width
.GetRelationship() != wxAsIs
) ||
1117 (constr
->height
.GetRelationship() != wxAsIs
))
1119 // _Should_ call Layout() recursively.
1120 child
->SetSize(x
, y
, w
, h
);
1129 node
= node
->Next();
1131 doneSoFar
.DeleteObject(win
);
1135 #endif // WXWIN_COMPATIBILITY
1137 #endif // wxUSE_CONSTRAINTS