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