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