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*
Item()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*
Left() const37 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*
Right() const49 Area::Right() const
50 {
51 return fRight;
52 }
53
54
55 /**
56 * Gets the top tab of the area.
57 */
58 YTab*
Top() const59 Area::Top() const
60 {
61 return fTop;
62 }
63
64
65 /**
66 * Gets the bottom tab of the area.
67 */
68 YTab*
Bottom() const69 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
SetLeft(BReference<XTab> left)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
SetRight(BReference<XTab> right)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
SetTop(BReference<YTab> top)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
SetBottom(BReference<YTab> bottom)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*
GetRow() const151 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*
GetColumn() const161 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
ShrinkPenalties() const172 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
GrowPenalties() const183 Area::GrowPenalties() const
184 {
185 return fGrowPenalties;
186 }
187
188
189 void
SetShrinkPenalties(BSize shrink)190 Area::SetShrinkPenalties(BSize shrink) {
191 fShrinkPenalties = shrink;
192
193 fLayoutItem->Layout()->InvalidateLayout();
194 }
195
196
197 void
SetGrowPenalties(BSize grow)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
ContentAspectRatio() const210 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
SetContentAspectRatio(double ratio)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
GetInsets(float * left,float * top,float * right,float * bottom) const241 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
LeftInset() const258 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
TopInset() const272 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
RightInset() const286 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
BottomInset() const300 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
SetInsets(float insets)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
SetInsets(float horizontal,float vertical)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
SetInsets(float left,float top,float right,float bottom)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
SetLeftInset(float left)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
SetTopInset(float top)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
SetRightInset(float right)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
SetBottomInset(float bottom)391 Area::SetBottomInset(float bottom)
392 {
393 fRightBottomInset.height = bottom;
394 fLayoutItem->Layout()->InvalidateLayout();
395 }
396
397
398 BString
ToString() const399 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*
SetWidthAs(Area * area,float factor)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*
SetHeightAs(Area * area,float factor)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
InvalidateSizeConstraints()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
Frame() const460 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 */
~Area()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
new_area_id()484 new_area_id()
485 {
486 return sAreaID++;
487 }
488
489
490 /**
491 * Constructor.
492 * Uses XTabs and YTabs.
493 */
Area(BLayoutItem * item)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
ID() const519 Area::ID() const
520 {
521 return fID;
522 }
523
524
525 void
SetID(int32 id)526 Area::SetID(int32 id)
527 {
528 fID = id;
529 }
530
531
532 /**
533 * Initialize variables.
534 */
535 void
_Init(LinearSpec * ls,XTab * left,YTab * top,XTab * right,YTab * bottom,RowColumnManager * manager)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
_Init(LinearSpec * ls,Row * row,Column * column,RowColumnManager * manager)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
_DoLayout(const BPoint & offset)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
_UpdateMinSizeConstraint(BSize min)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
_UpdateMaxSizeConstraint(BSize max)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