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 fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 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 fPreferredContentWidth->SetLeftSide(-1.0, fLeft, 1.0, fRight); 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 fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 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 fPreferredContentHeight->SetLeftSide(-1.0, fTop, 1.0, fBottom); 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 (fContentAspectRatio <= 0) { 267 delete fContentAspectRatioC; 268 fContentAspectRatioC = NULL; 269 } else if (fContentAspectRatioC == NULL) { 270 fContentAspectRatioC = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, 271 ratio, fTop, -ratio, fBottom, OperatorType(EQ), 0.0); 272 fConstraints.AddItem(fContentAspectRatioC); 273 } else { 274 fContentAspectRatioC->SetLeftSide(-1.0, fLeft, 1.0, fRight, ratio, 275 fTop, -ratio, fBottom); 276 } 277 fLayoutItem->Layout()->InvalidateLayout(); 278 } 279 280 281 /** 282 * Gets left inset between area and its content. 283 */ 284 int32 285 Area::LeftInset() const 286 { 287 if (fTopLeftInset.IsWidthSet()) 288 return fTopLeftInset.Width(); 289 290 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 291 if (fLeft == layout->Left()) 292 return layout->Inset(); 293 return layout->Spacing() / 2; 294 } 295 296 297 /** 298 * Gets top inset between area and its content. 299 */ 300 int32 301 Area::TopInset() const 302 { 303 if (fTopLeftInset.IsHeightSet()) 304 return fTopLeftInset.Height(); 305 306 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 307 if (fTop == layout->Top()) 308 return layout->Inset(); 309 return layout->Spacing() / 2; 310 } 311 312 313 /** 314 * Gets right inset between area and its content. 315 */ 316 int32 317 Area::RightInset() const 318 { 319 if (fRightBottomInset.IsWidthSet()) 320 return fRightBottomInset.Width(); 321 322 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 323 if (fRight == layout->Right()) 324 return layout->Inset(); 325 return layout->Spacing() / 2; 326 } 327 328 329 /** 330 * Gets bottom inset between area and its content. 331 */ 332 int32 333 Area::BottomInset() const 334 { 335 if (fRightBottomInset.IsHeightSet()) 336 return fRightBottomInset.Height(); 337 338 BALMLayout* layout = static_cast<BALMLayout*>(fLayoutItem->Layout()); 339 if (fBottom == layout->Bottom()) 340 return layout->Inset(); 341 return layout->Spacing() / 2; 342 } 343 344 345 /** 346 * Sets left inset between area and its content. 347 */ 348 void 349 Area::SetLeftInset(int32 left) 350 { 351 fTopLeftInset.width = left; 352 fLayoutItem->Layout()->InvalidateLayout(); 353 } 354 355 356 /** 357 * Sets top inset between area and its content. 358 */ 359 void 360 Area::SetTopInset(int32 top) 361 { 362 fTopLeftInset.height = top; 363 fLayoutItem->Layout()->InvalidateLayout(); 364 } 365 366 367 /** 368 * Sets right inset between area and its content. 369 */ 370 void 371 Area::SetRightInset(int32 right) 372 { 373 fRightBottomInset.width = right; 374 fLayoutItem->Layout()->InvalidateLayout(); 375 } 376 377 378 /** 379 * Sets bottom inset between area and its content. 380 */ 381 void 382 Area::SetBottomInset(int32 bottom) 383 { 384 fRightBottomInset.height = bottom; 385 fLayoutItem->Layout()->InvalidateLayout(); 386 } 387 388 389 Area::operator BString() const 390 { 391 BString string; 392 GetString(string); 393 return string; 394 } 395 396 397 void 398 Area::GetString(BString& string) const 399 { 400 string << "Area("; 401 fLeft->GetString(string); 402 string << ", "; 403 fTop->GetString(string); 404 string << ", "; 405 fRight->GetString(string); 406 string << ", "; 407 fBottom->GetString(string); 408 string << ")"; 409 } 410 411 412 /*! 413 * Sets the width of the area times factor to be the same as the width of the 414 * given area. 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(-factor, fLeft, factor, fRight, 1.0, area->Left(), 423 -1.0, area->Right(), OperatorType(EQ), 0.0); 424 } 425 426 427 /*! 428 * Sets the height of the area times factor to be the same as the height of the 429 * given area. 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(-factor, fTop, factor, fBottom, 1.0, area->Top(), 438 -1.0, area->Bottom(), OperatorType(EQ), 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 BSize prefSize = fLayoutItem->PreferredSize(); 452 453 _UpdateMinSizeConstraint(minSize); 454 _UpdateMaxSizeConstraint(maxSize); 455 _UpdatePreferredConstraint(prefSize); 456 } 457 458 459 /** 460 * Destructor. 461 * Removes the area from its specification. 462 */ 463 Area::~Area() 464 { 465 for (int32 i = 0; i < fConstraints.CountItems(); i++) 466 delete (Constraint*)fConstraints.ItemAt(i); 467 } 468 469 470 /** 471 * Constructor. 472 * Uses XTabs and YTabs. 473 */ 474 Area::Area(BLayoutItem* item) 475 : 476 fLayoutItem(item), 477 478 fLS(NULL), 479 fLeft(NULL), 480 fRight(NULL), 481 fTop(NULL), 482 fBottom(NULL), 483 484 fRow(NULL), 485 fColumn(NULL), 486 487 fShrinkPenalties(5, 5), 488 fGrowPenalties(5, 5), 489 490 fMinContentWidth(NULL), 491 fMaxContentWidth(NULL), 492 fMinContentHeight(NULL), 493 fMaxContentHeight(NULL), 494 fPreferredContentWidth(NULL), 495 fPreferredContentHeight(NULL), 496 497 fContentAspectRatio(-1), 498 fContentAspectRatioC(NULL) 499 { 500 501 } 502 503 504 /** 505 * Initialize variables. 506 */ 507 void 508 Area::_Init(LinearSpec* ls, XTab* left, YTab* top, XTab* right, YTab* bottom) 509 { 510 fLS = ls; 511 fLeft = left; 512 fRight = right; 513 fTop = top; 514 fBottom = bottom; 515 516 // adds the two essential constraints of the area that make sure that the 517 // left x-tab is really to the left of the right x-tab, and the top y-tab 518 // really above the bottom y-tab 519 fMinContentWidth = ls->AddConstraint(-1.0, fLeft, 1.0, fRight, 520 OperatorType(GE), 0); 521 fMinContentHeight = ls->AddConstraint(-1.0, fTop, 1.0, fBottom, 522 OperatorType(GE), 0); 523 524 fConstraints.AddItem(fMinContentWidth); 525 fConstraints.AddItem(fMinContentHeight); 526 527 fPreferredContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, 528 OperatorType(EQ), 0, fShrinkPenalties.Height(), fGrowPenalties.Width()); 529 530 fPreferredContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, 531 OperatorType(EQ), 0, fShrinkPenalties.Height(), 532 fGrowPenalties.Height()); 533 534 fConstraints.AddItem(fPreferredContentWidth); 535 fConstraints.AddItem(fPreferredContentHeight); 536 } 537 538 539 void 540 Area::_Init(LinearSpec* ls, Row* row, Column* column) 541 { 542 _Init(ls, column->Left(), row->Top(), column->Right(), row->Bottom()); 543 fRow = row; 544 fColumn = column; 545 } 546 547 548 /** 549 * Perform layout on the area. 550 */ 551 void 552 Area::_DoLayout() 553 { 554 // check if if we are initialized 555 if (!fLeft) 556 return; 557 558 BRect areaFrame(round(fLeft->Value()), round(fTop->Value()), 559 round(fRight->Value()), round(fBottom->Value())); 560 areaFrame.left += LeftInset(); 561 areaFrame.right -= RightInset(); 562 areaFrame.top += TopInset(); 563 areaFrame.bottom -= BottomInset(); 564 565 fLayoutItem->AlignInFrame(areaFrame); 566 } 567 568 569 void 570 Area::_UpdateMinSizeConstraint(BSize min) 571 { 572 float width = 0.; 573 float height = 0.; 574 if (min.width > 0) 575 width = min.Width() + LeftInset() + RightInset(); 576 if (min.height > 0) 577 height = min.Height() + TopInset() + BottomInset(); 578 579 fMinContentWidth->SetRightSide(width); 580 fMinContentHeight->SetRightSide(height); 581 } 582 583 584 void 585 Area::_UpdateMaxSizeConstraint(BSize max) 586 { 587 max.width += LeftInset() + RightInset(); 588 max.height += TopInset() + BottomInset(); 589 590 // we only need max constraints if the alignment is full height/width 591 // otherwise we can just align the item in the free space 592 BAlignment alignment = fLayoutItem->Alignment(); 593 if (alignment.Vertical() == B_ALIGN_USE_FULL_HEIGHT) { 594 if (fMaxContentHeight == NULL) { 595 fMaxContentHeight = fLS->AddConstraint(-1.0, fTop, 1.0, fBottom, 596 OperatorType(LE), max.Height()); 597 fConstraints.AddItem(fMaxContentHeight); 598 } else 599 fMaxContentHeight->SetRightSide(max.Height()); 600 } 601 else { 602 fConstraints.RemoveItem(fMaxContentHeight); 603 delete fMaxContentHeight; 604 fMaxContentHeight = NULL; 605 } 606 607 if (alignment.Horizontal() == B_ALIGN_USE_FULL_WIDTH) { 608 if (fMaxContentWidth == NULL) { 609 fMaxContentWidth = fLS->AddConstraint(-1.0, fLeft, 1.0, fRight, 610 OperatorType(LE), max.Width()); 611 fConstraints.AddItem(fMaxContentWidth); 612 } else 613 fMaxContentWidth->SetRightSide(max.Width()); 614 } 615 else { 616 fConstraints.RemoveItem(fMaxContentWidth); 617 delete fMaxContentWidth; 618 fMaxContentWidth = NULL; 619 } 620 } 621 622 623 void 624 Area::_UpdatePreferredConstraint(BSize preferred) 625 { 626 float width = 32000; 627 float height = 32000; 628 if (preferred.width > 0) 629 width = preferred.Width() + LeftInset() + RightInset(); 630 if (preferred.height > 0) 631 height = preferred.Height() + TopInset() + BottomInset(); 632 633 fPreferredContentWidth->SetRightSide(width); 634 fPreferredContentHeight->SetRightSide(height); 635 } 636