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