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