xref: /haiku/src/libs/alm/Area.cpp (revision 70d5966963513ff47a456ba70b7d2eec0f99648d)
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 #if USE_SCALE_VARIABLE
630 	fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, -1.0,
631 		fScaleWidth, kEQ, 0, fShrinkPenalties.Width(),
632 		fGrowPenalties.Width());
633 
634 	fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, -1.0,
635 		fScaleHeight, kEQ, 0, fShrinkPenalties.Height(),
636 		fGrowPenalties.Height());
637 #else
638 	fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kEQ,
639 		0, fShrinkPenalties.Width(), fGrowPenalties.Width());
640 	fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, kEQ,
641 		0, fShrinkPenalties.Height(), fGrowPenalties.Height());
642 #endif
643 
644 	BSize preferredSize = fLayoutItem->PreferredSize();
645 	_UpdatePreferredWidthConstraint(preferredSize);
646 	_UpdatePreferredHeightConstraint(preferredSize);
647 
648 	fConstraints.AddItem(fPreferredContentWidth);
649 	fConstraints.AddItem(fPreferredContentHeight);
650 }
651 
652 
653 #if USE_SCALE_VARIABLE
654 void
655 Area::_Init(LinearSpec* ls, Row* row, Column* column, Variable* scaleWidth,
656 	Variable* scaleHeight)
657 {
658 	_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom(),
659 		scaleWidth, scaleHeight);
660 #else
661 void
662 Area::_Init(LinearSpec* ls, Row* row, Column* column)
663 {
664 	_Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom());
665 #endif
666 	fRow = row;
667 	fColumn = column;
668 }
669 
670 
671 /**
672  * Perform layout on the area.
673  */
674 void
675 Area::_DoLayout()
676 {
677 	// check if if we are initialized
678 	if (!fLeft)
679 		return;
680 
681 	BRect areaFrame(round(fLeft->Value()), round(fTop->Value()),
682 		round(fRight->Value()), round(fBottom->Value()));
683 	areaFrame.left += LeftInset();
684 	areaFrame.right -= RightInset();
685 	areaFrame.top += TopInset();
686 	areaFrame.bottom -= BottomInset();
687 
688 	fLayoutItem->AlignInFrame(areaFrame);
689 }
690 
691 
692 void
693 Area::_UpdateMinSizeConstraint(BSize min)
694 {
695 	float width = 0.;
696 	float height = 0.;
697 	if (min.width > 0)
698 		width = min.Width() + LeftInset() + RightInset();
699 	if (min.height > 0)
700 		height = min.Height() + TopInset() + BottomInset();
701 
702 	fMinContentWidth->SetRightSide(width);
703 	fMinContentHeight->SetRightSide(height);
704 }
705 
706 
707 void
708 Area::_UpdateMaxSizeConstraint(BSize max)
709 {
710 	max.width += LeftInset() + RightInset();
711 	max.height += TopInset() + BottomInset();
712 
713 	// we only need max constraints if the alignment is full height/width
714 	// otherwise we can just align the item in the free space
715 	BAlignment alignment = fLayoutItem->Alignment();
716 	if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) {
717 		if (fMaxContentHeight == NULL) {
718 			fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
719 				kLE, max.Height());
720 			fConstraints.AddItem(fMaxContentHeight);
721 		} else
722 			fMaxContentHeight->SetRightSide(max.Height());
723 	}
724 	else {
725 		fConstraints.RemoveItem(fMaxContentHeight);
726 		delete fMaxContentHeight;
727 		fMaxContentHeight = NULL;
728 	}
729 
730 	if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) {
731 		if (fMaxContentWidth == NULL) {
732 			fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE,
733 				max.Width());
734 			fConstraints.AddItem(fMaxContentWidth);
735 		} else
736 			fMaxContentWidth->SetRightSide(max.Width());
737 	}
738 	else {
739 		fConstraints.RemoveItem(fMaxContentWidth);
740 		delete fMaxContentWidth;
741 		fMaxContentWidth = NULL;
742 	}
743 }
744 
745 
746 void
747 Area::_UpdatePreferredWidthConstraint(BSize& preferred)
748 {
749 	float width = 0;
750 	if (preferred.width > 0)
751 		width = preferred.Width() + LeftInset() + RightInset();
752 #if USE_SCALE_VARIABLE
753 	fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight, -width,
754 		fScaleWidth);
755 #else
756 	fPreferredContentWidth->SetRightSide(width);
757 #endif
758 }
759 
760 
761 void
762 Area::_UpdatePreferredHeightConstraint(BSize& preferred)
763 {
764 	float height = 0;
765 	if (preferred.height > 0)
766 		height = preferred.Height() + TopInset() + BottomInset();
767 #if USE_SCALE_VARIABLE
768 	fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom, -height,
769 		fScaleHeight);
770 #else
771 	fPreferredContentHeight->SetRightSide(height);
772 #endif
773 }
774