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