tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / TwoDimensionalLayout.cpp
blobffb93b0441a2d810af55088ae6ce048ed8175574
1 /*
2 * Copyright 2006-2010, Ingo Weinhold <ingo_weinhold@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
7 #include <TwoDimensionalLayout.h>
9 #include <stdio.h>
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>
19 #include <Referenceable.h>
21 #include "CollapsingLayouter.h"
24 // Some words of explanation:
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.
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.
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.
50 // TODO: Check for memory leaks!
52 //#define DEBUG_LAYOUT
54 // CompoundLayouter
55 class BTwoDimensionalLayout::CompoundLayouter : public BReferenceable {
56 public:
57 CompoundLayouter(orientation orientation);
58 virtual ~CompoundLayouter();
60 orientation Orientation();
62 virtual Layouter* GetLayouter(bool minMax);
64 LayoutInfo* GetLayoutInfo();
66 void AddLocalLayouter(LocalLayouter* localLayouter);
67 void RemoveLocalLayouter(
68 LocalLayouter* localLayouter);
70 status_t AddAlignedLayoutsToArchive(BArchiver* archiver,
71 LocalLayouter* requestedBy);
73 void AbsorbCompoundLayouter(CompoundLayouter* other);
75 virtual void InvalidateLayout();
76 bool IsMinMaxValid();
77 void ValidateMinMax();
78 void Layout(float size, LocalLayouter* localLayouter,
79 BLayoutContext* context);
81 protected:
82 virtual void DoLayout(float size,
83 LocalLayouter* localLayouter,
84 BLayoutContext* context);
86 Layouter* fLayouter;
87 LayoutInfo* fLayoutInfo;
88 orientation fOrientation;
89 BList fLocalLayouters;
90 BLayoutContext* fLayoutContext;
91 float fLastLayoutSize;
93 void _PrepareItems();
95 int32 _CountElements();
96 bool _HasMultiElementItems();
98 void _AddConstraints(Layouter* layouter);
100 float _Spacing();
103 // VerticalCompoundLayouter
104 class BTwoDimensionalLayout::VerticalCompoundLayouter
105 : public CompoundLayouter, private BLayoutContextListener {
106 public:
107 VerticalCompoundLayouter();
109 virtual Layouter* GetLayouter(bool minMax);
111 virtual void InvalidateLayout();
113 void InvalidateHeightForWidth();
115 void InternalGetHeightForWidth(
116 LocalLayouter* localLayouter,
117 BLayoutContext* context,
118 bool realLayout, float* minHeight,
119 float* maxHeight, float* preferredHeight);
121 protected:
122 virtual void DoLayout(float size,
123 LocalLayouter* localLayouter,
124 BLayoutContext* context);
126 private:
127 Layouter* fHeightForWidthLayouter;
128 float fCachedMinHeightForWidth;
129 float fCachedMaxHeightForWidth;
130 float fCachedPreferredHeightForWidth;
131 BLayoutContext* fHeightForWidthLayoutContext;
133 bool _HasHeightForWidth();
135 bool _SetHeightForWidthLayoutContext(
136 BLayoutContext* context);
138 // BLayoutContextListener
139 virtual void LayoutContextLeft(BLayoutContext* context);
142 // LocalLayouter
143 class BTwoDimensionalLayout::LocalLayouter : private BLayoutContextListener {
144 public:
145 LocalLayouter(BTwoDimensionalLayout* layout);
146 ~LocalLayouter();
148 // interface for the BTwoDimensionalLayout class
150 BSize MinSize();
151 BSize MaxSize();
152 BSize PreferredSize();
154 void InvalidateLayout();
155 void Layout(BSize size);
157 BRect ItemFrame(Dimensions itemDimensions);
159 void ValidateMinMax();
161 void DoHorizontalLayout(float width);
163 void InternalGetHeightForWidth(float width,
164 float* minHeight, float* maxHeight,
165 float* preferredHeight);
167 void AlignWith(LocalLayouter* other,
168 orientation orientation);
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);
179 // interface for the compound layout context
181 void PrepareItems(
182 CompoundLayouter* compoundLayouter);
183 int32 CountElements(
184 CompoundLayouter* compoundLayouter);
185 bool HasMultiElementItems(
186 CompoundLayouter* compoundLayouter);
188 void AddConstraints(
189 CompoundLayouter* compoundLayouter,
190 Layouter* layouter);
192 float Spacing(CompoundLayouter* compoundLayouter);
194 bool HasHeightForWidth();
196 bool AddHeightForWidthConstraints(
197 VerticalCompoundLayouter* compoundLayouter,
198 Layouter* layouter,
199 BLayoutContext* context);
200 void SetHeightForWidthConstraintsAdded(bool added);
202 void SetCompoundLayouter(
203 CompoundLayouter* compoundLayouter,
204 orientation orientation);
206 void InternalInvalidateLayout(
207 CompoundLayouter* compoundLayouter);
209 // implementation private
210 private:
211 BTwoDimensionalLayout* fLayout;
212 CompoundLayouter* fHLayouter;
213 VerticalCompoundLayouter* fVLayouter;
214 BList fHeightForWidthItems;
216 // active layout context when doing last horizontal layout
217 BLayoutContext* fHorizontalLayoutContext;
218 float fHorizontalLayoutWidth;
219 bool fHeightForWidthConstraintsAdded;
221 void _SetHorizontalLayoutContext(
222 BLayoutContext* context, float width);
224 // BLayoutContextListener
225 virtual void LayoutContextLeft(BLayoutContext* context);
229 // #pragma mark -
231 // archiving constants
232 namespace {
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}
243 BTwoDimensionalLayout::BTwoDimensionalLayout()
245 fLeftInset(0),
246 fRightInset(0),
247 fTopInset(0),
248 fBottomInset(0),
249 fHSpacing(0),
250 fVSpacing(0),
251 fLocalLayouter(new LocalLayouter(this))
256 BTwoDimensionalLayout::BTwoDimensionalLayout(BMessage* from)
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))
267 BRect insets;
268 from->FindRect(kInsetsField, &insets);
269 SetInsets(insets.left, insets.top, insets.right, insets.bottom);
271 from->FindFloat(kSpacingField, 0, &fHSpacing);
272 from->FindFloat(kSpacingField, 1, &fVSpacing);
276 BTwoDimensionalLayout::~BTwoDimensionalLayout()
278 delete fLocalLayouter;
282 void
283 BTwoDimensionalLayout::SetInsets(float left, float top, float right,
284 float bottom)
286 fLeftInset = BControlLook::ComposeSpacing(left);
287 fTopInset = BControlLook::ComposeSpacing(top);
288 fRightInset = BControlLook::ComposeSpacing(right);
289 fBottomInset = BControlLook::ComposeSpacing(bottom);
291 InvalidateLayout();
295 void
296 BTwoDimensionalLayout::SetInsets(float horizontal, float vertical)
298 fLeftInset = BControlLook::ComposeSpacing(horizontal);
299 fRightInset = fLeftInset;
301 fTopInset = BControlLook::ComposeSpacing(vertical);
302 fBottomInset = fTopInset;
304 InvalidateLayout();
308 void
309 BTwoDimensionalLayout::SetInsets(float insets)
311 fLeftInset = BControlLook::ComposeSpacing(insets);
312 fRightInset = fLeftInset;
313 fTopInset = fLeftInset;
314 fBottomInset = fLeftInset;
316 InvalidateLayout();
320 void
321 BTwoDimensionalLayout::GetInsets(float* left, float* top, float* right,
322 float* bottom) const
324 if (left)
325 *left = fLeftInset;
326 if (top)
327 *top = fTopInset;
328 if (right)
329 *right = fRightInset;
330 if (bottom)
331 *bottom = fBottomInset;
335 void
336 BTwoDimensionalLayout::AlignLayoutWith(BTwoDimensionalLayout* other,
337 orientation orientation)
339 if (!other || other == this)
340 return;
342 fLocalLayouter->AlignWith(other->fLocalLayouter, orientation);
344 InvalidateLayout();
348 BSize
349 BTwoDimensionalLayout::BaseMinSize()
351 _ValidateMinMax();
352 return AddInsets(fLocalLayouter->MinSize());
356 BSize
357 BTwoDimensionalLayout::BaseMaxSize()
359 _ValidateMinMax();
360 return AddInsets(fLocalLayouter->MaxSize());
364 BSize
365 BTwoDimensionalLayout::BasePreferredSize()
367 _ValidateMinMax();
368 return AddInsets(fLocalLayouter->PreferredSize());
372 BAlignment
373 BTwoDimensionalLayout::BaseAlignment()
375 return BAbstractLayout::BaseAlignment();
379 bool
380 BTwoDimensionalLayout::HasHeightForWidth()
382 _ValidateMinMax();
383 return fLocalLayouter->HasHeightForWidth();
387 void
388 BTwoDimensionalLayout::GetHeightForWidth(float width, float* min, float* max,
389 float* preferred)
391 if (!HasHeightForWidth())
392 return;
394 float outerSpacing = fLeftInset + fRightInset - 1;
395 fLocalLayouter->InternalGetHeightForWidth(BLayoutUtils::SubtractDistances(
396 width, outerSpacing), min, max, preferred);
397 AddInsets(min, max, preferred);
401 void
402 BTwoDimensionalLayout::SetFrame(BRect frame)
404 BAbstractLayout::SetFrame(frame);
408 status_t
409 BTwoDimensionalLayout::Archive(BMessage* into, bool deep) const
411 BArchiver archiver(into);
412 status_t err = BAbstractLayout::Archive(into, deep);
414 if (err == B_OK) {
415 BRect insets(fLeftInset, fTopInset, fRightInset, fBottomInset);
416 err = into->AddRect(kInsetsField, insets);
419 if (err == B_OK)
420 err = into->AddFloat(kSpacingField, fHSpacing);
422 if (err == B_OK)
423 err = into->AddFloat(kSpacingField, fVSpacing);
425 return archiver.Finish(err);
429 status_t
430 BTwoDimensionalLayout::AllArchived(BMessage* into) const
432 BArchiver archiver(into);
434 status_t err = BLayout::AllArchived(into);
435 if (err == B_OK)
436 err = fLocalLayouter->AddAlignedLayoutsToArchive(&archiver);
437 return err;
441 status_t
442 BTwoDimensionalLayout::AllUnarchived(const BMessage* from)
444 status_t err = BLayout::AllUnarchived(from);
445 if (err != B_OK)
446 return err;
448 BUnarchiver unarchiver(from);
449 err = fLocalLayouter->AlignLayoutsFromArchive(&unarchiver, B_HORIZONTAL);
450 if (err == B_OK)
451 err = fLocalLayouter->AlignLayoutsFromArchive(&unarchiver, B_VERTICAL);
453 return err;
457 status_t
458 BTwoDimensionalLayout::ItemArchived(BMessage* into, BLayoutItem* item,
459 int32 index) const
461 return BAbstractLayout::ItemArchived(into, item, index);
465 status_t
466 BTwoDimensionalLayout::ItemUnarchived(const BMessage* from, BLayoutItem* item,
467 int32 index)
469 return BAbstractLayout::ItemUnarchived(from, item, index);
475 void
476 BTwoDimensionalLayout::LayoutInvalidated(bool children)
478 fLocalLayouter->InvalidateLayout();
482 void
483 BTwoDimensionalLayout::DoLayout()
485 _ValidateMinMax();
487 // layout the horizontal/vertical elements
488 BSize size(SubtractInsets(LayoutArea().Size()));
490 #ifdef DEBUG_LAYOUT
491 printf("BTwoDimensionalLayout::DerivedLayoutItems(): view: %p"
492 " size: (%.1f, %.1f)\n", View(), size.Width(), size.Height());
493 #endif
495 fLocalLayouter->Layout(size);
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);
512 #ifdef DEBUG_LAYOUT
513 printf(" frame for item %2d (view: %p): ", i, item->View());
514 frame.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);
529 item->AlignInFrame(frame);
531 //else
532 //printf(" item %2d not visible", i);
537 BSize
538 BTwoDimensionalLayout::AddInsets(BSize size)
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;
548 void
549 BTwoDimensionalLayout::AddInsets(float* minHeight, float* maxHeight,
550 float* preferredHeight)
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);
562 BSize
563 BTwoDimensionalLayout::SubtractInsets(BSize size)
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;
573 void
574 BTwoDimensionalLayout::PrepareItems(orientation orientation)
579 bool
580 BTwoDimensionalLayout::HasMultiColumnItems()
582 return false;
586 bool
587 BTwoDimensionalLayout::HasMultiRowItems()
589 return false;
593 void
594 BTwoDimensionalLayout::_ValidateMinMax()
596 fLocalLayouter->ValidateMinMax();
600 // #pragma mark - CompoundLayouter
603 BTwoDimensionalLayout::CompoundLayouter::CompoundLayouter(
604 orientation orientation)
606 fLayouter(NULL),
607 fLayoutInfo(NULL),
608 fOrientation(orientation),
609 fLocalLayouters(10),
610 fLayoutContext(NULL),
611 fLastLayoutSize(-1)
616 BTwoDimensionalLayout::CompoundLayouter::~CompoundLayouter()
618 delete fLayouter;
619 delete fLayoutInfo;
623 orientation
624 BTwoDimensionalLayout::CompoundLayouter::Orientation()
626 return fOrientation;
630 Layouter*
631 BTwoDimensionalLayout::CompoundLayouter::GetLayouter(bool minMax)
633 return fLayouter;
637 LayoutInfo*
638 BTwoDimensionalLayout::CompoundLayouter::GetLayoutInfo()
640 return fLayoutInfo;
644 void
645 BTwoDimensionalLayout::CompoundLayouter::AddLocalLayouter(
646 LocalLayouter* localLayouter)
648 if (localLayouter) {
649 if (!fLocalLayouters.HasItem(localLayouter)) {
650 fLocalLayouters.AddItem(localLayouter);
651 InvalidateLayout();
657 void
658 BTwoDimensionalLayout::CompoundLayouter::RemoveLocalLayouter(
659 LocalLayouter* localLayouter)
661 if (fLocalLayouters.RemoveItem(localLayouter))
662 InvalidateLayout();
666 status_t
667 BTwoDimensionalLayout::CompoundLayouter::AddAlignedLayoutsToArchive(
668 BArchiver* archiver, LocalLayouter* requestedBy)
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;
675 status_t err;
676 for (int32 i = fLocalLayouters.CountItems() - 1; i > 0; i--) {
677 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i);
679 bool wasAvailable;
680 err = layouter->AddOwnerToArchive(archiver, this, wasAvailable);
681 if (err != B_OK && wasAvailable)
682 return err;
684 return B_OK;
688 void
689 BTwoDimensionalLayout::CompoundLayouter::AbsorbCompoundLayouter(
690 CompoundLayouter* other)
692 if (other == this)
693 return;
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);
703 InvalidateLayout();
707 void
708 BTwoDimensionalLayout::CompoundLayouter::InvalidateLayout()
710 if (!fLayouter)
711 return;
713 delete fLayouter;
714 delete fLayoutInfo;
716 fLayouter = NULL;
717 fLayoutInfo = NULL;
718 fLayoutContext = NULL;
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);
729 bool
730 BTwoDimensionalLayout::CompoundLayouter::IsMinMaxValid()
732 return (fLayouter != NULL);
736 void
737 BTwoDimensionalLayout::CompoundLayouter::ValidateMinMax()
739 if (IsMinMaxValid())
740 return;
742 fLastLayoutSize = -1;
744 // create the layouter
745 _PrepareItems();
747 int elementCount = _CountElements();
749 fLayouter = new CollapsingLayouter(elementCount, _Spacing());
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);
758 fLayoutInfo = fLayouter->CreateLayoutInfo();
762 void
763 BTwoDimensionalLayout::CompoundLayouter::Layout(float size,
764 LocalLayouter* localLayouter, BLayoutContext* context)
766 ValidateMinMax();
768 if (context != fLayoutContext || fLastLayoutSize != size) {
769 DoLayout(size, localLayouter, context);
770 fLayoutContext = context;
771 fLastLayoutSize = size;
776 void
777 BTwoDimensionalLayout::CompoundLayouter::DoLayout(float size,
778 LocalLayouter* localLayouter, BLayoutContext* context)
780 fLayouter->Layout(fLayoutInfo, size);
784 void
785 BTwoDimensionalLayout::CompoundLayouter::_PrepareItems()
787 int32 count = fLocalLayouters.CountItems();
788 for (int32 i = 0; i < count; i++) {
789 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i);
790 layouter->PrepareItems(this);
795 int32
796 BTwoDimensionalLayout::CompoundLayouter::_CountElements()
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);
806 return elementCount;
810 bool
811 BTwoDimensionalLayout::CompoundLayouter::_HasMultiElementItems()
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;
820 return false;
824 void
825 BTwoDimensionalLayout::CompoundLayouter::_AddConstraints(Layouter* layouter)
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);
835 float
836 BTwoDimensionalLayout::CompoundLayouter::_Spacing()
838 if (!fLocalLayouters.IsEmpty())
839 return ((LocalLayouter*)fLocalLayouters.ItemAt(0))->Spacing(this);
840 return 0;
844 // #pragma mark - VerticalCompoundLayouter
847 BTwoDimensionalLayout::VerticalCompoundLayouter::VerticalCompoundLayouter()
849 CompoundLayouter(B_VERTICAL),
850 fHeightForWidthLayouter(NULL),
851 fCachedMinHeightForWidth(0),
852 fCachedMaxHeightForWidth(0),
853 fCachedPreferredHeightForWidth(0),
854 fHeightForWidthLayoutContext(NULL)
859 Layouter*
860 BTwoDimensionalLayout::VerticalCompoundLayouter::GetLayouter(bool minMax)
862 return (minMax || !_HasHeightForWidth()
863 ? fLayouter : fHeightForWidthLayouter);
867 void
868 BTwoDimensionalLayout::VerticalCompoundLayouter::InvalidateLayout()
870 CompoundLayouter::InvalidateLayout();
872 InvalidateHeightForWidth();
876 void
877 BTwoDimensionalLayout::VerticalCompoundLayouter::InvalidateHeightForWidth()
879 if (fHeightForWidthLayouter != NULL) {
880 delete fHeightForWidthLayouter;
881 fHeightForWidthLayouter = NULL;
883 // also make sure we're not reusing the old layout info
884 fLastLayoutSize = -1;
886 int32 count = fLocalLayouters.CountItems();
887 for (int32 i = 0; i < count; i++) {
888 LocalLayouter* layouter = (LocalLayouter*)fLocalLayouters.ItemAt(i);
889 layouter->SetHeightForWidthConstraintsAdded(false);
895 void
896 BTwoDimensionalLayout::VerticalCompoundLayouter::InternalGetHeightForWidth(
897 LocalLayouter* localLayouter, BLayoutContext* context, bool realLayout,
898 float* minHeight, float* maxHeight, float* preferredHeight)
900 bool updateCachedInfo = false;
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.
907 // clone the vertical layouter
908 delete fHeightForWidthLayouter;
909 delete fLayoutInfo;
910 fHeightForWidthLayouter = fLayouter->CloneLayouter();
911 fLayoutInfo = fHeightForWidthLayouter->CreateLayoutInfo();
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);
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);
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();
942 if (minHeight)
943 *minHeight = fCachedMinHeightForWidth;
944 if (maxHeight)
945 *maxHeight = fCachedMaxHeightForWidth;
946 if (preferredHeight)
947 *preferredHeight = fCachedPreferredHeightForWidth;
951 void
952 BTwoDimensionalLayout::VerticalCompoundLayouter::DoLayout(float size,
953 LocalLayouter* localLayouter, BLayoutContext* context)
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;
965 layouter->Layout(fLayoutInfo, size);
969 bool
970 BTwoDimensionalLayout::VerticalCompoundLayouter::_HasHeightForWidth()
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;
979 return false;
983 bool
984 BTwoDimensionalLayout::VerticalCompoundLayouter
985 ::_SetHeightForWidthLayoutContext(BLayoutContext* context)
987 if (context == fHeightForWidthLayoutContext)
988 return false;
990 if (fHeightForWidthLayoutContext != NULL) {
991 fHeightForWidthLayoutContext->RemoveListener(this);
992 fHeightForWidthLayoutContext = NULL;
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;
1001 fHeightForWidthLayoutContext = context;
1003 if (fHeightForWidthLayoutContext != NULL)
1004 fHeightForWidthLayoutContext->AddListener(this);
1006 InvalidateHeightForWidth();
1008 return true;
1012 void
1013 BTwoDimensionalLayout::VerticalCompoundLayouter::LayoutContextLeft(
1014 BLayoutContext* context)
1016 fHeightForWidthLayoutContext = NULL;
1020 // #pragma mark - LocalLayouter
1023 BTwoDimensionalLayout::LocalLayouter::LocalLayouter(
1024 BTwoDimensionalLayout* layout)
1026 fLayout(layout),
1027 fHLayouter(new CompoundLayouter(B_HORIZONTAL)),
1028 fVLayouter(new VerticalCompoundLayouter),
1029 fHeightForWidthItems(),
1030 fHorizontalLayoutContext(NULL),
1031 fHorizontalLayoutWidth(0),
1032 fHeightForWidthConstraintsAdded(false)
1034 fHLayouter->AddLocalLayouter(this);
1035 fVLayouter->AddLocalLayouter(this);
1039 BTwoDimensionalLayout::LocalLayouter::~LocalLayouter()
1041 if (fHLayouter != NULL) {
1042 fHLayouter->RemoveLocalLayouter(this);
1043 fHLayouter->ReleaseReference();
1046 if (fVLayouter != NULL) {
1047 fVLayouter->RemoveLocalLayouter(this);
1048 fVLayouter->ReleaseReference();
1053 BSize
1054 BTwoDimensionalLayout::LocalLayouter::MinSize()
1056 return BSize(fHLayouter->GetLayouter(true)->MinSize(),
1057 fVLayouter->GetLayouter(true)->MinSize());
1061 BSize
1062 BTwoDimensionalLayout::LocalLayouter::MaxSize()
1064 return BSize(fHLayouter->GetLayouter(true)->MaxSize(),
1065 fVLayouter->GetLayouter(true)->MaxSize());
1069 BSize
1070 BTwoDimensionalLayout::LocalLayouter::PreferredSize()
1072 return BSize(fHLayouter->GetLayouter(true)->PreferredSize(),
1073 fVLayouter->GetLayouter(true)->PreferredSize());
1077 void
1078 BTwoDimensionalLayout::LocalLayouter::InvalidateLayout()
1080 fHLayouter->InvalidateLayout();
1081 fVLayouter->InvalidateLayout();
1085 void
1086 BTwoDimensionalLayout::LocalLayouter::Layout(BSize size)
1088 DoHorizontalLayout(size.width);
1089 fVLayouter->Layout(size.height, this, fLayout->LayoutContext());
1093 BRect
1094 BTwoDimensionalLayout::LocalLayouter::ItemFrame(Dimensions itemDimensions)
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);
1108 void
1109 BTwoDimensionalLayout::LocalLayouter::ValidateMinMax()
1111 if (fHLayouter->IsMinMaxValid() && fVLayouter->IsMinMaxValid())
1112 return;
1114 if (!fHLayouter->IsMinMaxValid())
1115 fHeightForWidthItems.MakeEmpty();
1117 _SetHorizontalLayoutContext(NULL, -1);
1119 fHLayouter->ValidateMinMax();
1120 fVLayouter->ValidateMinMax();
1121 fLayout->ResetLayoutInvalidation();
1125 void
1126 BTwoDimensionalLayout::LocalLayouter::DoHorizontalLayout(float width)
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();
1138 void
1139 BTwoDimensionalLayout::LocalLayouter::InternalGetHeightForWidth(float width,
1140 float* minHeight, float* maxHeight, float* preferredHeight)
1142 DoHorizontalLayout(width);
1143 fVLayouter->InternalGetHeightForWidth(this, fHorizontalLayoutContext, false,
1144 minHeight, maxHeight, preferredHeight);
1148 void
1149 BTwoDimensionalLayout::LocalLayouter::AlignWith(LocalLayouter* other,
1150 orientation orientation)
1152 if (orientation == B_HORIZONTAL)
1153 other->fHLayouter->AbsorbCompoundLayouter(fHLayouter);
1154 else
1155 other->fVLayouter->AbsorbCompoundLayouter(fVLayouter);
1159 status_t
1160 BTwoDimensionalLayout::LocalLayouter::AddAlignedLayoutsToArchive(
1161 BArchiver* archiver)
1163 status_t err = fHLayouter->AddAlignedLayoutsToArchive(archiver, this);
1165 if (err == B_OK)
1166 err = fVLayouter->AddAlignedLayoutsToArchive(archiver, this);
1168 return err;
1172 status_t
1173 BTwoDimensionalLayout::LocalLayouter::AddOwnerToArchive(BArchiver* archiver,
1174 CompoundLayouter* requestedBy, bool& _wasAvailable)
1176 const char* field = kHAlignedLayoutField;
1177 if (requestedBy == fVLayouter)
1178 field = kVAlignedLayoutField;
1180 if ((_wasAvailable = archiver->IsArchived(fLayout)))
1181 return archiver->AddArchivable(field, fLayout);
1183 return B_NAME_NOT_FOUND;
1187 status_t
1188 BTwoDimensionalLayout::LocalLayouter::AlignLayoutsFromArchive(
1189 BUnarchiver* unarchiver, orientation posture)
1191 const char* field = kHAlignedLayoutField;
1192 if (posture == B_VERTICAL)
1193 field = kVAlignedLayoutField;
1195 int32 count;
1196 status_t err = unarchiver->ArchiveMessage()->GetInfo(field, NULL, &count);
1197 if (err == B_NAME_NOT_FOUND)
1198 return B_OK;
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);
1205 if (err == B_OK)
1206 retriever->AlignLayoutWith(fLayout, posture);
1209 return err;
1213 void
1214 BTwoDimensionalLayout::LocalLayouter::PrepareItems(
1215 CompoundLayouter* compoundLayouter)
1217 fLayout->PrepareItems(compoundLayouter->Orientation());
1221 int32
1222 BTwoDimensionalLayout::LocalLayouter::CountElements(
1223 CompoundLayouter* compoundLayouter)
1225 if (compoundLayouter->Orientation() == B_HORIZONTAL)
1226 return fLayout->InternalCountColumns();
1227 else
1228 return fLayout->InternalCountRows();
1232 bool
1233 BTwoDimensionalLayout::LocalLayouter::HasMultiElementItems(
1234 CompoundLayouter* compoundLayouter)
1236 if (compoundLayouter->Orientation() == B_HORIZONTAL)
1237 return fLayout->HasMultiColumnItems();
1238 else
1239 return fLayout->HasMultiRowItems();
1243 void
1244 BTwoDimensionalLayout::LocalLayouter::AddConstraints(
1245 CompoundLayouter* compoundLayouter, Layouter* layouter)
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);
1256 BSize min = item->MinSize();
1257 BSize max = item->MaxSize();
1258 BSize preferred = item->PreferredSize();
1260 if (orientation == B_HORIZONTAL) {
1261 layouter->AddConstraints(
1262 itemDimensions.x,
1263 itemDimensions.width,
1264 min.width,
1265 max.width,
1266 preferred.width);
1268 if (item->HasHeightForWidth())
1269 fHeightForWidthItems.AddItem(item);
1271 } else {
1272 layouter->AddConstraints(
1273 itemDimensions.y,
1274 itemDimensions.height,
1275 min.height,
1276 max.height,
1277 preferred.height);
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);
1296 float
1297 BTwoDimensionalLayout::LocalLayouter::Spacing(
1298 CompoundLayouter* compoundLayouter)
1300 return (compoundLayouter->Orientation() == B_HORIZONTAL
1301 ? fLayout->fHSpacing : fLayout->fVSpacing);
1305 bool
1306 BTwoDimensionalLayout::LocalLayouter::HasHeightForWidth()
1308 return !fHeightForWidthItems.IsEmpty();
1312 bool
1313 BTwoDimensionalLayout::LocalLayouter::AddHeightForWidthConstraints(
1314 VerticalCompoundLayouter* compoundLayouter, Layouter* layouter,
1315 BLayoutContext* context)
1317 if (context != fHorizontalLayoutContext)
1318 return false;
1320 if (fHeightForWidthConstraintsAdded)
1321 return false;
1323 LayoutInfo* hLayoutInfo = fHLayouter->GetLayoutInfo();
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);
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);
1345 SetHeightForWidthConstraintsAdded(true);
1347 return true;
1351 void
1352 BTwoDimensionalLayout::LocalLayouter::SetHeightForWidthConstraintsAdded(
1353 bool added)
1355 fHeightForWidthConstraintsAdded = added;
1359 void
1360 BTwoDimensionalLayout::LocalLayouter::SetCompoundLayouter(
1361 CompoundLayouter* compoundLayouter, orientation orientation)
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);
1372 if (compoundLayouter == oldCompoundLayouter)
1373 return;
1375 if (oldCompoundLayouter != NULL) {
1376 oldCompoundLayouter->RemoveLocalLayouter(this);
1377 oldCompoundLayouter->ReleaseReference();
1380 if (compoundLayouter != NULL)
1381 compoundLayouter->AcquireReference();
1383 InternalInvalidateLayout(compoundLayouter);
1387 void
1388 BTwoDimensionalLayout::LocalLayouter::InternalInvalidateLayout(
1389 CompoundLayouter* compoundLayouter)
1391 _SetHorizontalLayoutContext(NULL, -1);
1393 fLayout->BLayout::InvalidateLayout();
1397 void
1398 BTwoDimensionalLayout::LocalLayouter::_SetHorizontalLayoutContext(
1399 BLayoutContext* context, float width)
1401 if (context != fHorizontalLayoutContext) {
1402 if (fHorizontalLayoutContext != NULL)
1403 fHorizontalLayoutContext->RemoveListener(this);
1405 fHorizontalLayoutContext = context;
1407 if (fHorizontalLayoutContext != NULL)
1408 fHorizontalLayoutContext->AddListener(this);
1411 fHorizontalLayoutWidth = width;
1415 void
1416 BTwoDimensionalLayout::LocalLayouter::LayoutContextLeft(BLayoutContext* context)
1418 fHorizontalLayoutContext = NULL;
1419 fHorizontalLayoutWidth = -1;
1423 status_t
1424 BTwoDimensionalLayout::Perform(perform_code code, void* _data)
1426 return BAbstractLayout::Perform(code, _data);
1430 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout1() {}
1431 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout2() {}
1432 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout3() {}
1433 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout4() {}
1434 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout5() {}
1435 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout6() {}
1436 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout7() {}
1437 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout8() {}
1438 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout9() {}
1439 void BTwoDimensionalLayout::_ReservedTwoDimensionalLayout10() {}