1 /* 2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include <TwoDimensionalLayout.h> 7 8 #include <stdio.h> 9 10 #include <LayoutContext.h> 11 #include <LayoutItem.h> 12 #include <LayoutUtils.h> 13 #include <List.h> 14 #include <View.h> 15 16 #include "ComplexLayouter.h" 17 #include "OneElementLayouter.h" 18 #include "SimpleLayouter.h" 19 20 21 // Some words of explanation: 22 // 23 // This class is the base class for BLayouts that organize their items 24 // on a grid, with each item covering one or more grid cells (always a 25 // rectangular area). The derived classes only need to implement the 26 // hooks reporting the constraints for the items and additional constraints 27 // for the rows and columns. This class does all the layouting. 28 // 29 // The basic idea of the layout process is simple. The horizontal and the 30 // vertical dimensions are laid out independently and the items are set to the 31 // resulting locations and sizes. The "height for width" feature makes the 32 // height depend on the width, which makes things a bit more complicated. 33 // The horizontal dimension must be laid out first and and the results are 34 // fed into the vertical layout process. 35 // 36 // The AlignLayoutWith() feature, which allows to align layouts for different 37 // views with each other, causes the need for the three inner *Layouter classes. 38 // For each set of layouts aligned with each other with respect to one 39 // dimension that dimension must be laid out together. The class responsible 40 // is CompoundLayouter; one instance exists per such set. The derived class 41 // VerticalCompoundLayouter is a specialization for the vertical dimension 42 // which additionally takes care of the "height for width" feature. Per layout 43 // a single LocalLayouter exists, which comprises the required glue layout 44 // code and serves as a proxy for the layout, providing service methods 45 // needed by the CompoundLayouter. 46 47 // TODO: Check for memory leaks! 48 49 //#define DEBUG_LAYOUT 50 51 // CompoundLayouter 52 class BTwoDimensionalLayout::CompoundLayouter { 53 public: 54 CompoundLayouter(enum orientation orientation); 55 virtual ~CompoundLayouter(); 56 57 orientation Orientation(); 58 59 virtual Layouter* GetLayouter(bool minMax); 60 61 LayoutInfo* GetLayoutInfo(); 62 63 void AddLocalLayouter(LocalLayouter* localLayouter); 64 void RemoveLocalLayouter( 65 LocalLayouter* localLayouter); 66 67 void AbsorbCompoundLayouter(CompoundLayouter* other); 68 69 virtual void InvalidateLayout(); 70 bool IsMinMaxValid(); 71 void ValidateMinMax(); 72 void Layout(float size, LocalLayouter* localLayouter, 73 BLayoutContext* context); 74 75 protected: 76 virtual void DoLayout(float size, 77 LocalLayouter* localLayouter, 78 BLayoutContext* context); 79 80 Layouter* fLayouter; 81 LayoutInfo* fLayoutInfo; 82 orientation fOrientation; 83 BList fLocalLayouters; 84 BLayoutContext* fLayoutContext; 85 float fLastLayoutSize; 86 87 void _PrepareItems(); 88 89 int32 _CountElements(); 90 bool _HasMultiElementItems(); 91 92 void _AddConstraints(Layouter* layouter); 93 94 float _Spacing(); 95 }; 96 97 // VerticalCompoundLayouter 98 class BTwoDimensionalLayout::VerticalCompoundLayouter 99 : public CompoundLayouter, private BLayoutContextListener { 100 public: 101 VerticalCompoundLayouter(); 102 103 virtual Layouter* GetLayouter(bool minMax); 104 105 virtual void InvalidateLayout(); 106 107 void InvalidateHeightForWidth(); 108 109 void InternalGetHeightForWidth( 110 LocalLayouter* localLayouter, 111 BLayoutContext* context, 112 bool realLayout, float* minHeight, 113 float* maxHeight, float* preferredHeight); 114 115 protected: 116 virtual void DoLayout(float size, 117 LocalLayouter* localLayouter, 118 BLayoutContext* context); 119 120 private: 121 Layouter* fHeightForWidthLayouter; 122 float fCachedMinHeightForWidth; 123 float fCachedMaxHeightForWidth; 124 float fCachedPreferredHeightForWidth; 125 BLayoutContext* fHeightForWidthLayoutContext; 126 127 bool _HasHeightForWidth(); 128 129 bool _SetHeightForWidthLayoutContext( 130 BLayoutContext* context); 131 132 // BLayoutContextListener 133 virtual void LayoutContextLeft(BLayoutContext* context); 134 }; 135 136 // LocalLayouter 137 class BTwoDimensionalLayout::LocalLayouter : private BLayoutContextListener { 138 public: 139 LocalLayouter(BTwoDimensionalLayout* layout); 140 141 // interface for the BTwoDimensionalLayout class 142 143 BSize MinSize(); 144 BSize MaxSize(); 145 BSize PreferredSize(); 146 147 void InvalidateLayout(); 148 void Layout(BSize size); 149 150 BRect ItemFrame(Dimensions itemDimensions); 151 152 void ValidateMinMax(); 153 154 void DoHorizontalLayout(float width); 155 156 void InternalGetHeightForWidth(float width, 157 float* minHeight, float* maxHeight, 158 float* preferredHeight); 159 160 void AlignWith(LocalLayouter* other, 161 enum orientation orientation); 162 163 164 // interface for the compound layout context 165 166 void PrepareItems( 167 CompoundLayouter* compoundLayouter); 168 int32 CountElements( 169 CompoundLayouter* compoundLayouter); 170 bool HasMultiElementItems( 171 CompoundLayouter* compoundLayouter); 172 173 void AddConstraints( 174 CompoundLayouter* compoundLayouter, 175 Layouter* layouter); 176 177 float Spacing(CompoundLayouter* compoundLayouter); 178 179 bool HasHeightForWidth(); 180 181 bool AddHeightForWidthConstraints( 182 VerticalCompoundLayouter* compoundLayouter, 183 Layouter* layouter, 184 BLayoutContext* context); 185 void SetHeightForWidthConstraintsAdded(bool added); 186 187 void SetCompoundLayouter( 188 CompoundLayouter* compoundLayouter, 189 enum orientation orientation); 190 191 void InternalInvalidateLayout( 192 CompoundLayouter* compoundLayouter); 193 194 // implementation private 195 private: 196 BTwoDimensionalLayout* fLayout; 197 CompoundLayouter* fHLayouter; 198 VerticalCompoundLayouter* fVLayouter; 199 BList fHeightForWidthItems; 200 201 // active layout context when doing last horizontal layout 202 BLayoutContext* fHorizontalLayoutContext; 203 float fHorizontalLayoutWidth; 204 bool fHeightForWidthConstraintsAdded; 205 206 void _SetHorizontalLayoutContext( 207 BLayoutContext* context, float width); 208 209 // BLayoutContextListener 210 virtual void LayoutContextLeft(BLayoutContext* context); 211 }; 212 213 214 // #pragma mark - 215 216 217 // constructor 218 BTwoDimensionalLayout::BTwoDimensionalLayout() 219 : fLeftInset(0), 220 fRightInset(0), 221 fTopInset(0), 222 fBottomInset(0), 223 fHSpacing(0), 224 fVSpacing(0), 225 fLocalLayouter(new LocalLayouter(this)) 226 { 227 } 228 229 // destructor 230 BTwoDimensionalLayout::~BTwoDimensionalLayout() 231 { 232 delete fLocalLayouter; 233 } 234 235 // SetInsets 236 void 237 BTwoDimensionalLayout::SetInsets(float left, float top, float right, 238 float bottom) 239 { 240 fLeftInset = left; 241 fTopInset = top; 242 fRightInset = right; 243 fBottomInset = bottom; 244 245 InvalidateLayout(); 246 } 247 248 // GetInsets 249 void 250 BTwoDimensionalLayout::GetInsets(float* left, float* top, float* right, 251 float* bottom) 252 { 253 if (left) 254 *left = fLeftInset; 255 if (top) 256 *top = fTopInset; 257 if (right) 258 *right = fRightInset; 259 if (bottom) 260 *bottom = fBottomInset; 261 } 262 263 // AlignLayoutWith 264 void 265 BTwoDimensionalLayout::AlignLayoutWith(BTwoDimensionalLayout* other, 266 enum orientation orientation) 267 { 268 if (!other || other == this) 269 return; 270 271 fLocalLayouter->AlignWith(other->fLocalLayouter, orientation); 272 273 InvalidateLayout(); 274 } 275 276 // MinSize 277 BSize 278 BTwoDimensionalLayout::MinSize() 279 { 280 _ValidateMinMax(); 281 return AddInsets(fLocalLayouter->MinSize()); 282 } 283 284 // MaxSize 285 BSize 286 BTwoDimensionalLayout::MaxSize() 287 { 288 _ValidateMinMax(); 289 return AddInsets(fLocalLayouter->MaxSize()); 290 } 291 292 // PreferredSize 293 BSize 294 BTwoDimensionalLayout::PreferredSize() 295 { 296 _ValidateMinMax(); 297 return AddInsets(fLocalLayouter->PreferredSize()); 298 } 299 300 // Alignment 301 BAlignment 302 BTwoDimensionalLayout::Alignment() 303 { 304 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 305 } 306 307 // GetHeightForWidth 308 bool 309 BTwoDimensionalLayout::HasHeightForWidth() 310 { 311 _ValidateMinMax(); 312 return fLocalLayouter->HasHeightForWidth(); 313 } 314 315 // GetHeightForWidth 316 void 317 BTwoDimensionalLayout::GetHeightForWidth(float width, float* min, float* max, 318 float* preferred) 319 { 320 if (!HasHeightForWidth()) 321 return; 322 323 float outerSpacing = fLeftInset + fRightInset - 1; 324 fLocalLayouter->InternalGetHeightForWidth(BLayoutUtils::SubtractDistances( 325 width, outerSpacing), min, max, preferred); 326 AddInsets(min, max, preferred); 327 } 328 329 // InvalidateLayout 330 void 331 BTwoDimensionalLayout::InvalidateLayout() 332 { 333 BLayout::InvalidateLayout(); 334 335 fLocalLayouter->InvalidateLayout(); 336 } 337 338 // LayoutView 339 void 340 BTwoDimensionalLayout::LayoutView() 341 { 342 _ValidateMinMax(); 343 344 // layout the horizontal/vertical elements 345 BSize size = SubtractInsets(View()->Frame().Size()); 346 #ifdef DEBUG_LAYOUT 347 printf("BTwoDimensionalLayout::LayoutView(%p): size: (%.1f, %.1f)\n", 348 View(), size.width, size.height); 349 #endif 350 351 fLocalLayouter->Layout(size); 352 353 // layout the items 354 int itemCount = CountItems(); 355 for (int i = 0; i < itemCount; i++) { 356 BLayoutItem* item = ItemAt(i); 357 if (item->IsVisible()) { 358 Dimensions itemDimensions; 359 GetItemDimensions(item, &itemDimensions); 360 BRect frame = fLocalLayouter->ItemFrame(itemDimensions); 361 frame.left += fLeftInset; 362 frame.top += fTopInset; 363 frame.right += fLeftInset; 364 frame.bottom += fTopInset; 365 { 366 #ifdef DEBUG_LAYOUT 367 printf(" frame for item %2d (view: %p): ", i, item->View()); 368 frame.PrintToStream(); 369 #endif 370 //BSize min(item->MinSize()); 371 //BSize max(item->MaxSize()); 372 //printf(" min: (%.1f, %.1f), max: (%.1f, %.1f)\n", min.width, min.height, 373 // max.width, max.height); 374 //if (item->HasHeightForWidth()) { 375 //float minHeight, maxHeight, preferredHeight; 376 //item->GetHeightForWidth(frame.Width(), &minHeight, &maxHeight, 377 // &preferredHeight); 378 //printf(" hfw: min: %.1f, max: %.1f, pref: %.1f\n", minHeight, maxHeight, 379 // preferredHeight); 380 //} 381 } 382 383 item->AlignInFrame(frame); 384 } 385 //else 386 //printf(" item %2d not visible", i); 387 } 388 } 389 390 // AddInsets 391 BSize 392 BTwoDimensionalLayout::AddInsets(BSize size) 393 { 394 size.width = BLayoutUtils::AddDistances(size.width, 395 fLeftInset + fRightInset - 1); 396 size.height = BLayoutUtils::AddDistances(size.height, 397 fTopInset + fBottomInset - 1); 398 return size; 399 } 400 401 // AddInsets 402 void 403 BTwoDimensionalLayout::AddInsets(float* minHeight, float* maxHeight, 404 float* preferredHeight) 405 { 406 float insets = fTopInset + fBottomInset - 1; 407 if (minHeight) 408 *minHeight = BLayoutUtils::AddDistances(*minHeight, insets); 409 if (maxHeight) 410 *maxHeight = BLayoutUtils::AddDistances(*maxHeight, insets); 411 if (preferredHeight) 412 *preferredHeight = BLayoutUtils::AddDistances(*preferredHeight, insets); 413 } 414 415 // SubtractInsets 416 BSize 417 BTwoDimensionalLayout::SubtractInsets(BSize size) 418 { 419 size.width = BLayoutUtils::SubtractDistances(size.width, 420 fLeftInset + fRightInset - 1); 421 size.height = BLayoutUtils::SubtractDistances(size.height, 422 fTopInset + fBottomInset - 1); 423 return size; 424 } 425 426 // PrepareItems 427 void 428 BTwoDimensionalLayout::PrepareItems(enum orientation orientation) 429 { 430 } 431 432 // HasMultiColumnItems 433 bool 434 BTwoDimensionalLayout::HasMultiColumnItems() 435 { 436 return false; 437 } 438 439 // HasMultiRowItems 440 bool 441 BTwoDimensionalLayout::HasMultiRowItems() 442 { 443 return false; 444 } 445 446 // _ValidateMinMax 447 void 448 BTwoDimensionalLayout::_ValidateMinMax() 449 { 450 fLocalLayouter->ValidateMinMax(); 451 } 452 453 // _CurrentLayoutContext 454 BLayoutContext* 455 BTwoDimensionalLayout::_CurrentLayoutContext() 456 { 457 BView* view = View(); 458 return (view ? view->LayoutContext() : NULL); 459 } 460 461 462 // #pragma mark - CompoundLayouter 463 464 // constructor 465 BTwoDimensionalLayout::CompoundLayouter::CompoundLayouter( 466 enum orientation orientation) 467 : fLayouter(NULL), 468 fLayoutInfo(NULL), 469 fOrientation(orientation), 470 fLocalLayouters(10), 471 fLayoutContext(NULL), 472 fLastLayoutSize(-1) 473 { 474 } 475 476 // destructor 477 BTwoDimensionalLayout::CompoundLayouter::~CompoundLayouter() 478 { 479 } 480 481 // Orientation 482 orientation 483 BTwoDimensionalLayout::CompoundLayouter::Orientation() 484 { 485 return fOrientation; 486 } 487 488 // GetLayouter 489 Layouter* 490 BTwoDimensionalLayout::CompoundLayouter::GetLayouter(bool minMax) 491 { 492 return fLayouter; 493 } 494 495 // GetLayoutInfo 496 LayoutInfo* 497 BTwoDimensionalLayout::CompoundLayouter::GetLayoutInfo() 498 { 499 return fLayoutInfo; 500 } 501 502 // AddLocalLayouter 503 void 504 BTwoDimensionalLayout::CompoundLayouter::AddLocalLayouter( 505 LocalLayouter* localLayouter) 506 { 507 if (localLayouter) { 508 if (!fLocalLayouters.HasItem(localLayouter)) { 509 fLocalLayouters.AddItem(localLayouter); 510 InvalidateLayout(); 511 } 512 } 513 } 514 515 // RemoveLocalLayouter 516 void 517 BTwoDimensionalLayout::CompoundLayouter::RemoveLocalLayouter( 518 LocalLayouter* localLayouter) 519 { 520 if (fLocalLayouters.RemoveItem(localLayouter)) 521 InvalidateLayout(); 522 } 523 524 // AbsorbCompoundLayouter 525 void 526 BTwoDimensionalLayout::CompoundLayouter::AbsorbCompoundLayouter( 527 CompoundLayouter* other) 528 { 529 if (other == this) 530 return; 531 532 int32 count = other->fLocalLayouters.CountItems(); 533 for (int32 i = 0; i < count; i++) { 534 LocalLayouter* layouter 535 = (LocalLayouter*)other->fLocalLayouters.ItemAt(i); 536 AddLocalLayouter(layouter); 537 layouter->SetCompoundLayouter(this, fOrientation); 538 } 539 540 InvalidateLayout(); 541 } 542 543 // InvalidateLayout 544 void 545 BTwoDimensionalLayout::CompoundLayouter::InvalidateLayout() 546 { 547 if (!fLayouter) 548 return; 549 550 delete fLayouter; 551 delete fLayoutInfo; 552 553 fLayouter = NULL; 554 fLayoutInfo = NULL; 555 fLayoutContext = NULL; 556 557 // notify all local layouters to invalidate the respective views 558 int32 count = fLocalLayouters.CountItems(); 559 for (int32 i = 0; i < count; i++) { 560 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 561 layouter->InternalInvalidateLayout(this); 562 } 563 } 564 565 // IsMinMaxValid 566 bool 567 BTwoDimensionalLayout::CompoundLayouter::IsMinMaxValid() 568 { 569 return (fLayouter != NULL); 570 } 571 572 // ValidateMinMax 573 void 574 BTwoDimensionalLayout::CompoundLayouter::ValidateMinMax() 575 { 576 if (IsMinMaxValid()) 577 return; 578 579 fLastLayoutSize = -1; 580 581 // create the layouter 582 _PrepareItems(); 583 584 int elementCount = _CountElements(); 585 586 if (elementCount <= 1) 587 fLayouter = new OneElementLayouter(); 588 else if (_HasMultiElementItems()) 589 fLayouter = new ComplexLayouter(elementCount, _Spacing()); 590 else 591 fLayouter = new SimpleLayouter(elementCount, _Spacing()); 592 593 // tell the layouter about our constraints 594 // TODO: We should probably ignore local layouters whose view is hidden. It's a bit tricky to find 595 // out, whether the view is hidden, though, since this doesn't necessarily mean only hidden 596 // relative to the parent, but hidden relative to a common parent. 597 _AddConstraints(fLayouter); 598 599 fLayoutInfo = fLayouter->CreateLayoutInfo(); 600 } 601 602 // Layout 603 void 604 BTwoDimensionalLayout::CompoundLayouter::Layout(float size, 605 LocalLayouter* localLayouter, BLayoutContext* context) 606 { 607 ValidateMinMax(); 608 609 if (context != fLayoutContext || fLastLayoutSize != size) { 610 DoLayout(size, localLayouter, context); 611 fLayoutContext = context; 612 fLastLayoutSize = size; 613 } 614 } 615 616 // DoLayout 617 void 618 BTwoDimensionalLayout::CompoundLayouter::DoLayout(float size, 619 LocalLayouter* localLayouter, BLayoutContext* context) 620 { 621 fLayouter->Layout(fLayoutInfo, size); 622 } 623 624 // _PrepareItems 625 void 626 BTwoDimensionalLayout::CompoundLayouter::_PrepareItems() 627 { 628 int32 count = fLocalLayouters.CountItems(); 629 for (int32 i = 0; i < count; i++) { 630 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 631 layouter->PrepareItems(this); 632 } 633 } 634 635 // _CountElements 636 int32 637 BTwoDimensionalLayout::CompoundLayouter::_CountElements() 638 { 639 int32 elementCount = 0; 640 int32 count = fLocalLayouters.CountItems(); 641 for (int32 i = 0; i < count; i++) { 642 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 643 int32 layouterCount = layouter->CountElements(this); 644 elementCount = max_c(elementCount, layouterCount); 645 } 646 647 return elementCount; 648 } 649 650 // _HasMultiElementItems 651 bool 652 BTwoDimensionalLayout::CompoundLayouter::_HasMultiElementItems() 653 { 654 int32 count = fLocalLayouters.CountItems(); 655 for (int32 i = 0; i < count; i++) { 656 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 657 if (layouter->HasMultiElementItems(this)) 658 return true; 659 } 660 661 return false; 662 } 663 664 // _AddConstraints 665 void 666 BTwoDimensionalLayout::CompoundLayouter::_AddConstraints(Layouter* layouter) 667 { 668 int32 count = fLocalLayouters.CountItems(); 669 for (int32 i = 0; i < count; i++) { 670 LocalLayouter* localLayouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 671 localLayouter->AddConstraints(this, layouter); 672 } 673 } 674 675 // _Spacing 676 float 677 BTwoDimensionalLayout::CompoundLayouter::_Spacing() 678 { 679 if (!fLocalLayouters.IsEmpty()) 680 return ((LocalLayouter*)fLocalLayouters.ItemAt(0))->Spacing(this); 681 return 0; 682 } 683 684 685 // #pragma mark - VerticalCompoundLayouter 686 687 688 // constructor 689 BTwoDimensionalLayout::VerticalCompoundLayouter::VerticalCompoundLayouter() 690 : CompoundLayouter(B_VERTICAL), 691 fHeightForWidthLayouter(NULL), 692 fCachedMinHeightForWidth(0), 693 fCachedMaxHeightForWidth(0), 694 fCachedPreferredHeightForWidth(0), 695 fHeightForWidthLayoutContext(NULL) 696 { 697 } 698 699 // GetLayouter 700 Layouter* 701 BTwoDimensionalLayout::VerticalCompoundLayouter::GetLayouter(bool minMax) 702 { 703 return (minMax || !_HasHeightForWidth() 704 ? fLayouter : fHeightForWidthLayouter); 705 } 706 707 // InvalidateLayout 708 void 709 BTwoDimensionalLayout::VerticalCompoundLayouter::InvalidateLayout() 710 { 711 CompoundLayouter::InvalidateLayout(); 712 713 InvalidateHeightForWidth(); 714 } 715 716 // InvalidateHeightForWidth 717 void 718 BTwoDimensionalLayout::VerticalCompoundLayouter::InvalidateHeightForWidth() 719 { 720 if (fHeightForWidthLayouter != NULL) { 721 delete fHeightForWidthLayouter; 722 fHeightForWidthLayouter = NULL; 723 724 // also make sure we're not reusing the old layout info 725 fLastLayoutSize = -1; 726 727 int32 count = fLocalLayouters.CountItems(); 728 for (int32 i = 0; i < count; i++) { 729 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 730 layouter->SetHeightForWidthConstraintsAdded(false); 731 } 732 } 733 } 734 735 // InternalGetHeightForWidth 736 void 737 BTwoDimensionalLayout::VerticalCompoundLayouter::InternalGetHeightForWidth( 738 LocalLayouter* localLayouter, BLayoutContext* context, bool realLayout, 739 float* minHeight, float* maxHeight, float* preferredHeight) 740 { 741 bool updateCachedInfo = false; 742 743 if (_SetHeightForWidthLayoutContext(context) 744 || fHeightForWidthLayouter == NULL) { 745 // Either the layout context changed or we haven't initialized the 746 // height for width layouter yet. We create it and init it now. 747 748 // clone the vertical layouter 749 delete fHeightForWidthLayouter; 750 delete fLayoutInfo; 751 fHeightForWidthLayouter = fLayouter->CloneLayouter(); 752 fLayoutInfo = fHeightForWidthLayouter->CreateLayoutInfo(); 753 754 // add the children's height for width constraints 755 int32 count = fLocalLayouters.CountItems(); 756 for (int32 i = 0; i < count; i++) { 757 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 758 if (layouter->HasHeightForWidth()) { 759 layouter->AddHeightForWidthConstraints(this, 760 fHeightForWidthLayouter, context); 761 } 762 } 763 764 updateCachedInfo = true; 765 766 // get the height for width info 767 fCachedMinHeightForWidth = fHeightForWidthLayouter->MinSize(); 768 fCachedMaxHeightForWidth = fHeightForWidthLayouter->MaxSize(); 769 fCachedPreferredHeightForWidth 770 = fHeightForWidthLayouter->PreferredSize(); 771 772 } else if (localLayouter->HasHeightForWidth()) { 773 // There is a height for width layouter and it has been initialized 774 // in the current layout context. So we just add the height for width 775 // constraints of the calling local layouter, if they haven't been 776 // added yet. 777 updateCachedInfo = localLayouter->AddHeightForWidthConstraints(this, 778 fHeightForWidthLayouter, context); 779 } 780 781 // update cached height for width info, if something changed 782 if (updateCachedInfo) { 783 // get the height for width info 784 fCachedMinHeightForWidth = fHeightForWidthLayouter->MinSize(); 785 fCachedMaxHeightForWidth = fHeightForWidthLayouter->MaxSize(); 786 fCachedPreferredHeightForWidth 787 = fHeightForWidthLayouter->PreferredSize(); 788 } 789 790 if (minHeight) 791 *minHeight = fCachedMinHeightForWidth; 792 if (maxHeight) 793 *maxHeight = fCachedMaxHeightForWidth; 794 if (preferredHeight) 795 *preferredHeight = fCachedPreferredHeightForWidth; 796 } 797 798 // DoLayout 799 void 800 BTwoDimensionalLayout::VerticalCompoundLayouter::DoLayout(float size, 801 LocalLayouter* localLayouter, BLayoutContext* context) 802 { 803 Layouter* layouter; 804 if (_HasHeightForWidth()) { 805 float minHeight, maxHeight, preferredHeight; 806 InternalGetHeightForWidth(localLayouter, context, true, &minHeight, 807 &maxHeight, &preferredHeight); 808 size = max_c(size, minHeight); 809 layouter = fHeightForWidthLayouter; 810 } else 811 layouter = fLayouter; 812 813 layouter->Layout(fLayoutInfo, size); 814 } 815 816 // _HasHeightForWidth 817 bool 818 BTwoDimensionalLayout::VerticalCompoundLayouter::_HasHeightForWidth() 819 { 820 int32 count = fLocalLayouters.CountItems(); 821 for (int32 i = 0; i < count; i++) { 822 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i); 823 if (layouter->HasHeightForWidth()) 824 return true; 825 } 826 827 return false; 828 } 829 830 // _SetHeightForWidthLayoutContext 831 bool 832 BTwoDimensionalLayout::VerticalCompoundLayouter 833 ::_SetHeightForWidthLayoutContext(BLayoutContext* context) 834 { 835 if (context == fHeightForWidthLayoutContext) 836 return false; 837 838 if (fHeightForWidthLayoutContext != NULL) { 839 fHeightForWidthLayoutContext->RemoveListener(this); 840 fHeightForWidthLayoutContext = NULL; 841 } 842 843 // We can ignore the whole context business, if we have no more than one 844 // local layouter. We use the layout context only to recognize when calls 845 // of different local layouters belong to the same context. 846 if (fLocalLayouters.CountItems() <= 1) 847 return false; 848 849 fHeightForWidthLayoutContext = context; 850 851 if (fHeightForWidthLayoutContext != NULL) 852 fHeightForWidthLayoutContext->AddListener(this); 853 854 InvalidateHeightForWidth(); 855 856 return true; 857 } 858 859 // LayoutContextLeft 860 void 861 BTwoDimensionalLayout::VerticalCompoundLayouter::LayoutContextLeft( 862 BLayoutContext* context) 863 { 864 fHeightForWidthLayoutContext = NULL; 865 } 866 867 868 // #pragma mark - LocalLayouter 869 870 871 // constructor 872 BTwoDimensionalLayout::LocalLayouter::LocalLayouter( 873 BTwoDimensionalLayout* layout) 874 : fLayout(layout), 875 fHLayouter(new CompoundLayouter(B_HORIZONTAL)), 876 fVLayouter(new VerticalCompoundLayouter), 877 fHeightForWidthItems(), 878 fHorizontalLayoutContext(NULL), 879 fHorizontalLayoutWidth(0), 880 fHeightForWidthConstraintsAdded(false) 881 { 882 fHLayouter->AddLocalLayouter(this); 883 fVLayouter->AddLocalLayouter(this); 884 } 885 886 // MinSize 887 BSize 888 BTwoDimensionalLayout::LocalLayouter::MinSize() 889 { 890 return BSize(fHLayouter->GetLayouter(true)->MinSize(), 891 fVLayouter->GetLayouter(true)->MinSize()); 892 } 893 894 // MaxSize 895 BSize 896 BTwoDimensionalLayout::LocalLayouter::MaxSize() 897 { 898 return BSize(fHLayouter->GetLayouter(true)->MaxSize(), 899 fVLayouter->GetLayouter(true)->MaxSize()); 900 } 901 902 // PreferredSize 903 BSize 904 BTwoDimensionalLayout::LocalLayouter::PreferredSize() 905 { 906 return BSize(fHLayouter->GetLayouter(true)->PreferredSize(), 907 fVLayouter->GetLayouter(true)->PreferredSize()); 908 } 909 910 // InvalidateLayout 911 void 912 BTwoDimensionalLayout::LocalLayouter::InvalidateLayout() 913 { 914 fHLayouter->InvalidateLayout(); 915 fVLayouter->InvalidateLayout(); 916 } 917 918 // Layout 919 void 920 BTwoDimensionalLayout::LocalLayouter::Layout(BSize size) 921 { 922 DoHorizontalLayout(size.width); 923 fVLayouter->Layout(size.height, this, fLayout->_CurrentLayoutContext()); 924 } 925 926 // ItemFrame 927 BRect 928 BTwoDimensionalLayout::LocalLayouter::ItemFrame(Dimensions itemDimensions) 929 { 930 LayoutInfo* hLayoutInfo = fHLayouter->GetLayoutInfo(); 931 LayoutInfo* vLayoutInfo = fVLayouter->GetLayoutInfo(); 932 float x = hLayoutInfo->ElementLocation(itemDimensions.x); 933 float y = vLayoutInfo->ElementLocation(itemDimensions.y); 934 float width = hLayoutInfo->ElementRangeSize(itemDimensions.x, 935 itemDimensions.width); 936 float height = vLayoutInfo->ElementRangeSize(itemDimensions.y, 937 itemDimensions.height); 938 return BRect(x, y, x + width, y + height); 939 } 940 941 // ValidateMinMax 942 void 943 BTwoDimensionalLayout::LocalLayouter::ValidateMinMax() 944 { 945 if (fHLayouter->IsMinMaxValid() && fVLayouter->IsMinMaxValid()) 946 return; 947 948 if (!fHLayouter->IsMinMaxValid()) 949 fHeightForWidthItems.MakeEmpty(); 950 951 _SetHorizontalLayoutContext(NULL, -1); 952 953 fHLayouter->ValidateMinMax(); 954 fVLayouter->ValidateMinMax(); 955 } 956 957 // DoHorizontalLayout 958 void 959 BTwoDimensionalLayout::LocalLayouter::DoHorizontalLayout(float width) 960 { 961 BLayoutContext* context = fLayout->_CurrentLayoutContext(); 962 if (fHorizontalLayoutContext != context 963 || width != fHorizontalLayoutWidth) { 964 _SetHorizontalLayoutContext(context, width); 965 fHLayouter->Layout(width, this, context); 966 fVLayouter->InvalidateHeightForWidth(); 967 } 968 } 969 970 // InternalGetHeightForWidth 971 void 972 BTwoDimensionalLayout::LocalLayouter::InternalGetHeightForWidth(float width, 973 float* minHeight, float* maxHeight, float* preferredHeight) 974 { 975 DoHorizontalLayout(width); 976 fVLayouter->InternalGetHeightForWidth(this, fHorizontalLayoutContext, false, 977 minHeight, maxHeight, preferredHeight); 978 } 979 980 // AlignWith 981 void 982 BTwoDimensionalLayout::LocalLayouter::AlignWith(LocalLayouter* other, 983 enum orientation orientation) 984 { 985 if (orientation == B_HORIZONTAL) 986 other->fHLayouter->AbsorbCompoundLayouter(fHLayouter); 987 else 988 other->fVLayouter->AbsorbCompoundLayouter(fVLayouter); 989 } 990 991 // PrepareItems 992 void 993 BTwoDimensionalLayout::LocalLayouter::PrepareItems( 994 CompoundLayouter* compoundLayouter) 995 { 996 fLayout->PrepareItems(compoundLayouter->Orientation()); 997 } 998 999 // CountElements 1000 int32 1001 BTwoDimensionalLayout::LocalLayouter::CountElements( 1002 CompoundLayouter* compoundLayouter) 1003 { 1004 if (compoundLayouter->Orientation() == B_HORIZONTAL) 1005 return fLayout->InternalCountColumns(); 1006 else 1007 return fLayout->InternalCountRows(); 1008 } 1009 1010 // HasMultiElementItems 1011 bool 1012 BTwoDimensionalLayout::LocalLayouter::HasMultiElementItems( 1013 CompoundLayouter* compoundLayouter) 1014 { 1015 if (compoundLayouter->Orientation() == B_HORIZONTAL) 1016 return fLayout->HasMultiColumnItems(); 1017 else 1018 return fLayout->HasMultiRowItems(); 1019 } 1020 1021 // AddConstraints 1022 void 1023 BTwoDimensionalLayout::LocalLayouter::AddConstraints( 1024 CompoundLayouter* compoundLayouter, Layouter* layouter) 1025 { 1026 enum orientation orientation = compoundLayouter->Orientation(); 1027 int itemCount = fLayout->CountItems(); 1028 if (itemCount > 0) { 1029 for (int i = 0; i < itemCount; i++) { 1030 BLayoutItem* item = fLayout->ItemAt(i); 1031 if (item->IsVisible()) { 1032 Dimensions itemDimensions; 1033 fLayout->GetItemDimensions(item, &itemDimensions); 1034 1035 BSize min = item->MinSize(); 1036 BSize max = item->MaxSize(); 1037 BSize preferred = item->PreferredSize(); 1038 1039 if (orientation == B_HORIZONTAL) { 1040 layouter->AddConstraints( 1041 itemDimensions.x, 1042 itemDimensions.width, 1043 min.width, 1044 max.width, 1045 preferred.width); 1046 1047 if (item->HasHeightForWidth()) 1048 fHeightForWidthItems.AddItem(item); 1049 1050 } else { 1051 layouter->AddConstraints( 1052 itemDimensions.y, 1053 itemDimensions.height, 1054 min.height, 1055 max.height, 1056 preferred.height); 1057 } 1058 } 1059 } 1060 1061 // add column/row constraints 1062 ColumnRowConstraints constraints; 1063 int elementCount = CountElements(compoundLayouter); 1064 for (int element = 0; element < elementCount; element++) { 1065 fLayout->GetColumnRowConstraints(orientation, element, 1066 &constraints); 1067 layouter->SetWeight(element, constraints.weight); 1068 layouter->AddConstraints(element, 1, constraints.min, 1069 constraints.max, constraints.min); 1070 } 1071 } 1072 } 1073 1074 // Spacing 1075 float 1076 BTwoDimensionalLayout::LocalLayouter::Spacing( 1077 CompoundLayouter* compoundLayouter) 1078 { 1079 return (compoundLayouter->Orientation() == B_HORIZONTAL 1080 ? fLayout->fHSpacing : fLayout->fVSpacing); 1081 } 1082 1083 // HasHeightForWidth 1084 bool 1085 BTwoDimensionalLayout::LocalLayouter::HasHeightForWidth() 1086 { 1087 return !fHeightForWidthItems.IsEmpty(); 1088 } 1089 1090 // AddHeightForWidthConstraints 1091 bool 1092 BTwoDimensionalLayout::LocalLayouter::AddHeightForWidthConstraints( 1093 VerticalCompoundLayouter* compoundLayouter, Layouter* layouter, 1094 BLayoutContext* context) 1095 { 1096 if (context != fHorizontalLayoutContext) 1097 return false; 1098 1099 if (fHeightForWidthConstraintsAdded) 1100 return false; 1101 1102 LayoutInfo* hLayoutInfo = fHLayouter->GetLayoutInfo(); 1103 1104 // add the children's height for width constraints 1105 int32 itemCount = fHeightForWidthItems.CountItems(); 1106 for (int32 i = 0; i < itemCount; i++) { 1107 BLayoutItem* item = (BLayoutItem*)fHeightForWidthItems.ItemAt(i); 1108 Dimensions itemDimensions; 1109 fLayout->GetItemDimensions(item, &itemDimensions); 1110 1111 float minHeight, maxHeight, preferredHeight; 1112 item->GetHeightForWidth( 1113 hLayoutInfo->ElementRangeSize(itemDimensions.x, 1114 itemDimensions.width), 1115 &minHeight, &maxHeight, &preferredHeight); 1116 layouter->AddConstraints( 1117 itemDimensions.y, 1118 itemDimensions.height, 1119 minHeight, 1120 maxHeight, 1121 preferredHeight); 1122 } 1123 1124 SetHeightForWidthConstraintsAdded(true); 1125 1126 return true; 1127 } 1128 1129 // SetHeightForWidthConstraintsAdded 1130 void 1131 BTwoDimensionalLayout::LocalLayouter::SetHeightForWidthConstraintsAdded( 1132 bool added) 1133 { 1134 fHeightForWidthConstraintsAdded = added; 1135 } 1136 1137 // SetCompoundLayouter 1138 void 1139 BTwoDimensionalLayout::LocalLayouter::SetCompoundLayouter( 1140 CompoundLayouter* compoundLayouter, enum orientation orientation) 1141 { 1142 if (orientation == B_HORIZONTAL) 1143 fHLayouter = compoundLayouter; 1144 else 1145 fVLayouter = (VerticalCompoundLayouter*)compoundLayouter; 1146 1147 InternalInvalidateLayout(compoundLayouter); 1148 } 1149 1150 // InternalInvalidateLayout 1151 void 1152 BTwoDimensionalLayout::LocalLayouter::InternalInvalidateLayout( 1153 CompoundLayouter* compoundLayouter) 1154 { 1155 _SetHorizontalLayoutContext(NULL, -1); 1156 1157 fLayout->BLayout::InvalidateLayout(); 1158 } 1159 1160 // _SetHorizontalLayoutContext 1161 void 1162 BTwoDimensionalLayout::LocalLayouter::_SetHorizontalLayoutContext( 1163 BLayoutContext* context, float width) 1164 { 1165 if (context != fHorizontalLayoutContext) { 1166 if (fHorizontalLayoutContext != NULL) 1167 fHorizontalLayoutContext->RemoveListener(this); 1168 1169 fHorizontalLayoutContext = context; 1170 1171 if (fHorizontalLayoutContext != NULL) 1172 fHorizontalLayoutContext->AddListener(this); 1173 } 1174 1175 fHorizontalLayoutWidth = width; 1176 } 1177 1178 // LayoutContextLeft 1179 void 1180 BTwoDimensionalLayout::LocalLayouter::LayoutContextLeft(BLayoutContext* context) 1181 { 1182 fHorizontalLayoutContext = NULL; 1183 fHorizontalLayoutWidth = -1; 1184 } 1185