xref: /haiku/src/libs/alm/Area.cpp (revision e8cd7007416a323259791ac09c013dcce2956976)
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 "Area.h"
10 
11 #include <algorithm>	// for max
12 
13 #include <Button.h>
14 #include <CheckBox.h>
15 #include <PictureButton.h>
16 #include <RadioButton.h>
17 #include <StatusBar.h>
18 #include <StringView.h>
19 
20 #include "ALMLayout.h"
21 
22 
23 using namespace LinearProgramming;
24 using namespace std;
25 
26 
27 GroupItem::GroupItem(BLayoutItem* item)
28 {
29 	_Init(item, NULL);
30 }
31 
32 
33 GroupItem::GroupItem(BView* view)
34 {
35 	_Init(NULL, view);
36 }
37 
38 
39 BLayoutItem*
40 GroupItem::LayoutItem()
41 {
42 	return fLayoutItem;
43 }
44 
45 
46 BView*
47 GroupItem::View()
48 {
49 	return fView;
50 }
51 
52 
53 const std::vector<GroupItem>&
54 GroupItem::GroupItems()
55 {
56 	return fGroupItems;
57 }
58 
59 
60 enum orientation
61 GroupItem::Orientation()
62 {
63 	return fOrientation;
64 }
65 
66 
67 GroupItem&
68 GroupItem::operator|(const GroupItem& right)
69 {
70 	return _AddItem(right, B_HORIZONTAL);
71 }
72 
73 
74 GroupItem&
75 GroupItem::operator/(const GroupItem& bottom)
76 {
77 	return _AddItem(bottom, B_VERTICAL);
78 }
79 
80 
81 GroupItem::GroupItem()
82 {
83 	_Init(NULL, NULL);
84 }
85 
86 
87 void
88 GroupItem::_Init(BLayoutItem* item, BView* view, enum orientation orien)
89 {
90 	fLayoutItem = item;
91 	fView = view;
92 	fOrientation = orien;
93 }
94 
95 
96 GroupItem&
97 GroupItem::_AddItem(const GroupItem& item, enum orientation orien)
98 {
99 	if (fGroupItems.size() == 0)
100 		fGroupItems.push_back(*this);
101 	else if (fOrientation != orien) {
102 		GroupItem clone = *this;
103 		fGroupItems.clear();
104 		_Init(NULL, NULL, orien);
105 		fGroupItems.push_back(clone);
106 	}
107 
108 	_Init(NULL, NULL, orien);
109 	fGroupItems.push_back(item);
110 	return *this;
111 }
112 
113 
114 BView*
115 Area::View()
116 {
117 	return fLayoutItem->View();
118 }
119 
120 
121 /**
122  * Gets the left tab of the area.
123  *
124  * @return the left tab of the area
125  */
126 XTab*
127 Area::Left() const
128 {
129 	return fLeft;
130 }
131 
132 
133 /**
134  * Gets the right tab of the area.
135  *
136  * @return the right tab of the area
137  */
138 XTab*
139 Area::Right() const
140 {
141 	return fRight;
142 }
143 
144 
145 /**
146  * Gets the top tab of the area.
147  */
148 YTab*
149 Area::Top() const
150 {
151 	return fTop;
152 }
153 
154 
155 /**
156  * Gets the bottom tab of the area.
157  */
158 YTab*
159 Area::Bottom() const
160 {
161 	return fBottom;
162 }
163 
164 
165 /**
166  * Sets the left tab of the area.
167  *
168  * @param left	the left tab of the area
169  */
170 void
171 Area::SetLeft(XTab* left)
172 {
173 	fLeft = left;
174 
175 	fColumn = NULL;
176 
177 	fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
178 	BSize preferredSize = fLayoutItem->PreferredSize();
179 	_UpdatePreferredWidthConstraint(preferredSize);
180 
181 	if (fMaxContentWidth != NULL)
182 		fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
183 
184 	fLayoutItem->Layout()->InvalidateLayout();
185 }
186 
187 
188 /**
189  * Sets the right tab of the area.
190  *
191  * @param right	the right tab of the area
192  */
193 void
194 Area::SetRight(XTab* right)
195 {
196 	fRight = right;
197 
198 	fColumn = NULL;
199 
200 	fMinContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
201 	BSize preferredSize = fLayoutItem->PreferredSize();
202 	_UpdatePreferredWidthConstraint(preferredSize);
203 	if (fMaxContentWidth != NULL)
204 		fMaxContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight);
205 
206 	fLayoutItem->Layout()->InvalidateLayout();
207 }
208 
209 
210 /**
211  * Sets the top tab of the area.
212  */
213 void
214 Area::SetTop(YTab* top)
215 {
216 	fTop = top;
217 
218 	fRow = NULL;
219 
220 	fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
221 	BSize preferredSize = fLayoutItem->PreferredSize();
222 	_UpdatePreferredHeightConstraint(preferredSize);
223 	if (fMaxContentHeight != NULL)
224 		fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
225 
226 	fLayoutItem->Layout()->InvalidateLayout();
227 }
228 
229 
230 /**
231  * Sets the bottom tab of the area.
232  */
233 void
234 Area::SetBottom(YTab* bottom)
235 {
236 	fBottom = bottom;
237 
238 	fRow = NULL;
239 
240 	fMinContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
241 	BSize preferredSize = fLayoutItem->PreferredSize();
242 	_UpdatePreferredHeightConstraint(preferredSize);
243 	if (fMaxContentHeight != NULL)
244 		fMaxContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom);
245 
246 	fLayoutItem->Layout()->InvalidateLayout();
247 }
248 
249 
250 /**
251  * Gets the row that defines the top and bottom tabs.
252  */
253 Row*
254 Area::GetRow() const
255 {
256 	return fRow;
257 }
258 
259 
260 /**
261  * Gets the column that defines the left and right tabs.
262  */
263 Column*
264 Area::GetColumn() const
265 {
266 	return fColumn;
267 }
268 
269 
270 /**
271  * Sets the row that defines the top and bottom tabs.
272  * May be null.
273  */
274 void
275 Area::SetRow(Row* row)
276 {
277 	SetTop(row->Top());
278 	SetBottom(row->Bottom());
279 	fRow = row;
280 	fLayoutItem->Layout()->InvalidateLayout();
281 }
282 
283 
284 /**
285  * Sets the column that defines the left and right tabs.
286  * May be null.
287  */
288 void
289 Area::SetColumn(Column* column)
290 {
291 	SetLeft(column->Left());
292 	SetRight(column->Right());
293 	fColumn = column;
294 	fLayoutItem->Layout()->InvalidateLayout();
295 }
296 
297 
298 /**
299  * The reluctance with which the area's content shrinks below its preferred size.
300  * The bigger the less likely is such shrinking.
301  */
302 BSize
303 Area::ShrinkPenalties() const
304 {
305 	return fShrinkPenalties;
306 }
307 
308 
309 /**
310  * The reluctance with which the area's content grows over its preferred size.
311  * The bigger the less likely is such growth.
312  */
313 BSize
314 Area::GrowPenalties() const
315 {
316 	return fGrowPenalties;
317 }
318 
319 
320 void Area::SetShrinkPenalties(BSize shrink) {
321 	fShrinkPenalties = shrink;
322 	if (fPreferredContentWidth != NULL) {
323 		fPreferredContentWidth->SetPenaltyNeg(shrink.Width());
324 		fPreferredContentHeight->SetPenaltyNeg(shrink.Height());
325 	}
326 	fLayoutItem->Layout()->InvalidateLayout();
327 }
328 
329 
330 void
331 Area::SetGrowPenalties(BSize grow)
332 {
333 	fGrowPenalties = grow;
334 	if (fPreferredContentWidth != NULL) {
335 		fPreferredContentWidth->SetPenaltyPos(grow.Width());
336 		fPreferredContentHeight->SetPenaltyPos(grow.Height());
337 	}
338 	fLayoutItem->Layout()->InvalidateLayout();
339 }
340 
341 
342 /**
343  * Gets aspect ratio of the area's content.
344  */
345 double
346 Area::ContentAspectRatio() const
347 {
348 	return fContentAspectRatio;
349 }
350 
351 
352 /**
353  * Sets aspect ratio of the area's content.
354  * May be different from the aspect ratio of the area.
355  */
356 void
357 Area::SetContentAspectRatio(double ratio)
358 {
359 	fContentAspectRatio = ratio;
360 	if (fContentAspectRatio <= 0) {
361 		delete fContentAspectRatioC;
362 		fContentAspectRatioC = NULL;
363 	} else if (fContentAspectRatioC == NULL) {
364 		fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
365 			ratio, fTop, -ratio, fBottom, kEQ, 0.0);
366 		fConstraints.AddItem(fContentAspectRatioC);
367 	} else {
368 		fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio,
369 			fTop, -ratio, fBottom);
370 	}
371 	fLayoutItem->Layout()->InvalidateLayout();
372 }
373 
374 
375 /**
376  * Gets left inset between area and its content.
377  */
378 float
379 Area::LeftInset() const
380 {
381 	if (fTopLeftInset.IsWidthSet())
382 		return fTopLeftInset.Width();
383 
384 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
385 	if (fLeft == layout->Left())
386 		return layout->Inset();
387 	return layout->Spacing() / 2;
388 }
389 
390 
391 /**
392  * Gets top inset between area and its content.
393  */
394 float
395 Area::TopInset() const
396 {
397 	if (fTopLeftInset.IsHeightSet())
398 		return fTopLeftInset.Height();
399 
400 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
401 	if (fTop == layout->Top())
402 		return layout->Inset();
403 	return layout->Spacing() / 2;
404 }
405 
406 
407 /**
408  * Gets right inset between area and its content.
409  */
410 float
411 Area::RightInset() const
412 {
413 	if (fRightBottomInset.IsWidthSet())
414 		return fRightBottomInset.Width();
415 
416 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
417 	if (fRight == layout->Right())
418 		return layout->Inset();
419 	return layout->Spacing() / 2;
420 }
421 
422 
423 /**
424  * Gets bottom inset between area and its content.
425  */
426 float
427 Area::BottomInset() const
428 {
429 	if (fRightBottomInset.IsHeightSet())
430 		return fRightBottomInset.Height();
431 
432 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
433 	if (fBottom == layout->Bottom())
434 		return layout->Inset();
435 	return layout->Spacing() / 2;
436 }
437 
438 
439 /**
440  * Sets left inset between area and its content.
441  */
442 void
443 Area::SetLeftInset(float left)
444 {
445 	fTopLeftInset.width = left;
446 	fLayoutItem->Layout()->InvalidateLayout();
447 }
448 
449 
450 /**
451  * Sets top inset between area and its content.
452  */
453 void
454 Area::SetTopInset(float top)
455 {
456 	fTopLeftInset.height = top;
457 	fLayoutItem->Layout()->InvalidateLayout();
458 }
459 
460 
461 /**
462  * Sets right inset between area and its content.
463  */
464 void
465 Area::SetRightInset(float right)
466 {
467 	fRightBottomInset.width = right;
468 	fLayoutItem->Layout()->InvalidateLayout();
469 }
470 
471 
472 /**
473  * Sets bottom inset between area and its content.
474  */
475 void
476 Area::SetBottomInset(float bottom)
477 {
478 	fRightBottomInset.height = bottom;
479 	fLayoutItem->Layout()->InvalidateLayout();
480 }
481 
482 
483 Area::operator BString() const
484 {
485 	BString string;
486 	GetString(string);
487 	return string;
488 }
489 
490 
491 void
492 Area::GetString(BString& string) const
493 {
494 	string << "Area(";
495 	fLeft->GetString(string);
496 	string << ", ";
497 	fTop->GetString(string);
498 	string << ", ";
499 	fRight->GetString(string);
500 	string << ", ";
501 	fBottom->GetString(string);
502 	string << ")";
503 }
504 
505 
506 /*!
507  * Sets the width of the area to be the same as the width of the given area
508  * times factor.
509  *
510  * @param area	the area that should have the same width
511  * @return the same-width constraint
512  */
513 Constraint*
514 Area::SetWidthAs(Area* area, float factor)
515 {
516 	return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(),
517 		-factor, area->Right(), kEQ, 0.0);
518 }
519 
520 
521 /*!
522  * Sets the height of the area to be the same as the height of the given area
523  * times factor.
524  *
525  * @param area	the area that should have the same height
526  * @return the same-height constraint
527  */
528 Constraint*
529 Area::SetHeightAs(Area* area, float factor)
530 {
531 	return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(),
532 		-factor, area->Bottom(), kEQ, 0.0);
533 }
534 
535 
536 void
537 Area::InvalidateSizeConstraints()
538 {
539 	// check if if we are initialized
540 	if (!fLeft)
541 		return;
542 
543 	BSize minSize = fLayoutItem->MinSize();
544 	BSize maxSize = fLayoutItem->MaxSize();
545 	BSize prefSize = fLayoutItem->PreferredSize();
546 
547 	_UpdateMinSizeConstraint(minSize);
548 	_UpdateMaxSizeConstraint(maxSize);
549 	_UpdatePreferredWidthConstraint(prefSize);
550 	_UpdatePreferredHeightConstraint(prefSize);
551 }
552 
553 
554 /**
555  * Destructor.
556  * Removes the area from its specification.
557  */
558 Area::~Area()
559 {
560 	for (int32 i = 0; i < fConstraints.CountItems(); i++)
561 		delete (Constraint*)fConstraints.ItemAt(i);
562 }
563 
564 
565 /**
566  * Constructor.
567  * Uses XTabs and YTabs.
568  */
569 Area::Area(BLayoutItem* item)
570 	:
571 	fLayoutItem(item),
572 
573 	fLS(NULL),
574 	fLeft(NULL),
575 	fRight(NULL),
576 	fTop(NULL),
577 	fBottom(NULL),
578 
579 	fRow(NULL),
580 	fColumn(NULL),
581 
582 	fShrinkPenalties(5, 5),
583 	fGrowPenalties(5, 5),
584 
585 	fMinContentWidth(NULL),
586 	fMaxContentWidth(NULL),
587 	fMinContentHeight(NULL),
588 	fMaxContentHeight(NULL),
589 	fPreferredContentWidth(NULL),
590 	fPreferredContentHeight(NULL),
591 
592 	fContentAspectRatio(-1),
593 	fContentAspectRatioC(NULL)
594 {
595 
596 }
597 
598 
599 /**
600  * Initialize variables.
601  */
602 #if USE_SCALE_VARIABLE
603 void
604 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
605 	Variable* scaleWidth, Variable* scaleHeight)
606 {
607 	fScaleWidth = scaleWidth;
608 	fScaleHeight = scaleHeight;
609 #else
610 void
611 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom)
612 {
613 #endif
614 	fLS = ls;
615 	fLeft = left;
616 	fRight = right;
617 	fTop = top;
618 	fBottom = bottom;
619 
620 	// adds the two essential constraints of the area that make sure that the
621 	// left x-tab is really to the left of the right x-tab, and the top y-tab
622 	// really above the bottom y-tab
623 	fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0);
624 	fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0);
625 
626 	fConstraints.AddItem(fMinContentWidth);
627 	fConstraints.AddItem(fMinContentHeight);
628 
629 	_SetupPreferredConstraints();
630 
631 	BSize preferredSize = fLayoutItem->PreferredSize();
632 	_UpdatePreferredWidthConstraint(preferredSize);
633 	_UpdatePreferredHeightConstraint(preferredSize);
634 
635 	fConstraints.AddItem(fPreferredContentWidth);
636 	fConstraints.AddItem(fPreferredContentHeight);
637 }
638 
639 
640 #if USE_SCALE_VARIABLE
641 void
642 Area::_Init(LinearSpec* ls, Row* row, Column* column, Variable* scaleWidth,
643 	Variable* scaleHeight)
644 {
645 	_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(),
646 		scaleWidth, scaleHeight);
647 #else
648 void
649 Area::_Init(LinearSpec* ls, Row* row, Column* column)
650 {
651 	_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom());
652 #endif
653 	fRow = row;
654 	fColumn = column;
655 }
656 
657 
658 /**
659  * Perform layout on the area.
660  */
661 void
662 Area::_DoLayout()
663 {
664 	// check if if we are initialized
665 	if (!fLeft)
666 		return;
667 
668 	BRect areaFrame(round(fLeft->Value()), round(fTop->Value()),
669 		round(fRight->Value()), round(fBottom->Value()));
670 	areaFrame.left += LeftInset();
671 	areaFrame.right -= RightInset();
672 	areaFrame.top += TopInset();
673 	areaFrame.bottom -= BottomInset();
674 
675 	fLayoutItem->AlignInFrame(areaFrame);
676 }
677 
678 
679 void
680 Area::_UpdateMinSizeConstraint(BSize min)
681 {
682 	float width = 0.;
683 	float height = 0.;
684 	if (min.width > 0)
685 		width = min.Width() + LeftInset() + RightInset();
686 	if (min.height > 0)
687 		height = min.Height() + TopInset() + BottomInset();
688 
689 	fMinContentWidth->SetRightSide(width);
690 	fMinContentHeight->SetRightSide(height);
691 }
692 
693 
694 void
695 Area::_UpdateMaxSizeConstraint(BSize max)
696 {
697 	max.width += LeftInset() + RightInset();
698 	max.height += TopInset() + BottomInset();
699 
700 	// we only need max constraints if the alignment is full height/width
701 	// otherwise we can just align the item in the free space
702 	BAlignment alignment = fLayoutItem->Alignment();
703 	if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) {
704 		if (fMaxContentHeight == NULL) {
705 			fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
706 				kLE, max.Height());
707 			fConstraints.AddItem(fMaxContentHeight);
708 		} else
709 			fMaxContentHeight->SetRightSide(max.Height());
710 	}
711 	else {
712 		fConstraints.RemoveItem(fMaxContentHeight);
713 		delete fMaxContentHeight;
714 		fMaxContentHeight = NULL;
715 	}
716 
717 	if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) {
718 		if (fMaxContentWidth == NULL) {
719 			fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE,
720 				max.Width());
721 			fConstraints.AddItem(fMaxContentWidth);
722 		} else
723 			fMaxContentWidth->SetRightSide(max.Width());
724 	}
725 	else {
726 		fConstraints.RemoveItem(fMaxContentWidth);
727 		delete fMaxContentWidth;
728 		fMaxContentWidth = NULL;
729 	}
730 }
731 
732 
733 void
734 Area::_UpdatePreferredWidthConstraint(BSize& preferred)
735 {
736 	if (preferred.width == -1) {
737 		delete fPreferredContentWidth;
738 		fPreferredContentWidth = NULL;
739 		return;
740 	}
741 
742 	float width = 0;
743 	if (preferred.width > 0)
744 		width = preferred.width + LeftInset() + RightInset();
745 
746 	_SetupPreferredConstraints();
747 
748 #if USE_SCALE_VARIABLE
749 	fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight, -width,
750 		fScaleWidth);
751 #else
752 	fPreferredContentWidth->SetRightSide(width);
753 #endif
754 }
755 
756 
757 void
758 Area::_UpdatePreferredHeightConstraint(BSize& preferred)
759 {
760 	if (preferred.height == -1) {
761 		delete fPreferredContentHeight;
762 		fPreferredContentHeight = NULL;
763 		return;
764 	}
765 
766 	float height = 0;
767 	if (preferred.height > 0)
768 		height = preferred.height + TopInset() + BottomInset();
769 
770 	_SetupPreferredConstraints();
771 #if USE_SCALE_VARIABLE
772 	fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom, -height,
773 		fScaleHeight);
774 #else
775 	fPreferredContentHeight->SetRightSide(height);
776 #endif
777 }
778 
779 
780 void
781 Area::_SetupPreferredConstraints()
782 {
783 #if USE_SCALE_VARIABLE
784 	if (!fPreferredContentWidth) {
785 		fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
786 			-1.0, fScaleWidth, kEQ, 0, fShrinkPenalties.Width(),
787 			fGrowPenalties.Width());
788 	}
789 	if (!fPreferredContentHeight) {
790 		fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
791 			-1.0, fScaleHeight, kEQ, 0, fShrinkPenalties.Height(),
792 			fGrowPenalties.Height());
793 	}
794 #else
795 	if (!fPreferredContentWidth) {
796 		fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight,
797 			kEQ, 0, fShrinkPenalties.Width(), fGrowPenalties.Width());
798 	}
799 	if (!fPreferredContentHeight) {
800 		fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
801 			kEQ, 0, fShrinkPenalties.Height(), fGrowPenalties.Height());
802 	}
803 #endif
804 }
805