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