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