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