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