xref: /haiku/src/libs/alm/ALMLayout.cpp (revision 59c3263165908e945b0b4df6626af8e95d69aded)
1 /*
2  * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz
3  * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz
4  * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "ALMLayout.h"
10 
11 #include <math.h>		// for floor
12 #include <new>
13 #include <iostream>
14 
15 #include "ViewLayoutItem.h"
16 
17 #include "ResultType.h"
18 
19 
20 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET);
21 const BSize kMinSize(0, 0);
22 const BSize kMaxSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
23 
24 
25 /*!
26  * Constructor.
27  * Creates new layout engine.
28  *
29  * If friendLayout is not NULL the solver of the friend layout is used.
30  */
31 BALMLayout::BALMLayout(float spacing, BALMLayout* friendLayout)
32 	:
33 	fInset(0.0f),
34 	fSpacing(spacing),
35 	fCurrentArea(NULL)
36 {
37 	fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver;
38 
39 	fLeft = AddXTab();
40 	fRight = AddXTab();
41 	fTop = AddYTab();
42 	fBottom = AddYTab();
43 
44 	// the Left tab is always at x-position 0, and the Top tab is always at y-position 0
45 	fLeft->SetRange(0, 0);
46 	fTop->SetRange(0, 0);
47 
48 	// cached layout values
49 	// need to be invalidated whenever the layout specification is changed
50 	fMinSize = kUnsetSize;
51 	fMaxSize = kUnsetSize;
52 	fPreferredSize = kUnsetSize;
53 
54 	fPerformancePath = NULL;
55 
56 	fScaleWidth = fSolver->AddVariable();
57 	fScaleHeight = fSolver->AddVariable();
58 }
59 
60 
61 BALMLayout::~BALMLayout()
62 {
63 	delete fScaleWidth;
64 	delete fScaleHeight;
65 }
66 
67 
68 /**
69  * Adds a new x-tab to the specification.
70  *
71  * @return the new x-tab
72  */
73 XTab*
74 BALMLayout::AddXTab()
75 {
76 	XTab* tab = new(std::nothrow) XTab(fSolver);
77 	if (!tab)
78 		return NULL;
79 	if (!fSolver->AddVariable(tab)) {
80 		delete tab;
81 		return NULL;
82 	}
83 
84 	return tab;
85 }
86 
87 
88 /**
89  * Adds a new y-tab to the specification.
90  *
91  * @return the new y-tab
92  */
93 YTab*
94 BALMLayout::AddYTab()
95 {
96 	YTab* tab = new(std::nothrow) YTab(fSolver);
97 	if (!tab)
98 		return NULL;
99 	if (!fSolver->AddVariable(tab)) {
100 		delete tab;
101 		return NULL;
102 	}
103 
104 	return tab;
105 }
106 
107 
108 /**
109  * Adds a new row to the specification.
110  *
111  * @return the new row
112  */
113 Row*
114 BALMLayout::AddRow()
115 {
116 	return new(std::nothrow) Row(this);
117 }
118 
119 
120 /**
121  * Adds a new row to the specification that is glued to the given y-tabs.
122  *
123  * @param top
124  * @param bottom
125  * @return the new row
126  */
127 Row*
128 BALMLayout::AddRow(YTab* top, YTab* bottom)
129 {
130 	Row* row = new(std::nothrow) Row(this);
131 	if (top != NULL)
132 		row->Constraints()->AddItem(row->Top()->IsEqual(top));
133 	if (bottom != NULL)
134 		row->Constraints()->AddItem(row->Bottom()->IsEqual(bottom));
135 	return row;
136 }
137 
138 
139 /**
140  * Adds a new column to the specification.
141  *
142  * @return the new column
143  */
144 Column*
145 BALMLayout::AddColumn()
146 {
147 	return new(std::nothrow) Column(this);
148 }
149 
150 
151 /**
152  * Adds a new column to the specification that is glued to the given x-tabs.
153  *
154  * @param left
155  * @param right
156  * @return the new column
157  */
158 Column*
159 BALMLayout::AddColumn(XTab* left, XTab* right)
160 {
161 	Column* column = new(std::nothrow) Column(this);
162 	if (left != NULL)
163 		column->Constraints()->AddItem(column->Left()->IsEqual(left));
164 	if (right != NULL)
165 		column->Constraints()->AddItem(column->Right()->IsEqual(right));
166 	return column;
167 }
168 
169 
170 /**
171  * Finds the area that contains the given control.
172  *
173  * @param control	the control to look for
174  * @return the area that contains the control
175  */
176 Area*
177 BALMLayout::AreaFor(const BView* control) const
178 {
179 	return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control))));
180 }
181 
182 
183 Area*
184 BALMLayout::AreaFor(const BLayoutItem* item) const
185 {
186 	if (!item)
187 		return NULL;
188 	return static_cast<Area*>(item->LayoutData());
189 }
190 
191 
192 Area*
193 BALMLayout::CurrentArea() const
194 {
195 	return fCurrentArea;
196 }
197 
198 
199 void
200 BALMLayout::SetCurrentArea(const Area* area)
201 {
202 	fCurrentArea = const_cast<Area*>(area);
203 }
204 
205 
206 void
207 BALMLayout::SetCurrentArea(const BView* view)
208 {
209 	fCurrentArea = AreaFor(view);
210 }
211 
212 
213 void
214 BALMLayout::SetCurrentArea(const BLayoutItem* item)
215 {
216 	fCurrentArea = AreaFor(item);
217 }
218 
219 
220 XTab*
221 BALMLayout::LeftOf(const BView* view) const
222 {
223 	return AreaFor(view)->Left();
224 }
225 
226 
227 XTab*
228 BALMLayout::LeftOf(const BLayoutItem* item) const
229 {
230 	return AreaFor(item)->Left();
231 }
232 
233 
234 XTab*
235 BALMLayout::RightOf(const BView* view) const
236 {
237 	return AreaFor(view)->Right();
238 }
239 
240 
241 XTab*
242 BALMLayout::RightOf(const BLayoutItem* item) const
243 {
244 	return AreaFor(item)->Right();
245 }
246 
247 
248 YTab*
249 BALMLayout::TopOf(const BView* view) const
250 {
251 	return AreaFor(view)->Top();
252 }
253 
254 
255 YTab*
256 BALMLayout::TopOf(const BLayoutItem* item) const
257 {
258 	return AreaFor(item)->Top();
259 }
260 
261 
262 YTab*
263 BALMLayout::BottomOf(const BView* view) const
264 {
265 	return AreaFor(view)->Bottom();
266 }
267 
268 
269 YTab*
270 BALMLayout::BottomOf(const BLayoutItem* item) const
271 {
272 	return AreaFor(item)->Bottom();
273 }
274 
275 
276 void
277 BALMLayout::BuildLayout(GroupItem& item, XTab* left, YTab* top, XTab* right,
278 	YTab* bottom)
279 {
280 	if (!left)
281 		left = Left();
282 	if (!top)
283 		top = Top();
284 	if (!right)
285 		right = Right();
286 	if (!bottom)
287 		bottom = Bottom();
288 
289 	_ParseGroupItem(item, left, top, right, bottom);
290 }
291 
292 
293 void
294 BALMLayout::_ParseGroupItem(GroupItem& item, XTab* left, YTab* top, XTab* right,
295 	YTab* bottom)
296 {
297 	if (item.LayoutItem())
298 		AddItem(item.LayoutItem(), left, top, right, bottom);
299 	else if (item.View()) {
300 		AddView(item.View(), left, top, right, bottom);
301 	}
302 	else {
303 		for (unsigned int i = 0; i < item.GroupItems().size(); i++) {
304 			GroupItem& current = const_cast<GroupItem&>(
305 				item.GroupItems()[i]);
306 			if (item.Orientation() == B_HORIZONTAL) {
307 				XTab* r = (i == item.GroupItems().size() - 1) ? right
308 					: AddXTab();
309 				_ParseGroupItem(current, left, top, r, bottom);
310 				left = r;
311 			}
312 			else {
313 				YTab* b = (i == item.GroupItems().size() - 1) ? bottom
314 					: AddYTab();
315 				_ParseGroupItem(current, left, top, right, b);
316 				top = b;
317 			}
318 		}
319 	}
320 }
321 
322 
323 BLayoutItem*
324 BALMLayout::AddView(BView* child)
325 {
326 	return AddView(-1, child);
327 }
328 
329 
330 BLayoutItem*
331 BALMLayout::AddView(int32 index, BView* child)
332 {
333 	return BAbstractLayout::AddView(index, child);
334 }
335 
336 
337 /**
338  * Adds a new area to the specification, automatically setting preferred size constraints.
339  *
340  * @param left			left border
341  * @param top			top border
342  * @param right		right border
343  * @param bottom		bottom border
344  * @param content		the control which is the area content
345  * @return the new area
346  */
347 Area*
348 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right,
349 	YTab* bottom)
350 {
351 	BLayoutItem* item = _CreateLayoutItem(view);
352 	Area* area = AddItem(item, left, top, right, bottom);
353 	if (!area) {
354 		delete item;
355 		return NULL;
356 	}
357 	return area;
358 }
359 
360 
361 /**
362  * Adds a new area to the specification, automatically setting preferred size constraints.
363  *
364  * @param row			the row that defines the top and bottom border
365  * @param column		the column that defines the left and right border
366  * @param content		the control which is the area content
367  * @return the new area
368  */
369 Area*
370 BALMLayout::AddView(BView* view, Row* row, Column* column)
371 {
372 	BLayoutItem* item = _CreateLayoutItem(view);
373 	Area* area = AddItem(item, row, column);
374 	if (!area) {
375 		delete item;
376 		return NULL;
377 	}
378 	return area;
379 }
380 
381 
382 Area*
383 BALMLayout::AddViewToRight(BView* view, XTab* right, YTab* top, YTab* bottom)
384 {
385 	BLayoutItem* item = _CreateLayoutItem(view);
386 	Area* area = AddItemToRight(item, right, top, bottom);
387 	if (!area) {
388 		delete item;
389 		return NULL;
390 	}
391 	return area;
392 }
393 
394 
395 Area*
396 BALMLayout::AddViewToLeft(BView* view, XTab* left, YTab* top, YTab* bottom)
397 {
398 	BLayoutItem* item = _CreateLayoutItem(view);
399 	Area* area = AddItemToLeft(item, left, top, bottom);
400 	if (!area) {
401 		delete item;
402 		return NULL;
403 	}
404 	return area;
405 }
406 
407 
408 Area*
409 BALMLayout::AddViewToTop(BView* view, YTab* top, XTab* left, XTab* right)
410 {
411 	BLayoutItem* item = _CreateLayoutItem(view);
412 	Area* area = AddItemToTop(item, top, left, right);
413 	if (!area) {
414 		delete item;
415 		return NULL;
416 	}
417 	return area;
418 }
419 
420 
421 Area*
422 BALMLayout::AddViewToBottom(BView* view, YTab* bottom, XTab* left, XTab* right)
423 {
424 	BLayoutItem* item = _CreateLayoutItem(view);
425 	Area* area = AddItemToBottom(item, bottom, left, right);
426 	if (!area) {
427 		delete item;
428 		return NULL;
429 	}
430 	return area;
431 }
432 
433 
434 bool
435 BALMLayout::AddItem(BLayoutItem* item)
436 {
437 	return AddItem(-1, item);
438 }
439 
440 
441 bool
442 BALMLayout::AddItem(int32 index, BLayoutItem* item)
443 {
444 	if (!item)
445 		return false;
446 
447 	// simply add the item at the upper right corner of the previous item
448 	// TODO maybe find a more elegant solution
449 	XTab* left = Left();
450 	YTab* top = Top();
451 
452 	// check range
453 	if (index < 0 || index > CountItems())
454 		index = CountItems();
455 
456 	// for index = 0 we already have set the right tabs
457 	if (index != 0) {
458 		BLayoutItem* prevItem = ItemAt(index - 1);
459 		Area* area = AreaFor(prevItem);
460 		if (area) {
461 			left = area->Right();
462 			top = area->Top();
463 		}
464 	}
465 	Area* area = AddItem(item, left, top);
466 	return area ? true : false;
467 }
468 
469 
470 Area*
471 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* right,
472 	YTab* bottom)
473 {
474 	if (!right)
475 		right = AddXTab();
476 	if (!bottom)
477 		bottom = AddYTab();
478 
479 	if (!BAbstractLayout::AddItem(-1, item))
480 		return NULL;
481 	Area* area = AreaFor(item);
482 	if (!area)
483 		return NULL;
484 	fCurrentArea = area;
485 
486 	area->_Init(fSolver, left, top, right, bottom, fScaleWidth, fScaleHeight);
487 	return area;
488 }
489 
490 
491 Area*
492 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column)
493 {
494 	if (!BAbstractLayout::AddItem(-1, item))
495 		return NULL;
496 	Area* area = AreaFor(item);
497 	if (!area)
498 		return NULL;
499 	fCurrentArea = area;
500 
501 	area->_Init(fSolver, row, column, fScaleWidth, fScaleHeight);
502 	return area;
503 }
504 
505 
506 Area*
507 BALMLayout::AddItemToRight(BLayoutItem* item, XTab* right, YTab* top,
508 	YTab* bottom)
509 {
510 	XTab* left = fCurrentArea->Right();
511 	if (!right)
512 		right = AddXTab();
513 	if (!top)
514 		top = fCurrentArea->Top();
515 	if (!bottom)
516 		bottom = fCurrentArea->Bottom();
517 
518 	return AddItem(item, left, top, right, bottom);
519 }
520 
521 
522 Area*
523 BALMLayout::AddItemToLeft(BLayoutItem* item, XTab* left, YTab* top,
524 	YTab* bottom)
525 {
526 	if (!left)
527 		left = AddXTab();
528 	XTab* right = fCurrentArea->Left();
529 	if (!top)
530 		top = fCurrentArea->Top();
531 	if (!bottom)
532 		bottom = fCurrentArea->Bottom();
533 
534 	return AddItem(item, left, top, right, bottom);
535 }
536 
537 
538 Area*
539 BALMLayout::AddItemToTop(BLayoutItem* item, YTab* top, XTab* left, XTab* right)
540 {
541 	if (!left)
542 		left = fCurrentArea->Left();
543 	if (!right)
544 		right = fCurrentArea->Right();
545 	if (!top)
546 		top = AddYTab();
547 	YTab* bottom = fCurrentArea->Top();
548 
549 	return AddItem(item, left, top, right, bottom);
550 }
551 
552 
553 Area*
554 BALMLayout::AddItemToBottom(BLayoutItem* item, YTab* bottom, XTab* left,
555 	XTab* right)
556 {
557 	if (!left)
558 		left = fCurrentArea->Left();
559 	if (!right)
560 		right = fCurrentArea->Right();
561 	YTab* top = fCurrentArea->Bottom();
562 	if (!bottom)
563 		bottom = AddYTab();
564 
565 	return AddItem(item, left, top, right, bottom);
566 }
567 
568 
569 /**
570  * Gets the left variable.
571  */
572 XTab*
573 BALMLayout::Left() const
574 {
575 	return fLeft;
576 }
577 
578 
579 /**
580  * Gets the right variable.
581  */
582 XTab*
583 BALMLayout::Right() const
584 {
585 	return fRight;
586 }
587 
588 
589 /**
590  * Gets the top variable.
591  */
592 YTab*
593 BALMLayout::Top() const
594 {
595 	return fTop;
596 }
597 
598 
599 /**
600  * Gets the bottom variable.
601  */
602 YTab*
603 BALMLayout::Bottom() const
604 {
605 	return fBottom;
606 }
607 
608 
609 /**
610  * Gets minimum size.
611  */
612 BSize
613 BALMLayout::BaseMinSize() {
614 	if (fMinSize == kUnsetSize)
615 		fMinSize = _CalculateMinSize();
616 	return fMinSize;
617 }
618 
619 
620 /**
621  * Gets maximum size.
622  */
623 BSize
624 BALMLayout::BaseMaxSize()
625 {
626 	if (fMaxSize == kUnsetSize)
627 		fMaxSize = _CalculateMaxSize();
628 	return fMaxSize;
629 }
630 
631 
632 /**
633  * Gets preferred size.
634  */
635 BSize
636 BALMLayout::BasePreferredSize()
637 {
638 	if (fPreferredSize == kUnsetSize)
639 		fPreferredSize = _CalculatePreferredSize();
640 	return fPreferredSize;
641 }
642 
643 
644 /**
645  * Gets the alignment.
646  */
647 BAlignment
648 BALMLayout::BaseAlignment()
649 {
650 	BAlignment alignment;
651 	alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER);
652 	alignment.SetVertical(B_ALIGN_VERTICAL_CENTER);
653 	return alignment;
654 }
655 
656 
657 /**
658  * Invalidates the layout.
659  * Resets minimum/maximum/preferred size.
660  */
661 void
662 BALMLayout::InvalidateLayout(bool children)
663 {
664 	BLayout::InvalidateLayout(children);
665 	fMinSize = kUnsetSize;
666 	fMaxSize = kUnsetSize;
667 	fPreferredSize = kUnsetSize;
668 }
669 
670 
671 bool
672 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
673 {
674 	item->SetLayoutData(new(std::nothrow) Area(item));
675 	return item->LayoutData() != NULL;
676 }
677 
678 
679 void
680 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
681 {
682 	if (Area* area = AreaFor(item)) {
683 		item->SetLayoutData(NULL);
684 		delete area;
685 	}
686 }
687 
688 
689 /**
690  * Calculate and set the layout.
691  * If no layout specification is given, a specification is reverse engineered automatically.
692  */
693 void
694 BALMLayout::DerivedLayoutItems()
695 {
696 	_UpdateAreaConstraints();
697 
698 	// Enforced absolute positions of Right and Bottom
699 	BRect area(LayoutArea());
700 	Right()->SetRange(area.right, area.right);
701 	Bottom()->SetRange(area.bottom, area.bottom);
702 
703 	_SolveLayout();
704 
705 	// if new layout is infeasible, use previous layout
706 	if (fSolver->Result() == INFEASIBLE)
707 		return;
708 
709 	if (fSolver->Result() != OPTIMAL) {
710 		fSolver->Save("failed-layout.txt");
711 		printf("Could not solve the layout specification (%d). ",
712 			fSolver->Result());
713 		printf("Saved specification in file failed-layout.txt\n");
714 	}
715 
716 	// set the calculated positions and sizes for every area
717 	for (int32 i = 0; i < CountItems(); i++)
718 		AreaFor(ItemAt(i))->_DoLayout();
719 }
720 
721 
722 /**
723  * Gets the path of the performance log file.
724  *
725  * @return the path of the performance log file
726  */
727 char*
728 BALMLayout::PerformancePath() const
729 {
730 	return fPerformancePath;
731 }
732 
733 
734 /**
735  * Sets the path of the performance log file.
736  *
737  * @param path	the path of the performance log file
738  */
739 void
740 BALMLayout::SetPerformancePath(char* path)
741 {
742 	fPerformancePath = path;
743 }
744 
745 
746 LinearSpec*
747 BALMLayout::Solver() const
748 {
749 	return const_cast<LinearSpec*>(fSolver);
750 }
751 
752 
753 void
754 BALMLayout::SetInset(float inset)
755 {
756 	fInset = inset;
757 }
758 
759 
760 float
761 BALMLayout::Inset() const
762 {
763 	return fInset;
764 }
765 
766 
767 void
768 BALMLayout::SetSpacing(float spacing)
769 {
770 	fSpacing = spacing;
771 }
772 
773 
774 float
775 BALMLayout::Spacing() const
776 {
777 	return fSpacing;
778 }
779 
780 
781 BLayoutItem*
782 BALMLayout::_CreateLayoutItem(BView* view)
783 {
784 	return new(std::nothrow) BViewLayoutItem(view);
785 }
786 
787 
788 void
789 BALMLayout::_SolveLayout()
790 {
791 	// Try to solve the layout until the result is OPTIMAL or INFEASIBLE,
792 	// maximally 15 tries sometimes the solving algorithm encounters numerical
793 	// problems (NUMFAILURE), and repeating the solving often helps to overcome
794 	// them.
795 	BFile* file = NULL;
796 	if (fPerformancePath != NULL) {
797 		file = new(std::nothrow) BFile(fPerformancePath,
798 			B_READ_WRITE | B_CREATE_FILE | B_OPEN_AT_END);
799 	}
800 
801 	ResultType result;
802 	for (int32 tries = 0; tries < 15; tries++) {
803 		result = fSolver->Solve();
804 		if (fPerformancePath != NULL) {
805 			/*char buffer [100];
806 			file->Write(buffer, sprintf(buffer, "%d\t%fms\t#vars=%ld\t"
807 				"#constraints=%ld\n", result, fSolver->SolvingTime(),
808 				fSolver->Variables()->CountItems(),
809 				fSolver->Constraints()->CountItems()));*/
810 		}
811 		if (result == OPTIMAL || result == INFEASIBLE)
812 			break;
813 	}
814 	delete file;
815 }
816 
817 
818 /**
819  * Caculates the miminum size.
820  */
821 BSize
822 BALMLayout::_CalculateMinSize()
823 {
824 	_UpdateAreaConstraints();
825 
826 	SummandList* newObjFunction = new(std::nothrow) SummandList(2);
827 	newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fRight));
828 	newObjFunction->AddItem(new(std::nothrow) Summand(1.0, fBottom));
829 	SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
830 		newObjFunction);
831 	_SolveLayout();
832 	fSolver->SetObjectiveFunction(oldObjFunction);
833 
834 	if (fSolver->Result() == UNBOUNDED)
835 		return kMinSize;
836 	if (fSolver->Result() != OPTIMAL) {
837 		fSolver->Save("failed-layout.txt");
838 		printf("Could not solve the layout specification (%d). "
839 			"Saved specification in file failed-layout.txt", fSolver->Result());
840 	}
841 
842 	return BSize(Right()->Value() - Left()->Value(),
843 		Bottom()->Value() - Top()->Value());
844 }
845 
846 
847 /**
848  * Caculates the maximum size.
849  */
850 BSize
851 BALMLayout::_CalculateMaxSize()
852 {
853 	_UpdateAreaConstraints();
854 
855 	SummandList* newObjFunction = new(std::nothrow) SummandList(2);
856 	newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fRight));
857 	newObjFunction->AddItem(new(std::nothrow) Summand(-1.0, fBottom));
858 	SummandList* oldObjFunction = fSolver->SwapObjectiveFunction(
859 		newObjFunction);
860 	_SolveLayout();
861 	fSolver->SetObjectiveFunction(oldObjFunction);
862 
863 	if (fSolver->Result() == UNBOUNDED)
864 		return kMaxSize;
865 	if (fSolver->Result() != OPTIMAL) {
866 		fSolver->Save("failed-layout.txt");
867 		printf("Could not solve the layout specification (%d). "
868 			"Saved specification in file failed-layout.txt", fSolver->Result());
869 	}
870 
871 	return BSize(Right()->Value() - Left()->Value(),
872 		Bottom()->Value() - Top()->Value());
873 }
874 
875 
876 /**
877  * Caculates the preferred size.
878  */
879 BSize
880 BALMLayout::_CalculatePreferredSize()
881 {
882 	_UpdateAreaConstraints();
883 
884 	_SolveLayout();
885 	if (fSolver->Result() != OPTIMAL) {
886 		fSolver->Save("failed-layout.txt");
887 		printf("Could not solve the layout specification (%d). "
888 			"Saved specification in file failed-layout.txt", fSolver->Result());
889 	}
890 
891 	return BSize(Right()->Value() - Left()->Value(),
892 		Bottom()->Value() - Top()->Value());
893 }
894 
895 
896 void
897 BALMLayout::_UpdateAreaConstraints()
898 {
899 	for (int i = 0; i < CountItems(); i++)
900 		AreaFor(ItemAt(i))->InvalidateSizeConstraints();
901 }
902