xref: /haiku/src/libs/alm/Area.cpp (revision 681f48fcbccfa556fe01eab457ca1d549736b19e)
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(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(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(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(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 	fLayoutItem->Layout()->InvalidateLayout();
235 }
236 
237 
238 void
239 Area::GetInsets(float* left, float* top, float* right, float* bottom) const
240 {
241 	if (left)
242 		*left = fLeftTopInset.Width();
243 	if (top)
244 		*top = fLeftTopInset.Height();
245 	if (right)
246 		*right = fRightBottomInset.Width();
247 	if (bottom)
248 		*bottom = fRightBottomInset.Height();
249 }
250 
251 
252 /**
253  * Gets left inset between area and its content.
254  */
255 float
256 Area::LeftInset() const
257 {
258 	if (fLeftTopInset.IsWidthSet())
259 		return fLeftTopInset.Width();
260 
261 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
262 	return layout->InsetForTab(fLeft.Get());
263 }
264 
265 
266 /**
267  * Gets top inset between area and its content.
268  */
269 float
270 Area::TopInset() const
271 {
272 	if (fLeftTopInset.IsHeightSet())
273 		return fLeftTopInset.Height();
274 
275 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
276 	return layout->InsetForTab(fTop.Get());
277 }
278 
279 
280 /**
281  * Gets right inset between area and its content.
282  */
283 float
284 Area::RightInset() const
285 {
286 	if (fRightBottomInset.IsWidthSet())
287 		return fRightBottomInset.Width();
288 
289 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
290 	return layout->InsetForTab(fRight.Get());
291 }
292 
293 
294 /**
295  * Gets bottom inset between area and its content.
296  */
297 float
298 Area::BottomInset() const
299 {
300 	if (fRightBottomInset.IsHeightSet())
301 		return fRightBottomInset.Height();
302 
303 	BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout());
304 	return layout->InsetForTab(fBottom.Get());
305 }
306 
307 
308 void
309 Area::SetInsets(float insets)
310 {
311 	if (insets != B_SIZE_UNSET)
312 		insets = BControlLook::ComposeSpacing(insets);
313 
314 	fLeftTopInset.Set(insets, insets);
315 	fRightBottomInset.Set(insets, insets);
316 	fLayoutItem->Layout()->InvalidateLayout();
317 }
318 
319 
320 void
321 Area::SetInsets(float horizontal, float vertical)
322 {
323 	if (horizontal != B_SIZE_UNSET)
324 		horizontal = BControlLook::ComposeSpacing(horizontal);
325 	if (vertical != B_SIZE_UNSET)
326 		vertical = BControlLook::ComposeSpacing(vertical);
327 
328 	fLeftTopInset.Set(horizontal, horizontal);
329 	fRightBottomInset.Set(vertical, vertical);
330 	fLayoutItem->Layout()->InvalidateLayout();
331 }
332 
333 
334 void
335 Area::SetInsets(float left, float top, float right, float bottom)
336 {
337 	if (left != B_SIZE_UNSET)
338 		left = BControlLook::ComposeSpacing(left);
339 	if (right != B_SIZE_UNSET)
340 		right = BControlLook::ComposeSpacing(right);
341 	if (top != B_SIZE_UNSET)
342 		top = BControlLook::ComposeSpacing(top);
343 	if (bottom != B_SIZE_UNSET)
344 		bottom = BControlLook::ComposeSpacing(bottom);
345 
346 	fLeftTopInset.Set(left, top);
347 	fRightBottomInset.Set(right, bottom);
348 	fLayoutItem->Layout()->InvalidateLayout();
349 }
350 
351 
352 /**
353  * Sets left inset between area and its content.
354  */
355 void
356 Area::SetLeftInset(float left)
357 {
358 	fLeftTopInset.width = left;
359 	fLayoutItem->Layout()->InvalidateLayout();
360 }
361 
362 
363 /**
364  * Sets top inset between area and its content.
365  */
366 void
367 Area::SetTopInset(float top)
368 {
369 	fLeftTopInset.height = top;
370 	fLayoutItem->Layout()->InvalidateLayout();
371 }
372 
373 
374 /**
375  * Sets right inset between area and its content.
376  */
377 void
378 Area::SetRightInset(float right)
379 {
380 	fRightBottomInset.width = right;
381 	fLayoutItem->Layout()->InvalidateLayout();
382 }
383 
384 
385 /**
386  * Sets bottom inset between area and its content.
387  */
388 void
389 Area::SetBottomInset(float bottom)
390 {
391 	fRightBottomInset.height = bottom;
392 	fLayoutItem->Layout()->InvalidateLayout();
393 }
394 
395 
396 BString
397 Area::ToString() const
398 {
399 	BString string = "Area(";
400 	string += fLeft->ToString();
401 	string << ", ";
402 	string += fTop->ToString();
403 	string << ", ";
404 	string += fRight->ToString();
405 	string << ", ";
406 	string += fBottom->ToString();
407 	string << ")";
408 	return string;
409 }
410 
411 
412 /*!
413  * Sets the width of the area to be the same as the width of the given area
414  * times factor.
415  *
416  * @param area	the area that should have the same width
417  * @return the same-width constraint
418  */
419 Constraint*
420 Area::SetWidthAs(Area* area, float factor)
421 {
422 	return fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, factor, area->Left(),
423 		-factor, area->Right(), kEQ, 0.0);
424 }
425 
426 
427 /*!
428  * Sets the height of the area to be the same as the height of the given area
429  * times factor.
430  *
431  * @param area	the area that should have the same height
432  * @return the same-height constraint
433  */
434 Constraint*
435 Area::SetHeightAs(Area* area, float factor)
436 {
437 	return fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, factor, area->Top(),
438 		-factor, area->Bottom(), kEQ, 0.0);
439 }
440 
441 
442 void
443 Area::InvalidateSizeConstraints()
444 {
445 	// check if if we are initialized
446 	if (!fLeft)
447 		return;
448 
449 	BSize minSize = fLayoutItem->MinSize();
450 	BSize maxSize = fLayoutItem->MaxSize();
451 
452 	_UpdateMinSizeConstraint(minSize);
453 	_UpdateMaxSizeConstraint(maxSize);
454 }
455 
456 
457 BRect
458 Area::Frame() const
459 {
460 	return BRect(round(fLeft->Value()), round(fTop->Value()),
461 		round(fRight->Value()), round(fBottom->Value()));
462 }
463 
464 
465 /**
466  * Destructor.
467  * Removes the area from its specification.
468  */
469 Area::~Area()
470 {
471 	delete fMinContentWidth;
472 	delete fMaxContentWidth;
473 	delete fMinContentHeight;
474 	delete fMaxContentHeight;
475 	delete fContentAspectRatioC;
476 }
477 
478 
479 static int32 sAreaID = 0;
480 
481 static int32
482 new_area_id()
483 {
484 	return sAreaID++;
485 }
486 
487 
488 /**
489  * Constructor.
490  * Uses XTabs and YTabs.
491  */
492 Area::Area(BLayoutItem* item)
493 	:
494 	fLayoutItem(item),
495 	fLS(NULL),
496 	fLeft(NULL),
497 	fRight(NULL),
498 	fTop(NULL),
499 	fBottom(NULL),
500 	fRow(NULL),
501 	fColumn(NULL),
502 	fShrinkPenalties(5, 5),
503 	fGrowPenalties(5, 5),
504 	fContentAspectRatio(-1),
505 	fRowColumnManager(NULL),
506 	fMinContentWidth(NULL),
507 	fMaxContentWidth(NULL),
508 	fMinContentHeight(NULL),
509 	fMaxContentHeight(NULL),
510 	fContentAspectRatioC(NULL)
511 {
512 	fID = new_area_id();
513 }
514 
515 
516 int32
517 Area::ID() const
518 {
519 	return fID;
520 }
521 
522 
523 void
524 Area::SetID(int32 id)
525 {
526 	fID = id;
527 }
528 
529 
530 /**
531  * Initialize variables.
532  */
533 void
534 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom,
535 	RowColumnManager* manager)
536 {
537 	fLS = ls;
538 	fLeft = left;
539 	fRight = right;
540 	fTop = top;
541 	fBottom = bottom;
542 
543 	fRowColumnManager = manager;
544 
545 	// adds the two essential constraints of the area that make sure that the
546 	// left x-tab is really to the left of the right x-tab, and the top y-tab
547 	// really above the bottom y-tab
548 	fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, kGE, 0);
549 	fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, kGE, 0);
550 }
551 
552 
553 void
554 Area::_Init(LinearSpec* ls, Row* row, Column* column, RowColumnManager* manager)
555 {
556 	_Init(ls, column->Left(), row->Top(), column->Right(),
557 		row->Bottom(), manager);
558 
559 	fRow = row;
560 	fColumn = column;
561 }
562 
563 
564 /**
565  * Perform layout on the area.
566  */
567 void
568 Area::_DoLayout(const BPoint& offset)
569 {
570 	// check if if we are initialized
571 	if (!fLeft)
572 		return;
573 
574 	if (!fLayoutItem->IsVisible())
575 		fLayoutItem->AlignInFrame(BRect(0, 0, -1, -1));
576 
577 	BRect areaFrame(Frame());
578 	areaFrame.left += LeftInset();
579 	areaFrame.right -= RightInset();
580 	areaFrame.top += TopInset();
581 	areaFrame.bottom -= BottomInset();
582 
583 	fLayoutItem->AlignInFrame(areaFrame.OffsetBySelf(offset));
584 }
585 
586 
587 void
588 Area::_UpdateMinSizeConstraint(BSize min)
589 {
590 	if (!fLayoutItem->IsVisible()) {
591 		fMinContentHeight->SetRightSide(-1);
592 		fMinContentWidth->SetRightSide(-1);
593 		return;
594 	}
595 
596 	float width = 0.;
597 	float height = 0.;
598 	if (min.width > 0)
599 		width = min.Width() + LeftInset() + RightInset();
600 	if (min.height > 0)
601 		height = min.Height() + TopInset() + BottomInset();
602 
603 	fMinContentWidth->SetRightSide(width);
604 	fMinContentHeight->SetRightSide(height);
605 }
606 
607 
608 void
609 Area::_UpdateMaxSizeConstraint(BSize max)
610 {
611 	if (!fLayoutItem->IsVisible()) {
612 		fMaxContentHeight->SetRightSide(B_SIZE_UNLIMITED);
613 		fMaxContentWidth->SetRightSide(B_SIZE_UNLIMITED);
614 		return;
615 	}
616 
617 	max.width += LeftInset() + RightInset();
618 	max.height += TopInset() + BottomInset();
619 
620 	// we only need max constraints if the alignment is full height/width
621 	// otherwise we can just align the item in the free space
622 	BAlignment alignment = fLayoutItem->Alignment();
623 	if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) {
624 		if (fMaxContentHeight == NULL) {
625 			fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom,
626 				kLE, max.Height());
627 		} else
628 			fMaxContentHeight->SetRightSide(max.Height());
629 	} else {
630 		delete fMaxContentHeight;
631 		fMaxContentHeight = NULL;
632 	}
633 
634 	if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) {
635 		if (fMaxContentWidth == NULL) {
636 			fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, kLE,
637 				max.Width());
638 		} else
639 			fMaxContentWidth->SetRightSide(max.Width());
640 	} else {
641 		delete fMaxContentWidth;
642 		fMaxContentWidth = NULL;
643 	}
644 }
645