Author: yourpalal Date: 2011-04-15 01:20:27 +0200 (Fri, 15 Apr 2011) New Revision: 41252 Changeset: https://dev.haiku-os.org/changeset/41252 Ticket: https://dev.haiku-os.org/ticket/6979 Added: haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.cpp haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.h Modified: haiku/trunk/src/kits/interface/GridLayout.cpp haiku/trunk/src/kits/interface/Jamfile haiku/trunk/src/kits/interface/TwoDimensionalLayout.cpp Log: Add a new layouter 'CollapsingLayouter', which is used by BTwoDimensionalLayout to collapse empty rows or columns. Update BGridLayout so that empty rows/columns are given min/max constraints of B_SIZE_UNSET by default, which allows them to be collapsed. Fixes #6979 Modified: haiku/trunk/src/kits/interface/GridLayout.cpp =================================================================== --- haiku/trunk/src/kits/interface/GridLayout.cpp 2011-04-14 23:00:33 UTC (rev 41251) +++ haiku/trunk/src/kits/interface/GridLayout.cpp 2011-04-14 23:20:27 UTC (rev 41252) @@ -90,7 +90,7 @@ { if (Info* info = _InfoAt(index)) return info->minSize; - return -1; + return B_SIZE_UNSET; } void SetMinSize(int32 index, float size) @@ -103,7 +103,7 @@ { if (Info* info = _InfoAt(index)) return info->maxSize; - return B_SIZE_UNLIMITED; + return B_SIZE_UNSET; } void SetMaxSize(int32 index, float size) @@ -138,8 +138,8 @@ for (int32 i = count; i <= index; i++) { Info* info = new Info; info->weight = 1; - info->minSize = 0; - info->maxSize = B_SIZE_UNLIMITED; + info->minSize = B_SIZE_UNSET; + info->maxSize = B_SIZE_UNSET; fInfos.AddItem(info); } } Modified: haiku/trunk/src/kits/interface/Jamfile =================================================================== --- haiku/trunk/src/kits/interface/Jamfile 2011-04-14 23:00:33 UTC (rev 41251) +++ haiku/trunk/src/kits/interface/Jamfile 2011-04-14 23:20:27 UTC (rev 41252) @@ -133,6 +133,7 @@ WidthBuffer.cpp # layouter + CollapsingLayouter.cpp ComplexLayouter.cpp Layouter.cpp LayoutOptimizer.cpp Modified: haiku/trunk/src/kits/interface/TwoDimensionalLayout.cpp =================================================================== --- haiku/trunk/src/kits/interface/TwoDimensionalLayout.cpp 2011-04-14 23:00:33 UTC (rev 41251) +++ haiku/trunk/src/kits/interface/TwoDimensionalLayout.cpp 2011-04-14 23:20:27 UTC (rev 41252) @@ -18,9 +18,7 @@ #include <Referenceable.h> -#include "ComplexLayouter.h" -#include "OneElementLayouter.h" -#include "SimpleLayouter.h" +#include "CollapsingLayouter.h" @@ -706,12 +704,7 @@ int elementCount = _CountElements(); - if (elementCount <= 1) - fLayouter = new OneElementLayouter(); - else if (_HasMultiElementItems()) - fLayouter = new ComplexLayouter(elementCount, _Spacing()); - else - fLayouter = new SimpleLayouter(elementCount, _Spacing()); + fLayouter = new CollapsingLayouter(elementCount, _Spacing()); // tell the layouter about our constraints // TODO: We should probably ignore local layouters whose view is hidden. Added: haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.cpp =================================================================== --- haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.cpp (rev 0) +++ haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.cpp 2011-04-14 23:20:27 UTC (rev 41252) @@ -0,0 +1,334 @@ +/* + * Copyright 2011, Haiku, Inc. + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "CollapsingLayouter.h" + +#include "ComplexLayouter.h" +#include "OneElementLayouter.h" +#include "SimpleLayouter.h" + +#include <ObjectList.h> +#include <Size.h> + + +class CollapsingLayouter::ProxyLayoutInfo : public LayoutInfo { +public: + ProxyLayoutInfo(LayoutInfo* target, int32 elementCount) + : + fTarget(target), + fElementCount(elementCount) + { + fElements = new int32[elementCount]; + } + + ~ProxyLayoutInfo() + { + delete[] fElements; + delete fTarget; + } + + void + LayoutTarget(Layouter* layouter, float size) + { + if (layouter) + layouter->Layout(fTarget, size); + } + + void + SetElementPosition(int32 element, int32 position) + { + fElements[element] = position; + } + + float + ElementLocation(int32 element) + { + if (element < 0 || element >= fElementCount || fElements[element] < 0) + return 0; + return fTarget->ElementLocation(fElements[element]); + } + + float + ElementSize(int32 element) + { + if (element < 0 || element >= fElementCount || fElements[element] < 0) + return 0; + return fTarget->ElementSize(fElements[element]); + } + + float + ElementRangeSize(int32 element, int32 length) + { + if (element < 0 || element >= fElementCount || fElements[element] < 0) + return 0; + return fTarget->ElementRangeSize(fElements[element], length); + } + +private: + int32* fElements; + LayoutInfo* fTarget; + int32 fElementCount; +}; + + +struct CollapsingLayouter::Constraint { + int32 length; + float min; + float max; + float preferred; +}; + + +struct CollapsingLayouter::ElementInfo { + float weight; + int32 position; + bool valid; + BObjectList<Constraint> constraints; + + ElementInfo() + : + weight(0), + position(-1), + valid(false), + constraints(5, true) + { + } + + ~ElementInfo() + { + } + + void SetTo(const ElementInfo& other) + { + weight = other.weight; + position = other.position; + valid = other.valid; + for (int32 i = other.constraints.CountItems() - 1; i >= 0; i--) + constraints.AddItem(new Constraint(*other.constraints.ItemAt(i))); + } + + int32 LengthOfLongestConstraint() + { + int32 length = 0; + for (int32 i = constraints.CountItems() - 1; i >= 0; i--) + length = max_c(length, constraints.ItemAt(i)->length); + return length; + } +}; + + +CollapsingLayouter::CollapsingLayouter(int32 elementCount, float spacing) + : + fElementCount(elementCount), + fElements(new ElementInfo[elementCount]), + fValidElementCount(0), + fHaveMultiElementConstraints(false), + fSpacing(spacing), + fLayouter(NULL) +{ +} + + +CollapsingLayouter::~CollapsingLayouter() +{ + delete[] fElements; + delete fLayouter; +} + + +void +CollapsingLayouter::AddConstraints(int32 element, int32 length, float min, + float max, float preferred) +{ + if (min == B_SIZE_UNSET && max == B_SIZE_UNSET) + return; + if (element < 0 || length <= 0 || element + length > fElementCount) + return; + + Constraint* constraint = new Constraint(); + constraint->length = length; + constraint->min = min; + constraint->max = max; + constraint->preferred = preferred; + + if (length > 1) + fHaveMultiElementConstraints = true; + + int32 validElements = fValidElementCount; + + for (int32 i = element; i < element + length; i++) { + if (fElements[i].valid == false) { + fElements[i].valid = true; + fValidElementCount++; + } + } + + fElements[element].constraints.AddItem(constraint); + if (fValidElementCount > validElements) { + delete fLayouter; + fLayouter = NULL; + } + + if (fLayouter) + _AddConstraints(element, constraint); + +} + + +void +CollapsingLayouter::SetWeight(int32 element, float weight) +{ + if (element < 0 || element >= fElementCount) + return; + + ElementInfo& elementInfo = fElements[element]; + elementInfo.weight = weight; + + if (fLayouter && elementInfo.position >= 0) + fLayouter->SetWeight(elementInfo.position, weight); +} + + +float +CollapsingLayouter::MinSize() +{ + _ValidateLayouter(); + return fLayouter->MinSize(); +} + + +float +CollapsingLayouter::MaxSize() +{ + _ValidateLayouter(); + return fLayouter->MaxSize(); +} + + +float +CollapsingLayouter::PreferredSize() +{ + _ValidateLayouter(); + return fLayouter->PreferredSize(); +} + + +LayoutInfo* +CollapsingLayouter::CreateLayoutInfo() +{ + _ValidateLayouter(); + + return new ProxyLayoutInfo(fLayouter->CreateLayoutInfo(), fElementCount); +} + + +void +CollapsingLayouter::Layout(LayoutInfo* layoutInfo, float size) +{ + _ValidateLayouter(); + ProxyLayoutInfo* info = static_cast<ProxyLayoutInfo*>(layoutInfo); + for (int32 i = 0; i < fElementCount; i++) { + info->SetElementPosition(i, fElements[i].position); + } + + info->LayoutTarget(fLayouter, size); +} + + +Layouter* +CollapsingLayouter::CloneLayouter() +{ + CollapsingLayouter* clone = new CollapsingLayouter(fElementCount, fSpacing); + for (int32 i = 0; i < fElementCount; i++) + clone->fElements[i].SetTo(fElements[i]); + + clone->fValidElementCount = fValidElementCount; + clone->fHaveMultiElementConstraints = fHaveMultiElementConstraints; + + if (fLayouter) + clone->fLayouter = fLayouter->CloneLayouter(); + return clone; +} + + +void +CollapsingLayouter::_ValidateLayouter() +{ + if (fLayouter) + return; + + _CreateLayouter(); + _DoCollapse(); + _AddConstraints(); + _SetWeights(); +} + + +Layouter* +CollapsingLayouter::_CreateLayouter() +{ + if (fLayouter) + return fLayouter; + + if (fValidElementCount == 0) { + fLayouter = NULL; + } else if (fValidElementCount == 1) { + fLayouter = new OneElementLayouter(); + } else if (fHaveMultiElementConstraints) { + fLayouter = new ComplexLayouter(fValidElementCount, fSpacing); + } else { + fLayouter = new SimpleLayouter(fValidElementCount, fSpacing); + } + + return fLayouter; +} + + +void +CollapsingLayouter::_DoCollapse() +{ + int32 shift = 0; + for (int32 i = 0; i < fElementCount; i++) { + ElementInfo& element = fElements[i]; + if (!element.valid) { + shift++; + element.position = -1; + continue; + } else { + element.position = i - shift; + } + } +} + + +void +CollapsingLayouter::_AddConstraints() +{ + if (fLayouter == NULL) + return; + + for (int32 i = 0; i < fElementCount; i++) { + ElementInfo& element = fElements[i]; + for (int32 i = element.constraints.CountItems() - 1; i >= 0; i--) + _AddConstraints(element.position, element.constraints.ItemAt(i)); + } +} + + +void +CollapsingLayouter::_AddConstraints(int32 position, const Constraint* c) +{ + fLayouter->AddConstraints(position, c->length, c->min, c->max, + c->preferred); +} + + +void +CollapsingLayouter::_SetWeights() +{ + for (int32 i = 0; i < fElementCount; i++) { + fLayouter->SetWeight(fElements[i].position, fElements[i].weight); + } +} Added: haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.h =================================================================== --- haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.h (rev 0) +++ haiku/trunk/src/kits/interface/layouter/CollapsingLayouter.h 2011-04-14 23:20:27 UTC (rev 41252) @@ -0,0 +1,68 @@ +/* + * Copyright 2011, Haiku, Inc. + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef COLLAPSING_LAYOUTER_H +#define COLLAPSING_LAYOUTER_H + +#include "Layouter.h" + + +namespace BPrivate { +namespace Layout { + + +/* This layouter wraps either a Compound, Simple or OneElement layouter, and + * removes elements which have no constraints, or min/max constraints of + * B_SIZE_UNSET. The child layouter is given only the constraints for the + * remaining elements. When using the LayoutInfo of this layouter, + * collapsed (removed) elements are given no space on screen. + */ +class CollapsingLayouter : public Layouter { +public: + CollapsingLayouter(int32 elementCount, + float spacing); + virtual ~CollapsingLayouter(); + + virtual void AddConstraints(int32 element, int32 length, + float min, float max, float preferred); + virtual void SetWeight(int32 element, float weight); + + virtual float MinSize(); + virtual float MaxSize(); + virtual float PreferredSize(); + + virtual LayoutInfo* CreateLayoutInfo(); + + virtual void Layout(LayoutInfo* layoutInfo, float size); + + virtual Layouter* CloneLayouter(); + + +private: + class ProxyLayoutInfo; + struct Constraint; + struct ElementInfo; + + void _ValidateLayouter(); + Layouter* _CreateLayouter(); + void _DoCollapse(); + void _AddConstraints(); + void _AddConstraints(int32 position, + const Constraint* c); + void _SetWeights(); + + int32 fElementCount; + ElementInfo* fElements; + int32 fValidElementCount; + bool fHaveMultiElementConstraints; + float fSpacing; + Layouter* fLayouter; +}; + +} // namespace Layout +} // namespace BPrivate + +using BPrivate::Layout::CollapsingLayouter; + +#endif // COLLAPSING_LAYOUTER_H