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