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