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