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