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