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