...from orientation params. Elaborated type specifiers are not needed for C++ code and removing them makes doxygen happy. Verified working on both gcc2h and gcc4h builds.
1451 lines
34 KiB
C++
1451 lines
34 KiB
C++
/*
|
|
* Copyright 2006-2009, Ingo Weinhold <[email protected]>.
|
|
* All rights reserved. Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "SplitLayout.h"
|
|
|
|
#include <new>
|
|
#include <stdio.h>
|
|
|
|
#include <ControlLook.h>
|
|
#include <LayoutItem.h>
|
|
#include <LayoutUtils.h>
|
|
#include <Message.h>
|
|
#include <View.h>
|
|
|
|
#include "OneElementLayouter.h"
|
|
#include "SimpleLayouter.h"
|
|
|
|
|
|
using std::nothrow;
|
|
|
|
|
|
// archivng constants
|
|
namespace {
|
|
const char* const kItemCollapsibleField = "BSplitLayout:item:collapsible";
|
|
const char* const kItemWeightField = "BSplitLayout:item:weight";
|
|
const char* const kSpacingField = "BSplitLayout:spacing";
|
|
const char* const kSplitterSizeField = "BSplitLayout:splitterSize";
|
|
const char* const kIsVerticalField = "BSplitLayout:vertical";
|
|
const char* const kInsetsField = "BSplitLayout:insets";
|
|
}
|
|
|
|
|
|
class BSplitLayout::ItemLayoutInfo {
|
|
public:
|
|
float weight;
|
|
BRect layoutFrame;
|
|
BSize min;
|
|
BSize max;
|
|
bool isVisible;
|
|
bool isCollapsible;
|
|
|
|
ItemLayoutInfo()
|
|
:
|
|
weight(1.0f),
|
|
layoutFrame(0, 0, -1, -1),
|
|
min(),
|
|
max(),
|
|
isVisible(true),
|
|
isCollapsible(true)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
class BSplitLayout::ValueRange {
|
|
public:
|
|
int32 sumValue; // including spacing
|
|
int32 previousMin;
|
|
int32 previousMax;
|
|
int32 previousSize;
|
|
int32 nextMin;
|
|
int32 nextMax;
|
|
int32 nextSize;
|
|
};
|
|
|
|
|
|
class BSplitLayout::SplitterItem : public BLayoutItem {
|
|
public:
|
|
SplitterItem(BSplitLayout* layout)
|
|
:
|
|
fLayout(layout),
|
|
fFrame()
|
|
{
|
|
}
|
|
|
|
|
|
virtual BSize MinSize()
|
|
{
|
|
if (fLayout->Orientation() == B_HORIZONTAL)
|
|
return BSize(fLayout->SplitterSize() - 1, -1);
|
|
else
|
|
return BSize(-1, fLayout->SplitterSize() - 1);
|
|
}
|
|
|
|
virtual BSize MaxSize()
|
|
{
|
|
if (fLayout->Orientation() == B_HORIZONTAL)
|
|
return BSize(fLayout->SplitterSize() - 1, B_SIZE_UNLIMITED);
|
|
else
|
|
return BSize(B_SIZE_UNLIMITED, fLayout->SplitterSize() - 1);
|
|
}
|
|
|
|
virtual BSize PreferredSize()
|
|
{
|
|
return MinSize();
|
|
}
|
|
|
|
virtual BAlignment Alignment()
|
|
{
|
|
return BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
|
|
}
|
|
|
|
virtual void SetExplicitMinSize(BSize size)
|
|
{
|
|
// not allowed
|
|
}
|
|
|
|
virtual void SetExplicitMaxSize(BSize size)
|
|
{
|
|
// not allowed
|
|
}
|
|
|
|
virtual void SetExplicitPreferredSize(BSize size)
|
|
{
|
|
// not allowed
|
|
}
|
|
|
|
virtual void SetExplicitAlignment(BAlignment alignment)
|
|
{
|
|
// not allowed
|
|
}
|
|
|
|
virtual bool IsVisible()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual void SetVisible(bool visible)
|
|
{
|
|
// not allowed
|
|
}
|
|
|
|
|
|
virtual BRect Frame()
|
|
{
|
|
return fFrame;
|
|
}
|
|
|
|
virtual void SetFrame(BRect frame)
|
|
{
|
|
fFrame = frame;
|
|
}
|
|
|
|
private:
|
|
BSplitLayout* fLayout;
|
|
BRect fFrame;
|
|
};
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
BSplitLayout::BSplitLayout(orientation orientation, float spacing)
|
|
:
|
|
fOrientation(orientation),
|
|
fLeftInset(0),
|
|
fRightInset(0),
|
|
fTopInset(0),
|
|
fBottomInset(0),
|
|
fSplitterSize(6),
|
|
fSpacing(BControlLook::ComposeSpacing(spacing)),
|
|
|
|
fSplitterItems(),
|
|
fVisibleItems(),
|
|
fMin(),
|
|
fMax(),
|
|
fPreferred(),
|
|
|
|
fHorizontalLayouter(NULL),
|
|
fVerticalLayouter(NULL),
|
|
fHorizontalLayoutInfo(NULL),
|
|
fVerticalLayoutInfo(NULL),
|
|
|
|
fHeightForWidthItems(),
|
|
fHeightForWidthVerticalLayouter(NULL),
|
|
fHeightForWidthHorizontalLayoutInfo(NULL),
|
|
|
|
fLayoutValid(false),
|
|
|
|
fCachedHeightForWidthWidth(-2),
|
|
fHeightForWidthVerticalLayouterWidth(-2),
|
|
fCachedMinHeightForWidth(-1),
|
|
fCachedMaxHeightForWidth(-1),
|
|
fCachedPreferredHeightForWidth(-1),
|
|
|
|
fDraggingStartPoint(),
|
|
fDraggingStartValue(0),
|
|
fDraggingCurrentValue(0),
|
|
fDraggingSplitterIndex(-1)
|
|
{
|
|
}
|
|
|
|
|
|
BSplitLayout::BSplitLayout(BMessage* from)
|
|
:
|
|
BAbstractLayout(BUnarchiver::PrepareArchive(from)),
|
|
fOrientation(B_HORIZONTAL),
|
|
fLeftInset(0),
|
|
fRightInset(0),
|
|
fTopInset(0),
|
|
fBottomInset(0),
|
|
fSplitterSize(6),
|
|
fSpacing(be_control_look->DefaultItemSpacing()),
|
|
|
|
fSplitterItems(),
|
|
fVisibleItems(),
|
|
fMin(),
|
|
fMax(),
|
|
fPreferred(),
|
|
|
|
fHorizontalLayouter(NULL),
|
|
fVerticalLayouter(NULL),
|
|
fHorizontalLayoutInfo(NULL),
|
|
fVerticalLayoutInfo(NULL),
|
|
|
|
fHeightForWidthItems(),
|
|
fHeightForWidthVerticalLayouter(NULL),
|
|
fHeightForWidthHorizontalLayoutInfo(NULL),
|
|
|
|
fLayoutValid(false),
|
|
|
|
fCachedHeightForWidthWidth(-2),
|
|
fHeightForWidthVerticalLayouterWidth(-2),
|
|
fCachedMinHeightForWidth(-1),
|
|
fCachedMaxHeightForWidth(-1),
|
|
fCachedPreferredHeightForWidth(-1),
|
|
|
|
fDraggingStartPoint(),
|
|
fDraggingStartValue(0),
|
|
fDraggingCurrentValue(0),
|
|
fDraggingSplitterIndex(-1)
|
|
{
|
|
BUnarchiver unarchiver(from);
|
|
|
|
bool isVertical;
|
|
status_t err = from->FindBool(kIsVerticalField, &isVertical);
|
|
if (err != B_OK) {
|
|
unarchiver.Finish(err);
|
|
return;
|
|
}
|
|
fOrientation = (isVertical) ? B_VERTICAL : B_HORIZONTAL ;
|
|
|
|
BRect insets;
|
|
err = from->FindRect(kInsetsField, &insets);
|
|
if (err != B_OK) {
|
|
unarchiver.Finish(err);
|
|
return;
|
|
}
|
|
SetInsets(insets.left, insets.top, insets.right, insets.bottom);
|
|
|
|
err = from->FindFloat(kSplitterSizeField, &fSplitterSize);
|
|
if (err == B_OK)
|
|
err = from->FindFloat(kSpacingField, &fSpacing);
|
|
|
|
unarchiver.Finish(err);
|
|
}
|
|
|
|
|
|
BSplitLayout::~BSplitLayout()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetInsets(float left, float top, float right, float bottom)
|
|
{
|
|
fLeftInset = left;
|
|
fTopInset = top;
|
|
fRightInset = right;
|
|
fBottomInset = bottom;
|
|
|
|
InvalidateLayout();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::GetInsets(float* left, float* top, float* right,
|
|
float* bottom) const
|
|
{
|
|
if (left)
|
|
*left = fLeftInset;
|
|
if (top)
|
|
*top = fTopInset;
|
|
if (right)
|
|
*right = fRightInset;
|
|
if (bottom)
|
|
*bottom = fBottomInset;
|
|
}
|
|
|
|
|
|
float
|
|
BSplitLayout::Spacing() const
|
|
{
|
|
return fSpacing;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetSpacing(float spacing)
|
|
{
|
|
spacing = BControlLook::ComposeSpacing(spacing);
|
|
if (spacing != fSpacing) {
|
|
fSpacing = spacing;
|
|
|
|
InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
|
|
orientation
|
|
BSplitLayout::Orientation() const
|
|
{
|
|
return fOrientation;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetOrientation(orientation orientation)
|
|
{
|
|
if (orientation != fOrientation) {
|
|
fOrientation = orientation;
|
|
|
|
InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
|
|
float
|
|
BSplitLayout::SplitterSize() const
|
|
{
|
|
return fSplitterSize;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetSplitterSize(float size)
|
|
{
|
|
if (size != fSplitterSize) {
|
|
fSplitterSize = size;
|
|
|
|
InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
|
|
BLayoutItem*
|
|
BSplitLayout::AddView(BView* child)
|
|
{
|
|
return BAbstractLayout::AddView(child);
|
|
}
|
|
|
|
|
|
BLayoutItem*
|
|
BSplitLayout::AddView(int32 index, BView* child)
|
|
{
|
|
return BAbstractLayout::AddView(index, child);
|
|
}
|
|
|
|
|
|
BLayoutItem*
|
|
BSplitLayout::AddView(BView* child, float weight)
|
|
{
|
|
return AddView(-1, child, weight);
|
|
}
|
|
|
|
|
|
BLayoutItem*
|
|
BSplitLayout::AddView(int32 index, BView* child, float weight)
|
|
{
|
|
BLayoutItem* item = AddView(index, child);
|
|
if (item)
|
|
SetItemWeight(item, weight);
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::AddItem(BLayoutItem* item)
|
|
{
|
|
return BAbstractLayout::AddItem(item);
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::AddItem(int32 index, BLayoutItem* item)
|
|
{
|
|
return BAbstractLayout::AddItem(index, item);
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::AddItem(BLayoutItem* item, float weight)
|
|
{
|
|
return AddItem(-1, item, weight);
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::AddItem(int32 index, BLayoutItem* item, float weight)
|
|
{
|
|
bool success = AddItem(index, item);
|
|
if (success)
|
|
SetItemWeight(item, weight);
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
float
|
|
BSplitLayout::ItemWeight(int32 index) const
|
|
{
|
|
if (index < 0 || index >= CountItems())
|
|
return 0;
|
|
|
|
return ItemWeight(ItemAt(index));
|
|
}
|
|
|
|
|
|
float
|
|
BSplitLayout::ItemWeight(BLayoutItem* item) const
|
|
{
|
|
if (ItemLayoutInfo* info = _ItemLayoutInfo(item))
|
|
return info->weight;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetItemWeight(int32 index, float weight, bool invalidateLayout)
|
|
{
|
|
if (index < 0 || index >= CountItems())
|
|
return;
|
|
|
|
BLayoutItem* item = ItemAt(index);
|
|
SetItemWeight(item, weight);
|
|
|
|
if (fHorizontalLayouter) {
|
|
int32 visibleIndex = fVisibleItems.IndexOf(item);
|
|
if (visibleIndex >= 0) {
|
|
if (fOrientation == B_HORIZONTAL)
|
|
fHorizontalLayouter->SetWeight(visibleIndex, weight);
|
|
else
|
|
fVerticalLayouter->SetWeight(visibleIndex, weight);
|
|
}
|
|
}
|
|
|
|
if (invalidateLayout)
|
|
InvalidateLayout();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetItemWeight(BLayoutItem* item, float weight)
|
|
{
|
|
if (ItemLayoutInfo* info = _ItemLayoutInfo(item))
|
|
info->weight = weight;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::IsCollapsible(int32 index) const
|
|
{
|
|
return _ItemLayoutInfo(ItemAt(index))->isCollapsible;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetCollapsible(bool collapsible)
|
|
{
|
|
SetCollapsible(0, CountItems() - 1, collapsible);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetCollapsible(int32 index, bool collapsible)
|
|
{
|
|
SetCollapsible(index, index, collapsible);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetCollapsible(int32 first, int32 last, bool collapsible)
|
|
{
|
|
for (int32 i = first; i <= last; i++)
|
|
_ItemLayoutInfo(ItemAt(i))->isCollapsible = collapsible;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::IsItemCollapsed(int32 index) const
|
|
{
|
|
return _ItemLayoutInfo(ItemAt(index))->isVisible;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::SetItemCollapsed(int32 index, bool collapsed)
|
|
{
|
|
ItemAt(index)->SetVisible(collapsed);
|
|
|
|
InvalidateLayout(true);
|
|
}
|
|
|
|
|
|
BSize
|
|
BSplitLayout::BaseMinSize()
|
|
{
|
|
_ValidateMinMax();
|
|
|
|
return _AddInsets(fMin);
|
|
}
|
|
|
|
|
|
BSize
|
|
BSplitLayout::BaseMaxSize()
|
|
{
|
|
_ValidateMinMax();
|
|
|
|
return _AddInsets(fMax);
|
|
}
|
|
|
|
|
|
BSize
|
|
BSplitLayout::BasePreferredSize()
|
|
{
|
|
_ValidateMinMax();
|
|
|
|
return _AddInsets(fPreferred);
|
|
}
|
|
|
|
|
|
BAlignment
|
|
BSplitLayout::BaseAlignment()
|
|
{
|
|
return BAbstractLayout::BaseAlignment();
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::HasHeightForWidth()
|
|
{
|
|
_ValidateMinMax();
|
|
|
|
return !fHeightForWidthItems.IsEmpty();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::GetHeightForWidth(float width, float* min, float* max,
|
|
float* preferred)
|
|
{
|
|
if (!HasHeightForWidth())
|
|
return;
|
|
|
|
float innerWidth = _SubtractInsets(BSize(width, 0)).width;
|
|
_InternalGetHeightForWidth(innerWidth, false, min, max, preferred);
|
|
_AddInsets(min, max, preferred);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::LayoutInvalidated(bool children)
|
|
{
|
|
delete fHorizontalLayouter;
|
|
delete fVerticalLayouter;
|
|
delete fHorizontalLayoutInfo;
|
|
delete fVerticalLayoutInfo;
|
|
|
|
fHorizontalLayouter = NULL;
|
|
fVerticalLayouter = NULL;
|
|
fHorizontalLayoutInfo = NULL;
|
|
fVerticalLayoutInfo = NULL;
|
|
|
|
_InvalidateCachedHeightForWidth();
|
|
|
|
fLayoutValid = false;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::DoLayout()
|
|
{
|
|
_ValidateMinMax();
|
|
|
|
// layout the elements
|
|
BSize size = _SubtractInsets(LayoutArea().Size());
|
|
fHorizontalLayouter->Layout(fHorizontalLayoutInfo, size.width);
|
|
|
|
Layouter* verticalLayouter;
|
|
if (HasHeightForWidth()) {
|
|
float minHeight, maxHeight, preferredHeight;
|
|
_InternalGetHeightForWidth(size.width, true, &minHeight, &maxHeight,
|
|
&preferredHeight);
|
|
size.height = max_c(size.height, minHeight);
|
|
verticalLayouter = fHeightForWidthVerticalLayouter;
|
|
} else
|
|
verticalLayouter = fVerticalLayouter;
|
|
|
|
verticalLayouter->Layout(fVerticalLayoutInfo, size.height);
|
|
|
|
float xOffset = fLeftInset;
|
|
float yOffset = fTopInset;
|
|
float splitterWidth = 0; // pixel counts, no distances
|
|
float splitterHeight = 0; //
|
|
float xSpacing = 0;
|
|
float ySpacing = 0;
|
|
if (fOrientation == B_HORIZONTAL) {
|
|
splitterWidth = fSplitterSize;
|
|
splitterHeight = size.height + 1;
|
|
xSpacing = fSpacing;
|
|
} else {
|
|
splitterWidth = size.width + 1;
|
|
splitterHeight = fSplitterSize;
|
|
ySpacing = fSpacing;
|
|
}
|
|
|
|
int itemCount = CountItems();
|
|
for (int i = 0; i < itemCount; i++) {
|
|
// layout the splitter
|
|
if (i > 0) {
|
|
SplitterItem* splitterItem = _SplitterItemAt(i - 1);
|
|
|
|
_LayoutItem(splitterItem, BRect(xOffset, yOffset,
|
|
xOffset + splitterWidth - 1, yOffset + splitterHeight - 1),
|
|
true);
|
|
|
|
if (fOrientation == B_HORIZONTAL)
|
|
xOffset += splitterWidth + xSpacing;
|
|
else
|
|
yOffset += splitterHeight + ySpacing;
|
|
}
|
|
|
|
// layout the item
|
|
BLayoutItem* item = ItemAt(i);
|
|
int32 visibleIndex = fVisibleItems.IndexOf(item);
|
|
if (visibleIndex < 0) {
|
|
_LayoutItem(item, BRect(), false);
|
|
continue;
|
|
}
|
|
|
|
// get the dimensions of the item
|
|
float width = fHorizontalLayoutInfo->ElementSize(visibleIndex);
|
|
float height = fVerticalLayoutInfo->ElementSize(visibleIndex);
|
|
|
|
// place the component
|
|
_LayoutItem(item, BRect(xOffset, yOffset, xOffset + width,
|
|
yOffset + height), true);
|
|
|
|
if (fOrientation == B_HORIZONTAL)
|
|
xOffset += width + xSpacing + 1;
|
|
else
|
|
yOffset += height + ySpacing + 1;
|
|
}
|
|
|
|
fLayoutValid = true;
|
|
}
|
|
|
|
|
|
BRect
|
|
BSplitLayout::SplitterItemFrame(int32 index) const
|
|
{
|
|
if (SplitterItem* item = _SplitterItemAt(index))
|
|
return item->Frame();
|
|
return BRect();
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::IsAboveSplitter(const BPoint& point) const
|
|
{
|
|
return _SplitterItemAt(point) != NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::StartDraggingSplitter(BPoint point)
|
|
{
|
|
StopDraggingSplitter();
|
|
|
|
// Layout must be valid. Bail out, if it isn't.
|
|
if (!fLayoutValid)
|
|
return false;
|
|
|
|
// Things shouldn't be draggable, if we have a >= max layout.
|
|
BSize size = _SubtractInsets(LayoutArea().Size());
|
|
if ((fOrientation == B_HORIZONTAL && size.width >= fMax.width)
|
|
|| (fOrientation == B_VERTICAL && size.height >= fMax.height)) {
|
|
return false;
|
|
}
|
|
|
|
int32 index = -1;
|
|
if (_SplitterItemAt(point, &index) != NULL) {
|
|
fDraggingStartPoint = Owner()->ConvertToScreen(point);
|
|
fDraggingStartValue = _SplitterValue(index);
|
|
fDraggingCurrentValue = fDraggingStartValue;
|
|
fDraggingSplitterIndex = index;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::DragSplitter(BPoint point)
|
|
{
|
|
if (fDraggingSplitterIndex < 0)
|
|
return false;
|
|
|
|
point = Owner()->ConvertToScreen(point);
|
|
|
|
int32 valueDiff;
|
|
if (fOrientation == B_HORIZONTAL)
|
|
valueDiff = int32(point.x - fDraggingStartPoint.x);
|
|
else
|
|
valueDiff = int32(point.y - fDraggingStartPoint.y);
|
|
|
|
return _SetSplitterValue(fDraggingSplitterIndex,
|
|
fDraggingStartValue + valueDiff);
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::StopDraggingSplitter()
|
|
{
|
|
if (fDraggingSplitterIndex < 0)
|
|
return false;
|
|
|
|
// update the item weights
|
|
_UpdateSplitterWeights();
|
|
|
|
fDraggingSplitterIndex = -1;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int32
|
|
BSplitLayout::DraggedSplitter() const
|
|
{
|
|
return fDraggingSplitterIndex;
|
|
}
|
|
|
|
|
|
status_t
|
|
BSplitLayout::Archive(BMessage* into, bool deep) const
|
|
{
|
|
BArchiver archiver(into);
|
|
status_t err = BAbstractLayout::Archive(into, deep);
|
|
|
|
if (err == B_OK)
|
|
err = into->AddBool(kIsVerticalField, fOrientation == B_VERTICAL);
|
|
|
|
if (err == B_OK) {
|
|
BRect insets(fLeftInset, fTopInset, fRightInset, fBottomInset);
|
|
err = into->AddRect(kInsetsField, insets);
|
|
}
|
|
|
|
if (err == B_OK)
|
|
err = into->AddFloat(kSplitterSizeField, fSplitterSize);
|
|
|
|
if (err == B_OK)
|
|
err = into->AddFloat(kSpacingField, fSpacing);
|
|
|
|
return archiver.Finish(err);
|
|
}
|
|
|
|
|
|
BArchivable*
|
|
BSplitLayout::Instantiate(BMessage* from)
|
|
{
|
|
if (validate_instantiation(from, "BSplitLayout"))
|
|
return new(std::nothrow) BSplitLayout(from);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
BSplitLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
|
|
{
|
|
ItemLayoutInfo* info = _ItemLayoutInfo(item);
|
|
|
|
status_t err = into->AddFloat(kItemWeightField, info->weight);
|
|
if (err == B_OK)
|
|
err = into->AddBool(kItemCollapsibleField, info->isCollapsible);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
status_t
|
|
BSplitLayout::ItemUnarchived(const BMessage* from,
|
|
BLayoutItem* item, int32 index)
|
|
{
|
|
ItemLayoutInfo* info = _ItemLayoutInfo(item);
|
|
status_t err = from->FindFloat(kItemWeightField, index, &info->weight);
|
|
|
|
if (err == B_OK) {
|
|
bool* collapsible = &info->isCollapsible;
|
|
err = from->FindBool(kItemCollapsibleField, index, collapsible);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
|
|
{
|
|
ItemLayoutInfo* itemInfo = new(nothrow) ItemLayoutInfo();
|
|
if (!itemInfo)
|
|
return false;
|
|
|
|
if (CountItems() > 1) {
|
|
SplitterItem* splitter = new(nothrow) SplitterItem(this);
|
|
ItemLayoutInfo* splitterInfo = new(nothrow) ItemLayoutInfo();
|
|
if (!splitter || !splitterInfo || !fSplitterItems.AddItem(splitter)) {
|
|
delete itemInfo;
|
|
delete splitter;
|
|
delete splitterInfo;
|
|
return false;
|
|
}
|
|
splitter->SetLayoutData(splitterInfo);
|
|
SetItemWeight(splitter, 0);
|
|
}
|
|
|
|
item->SetLayoutData(itemInfo);
|
|
SetItemWeight(item, 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::ItemRemoved(BLayoutItem* item, int32 atIndex)
|
|
{
|
|
if (fSplitterItems.CountItems() > 0) {
|
|
SplitterItem* splitterItem = (SplitterItem*)fSplitterItems.RemoveItem(
|
|
fSplitterItems.CountItems() - 1);
|
|
delete _ItemLayoutInfo(splitterItem);
|
|
delete splitterItem;
|
|
}
|
|
|
|
delete _ItemLayoutInfo(item);
|
|
item->SetLayoutData(NULL);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_InvalidateCachedHeightForWidth()
|
|
{
|
|
delete fHeightForWidthVerticalLayouter;
|
|
delete fHeightForWidthHorizontalLayoutInfo;
|
|
|
|
fHeightForWidthVerticalLayouter = NULL;
|
|
fHeightForWidthHorizontalLayoutInfo = NULL;
|
|
|
|
fCachedHeightForWidthWidth = -2;
|
|
fHeightForWidthVerticalLayouterWidth = -2;
|
|
}
|
|
|
|
|
|
BSplitLayout::SplitterItem*
|
|
BSplitLayout::_SplitterItemAt(const BPoint& point, int32* index) const
|
|
{
|
|
int32 splitterCount = fSplitterItems.CountItems();
|
|
for (int32 i = 0; i < splitterCount; i++) {
|
|
SplitterItem* splitItem = _SplitterItemAt(i);
|
|
BRect frame = splitItem->Frame();
|
|
if (frame.Contains(point)) {
|
|
if (index != NULL)
|
|
*index = i;
|
|
return splitItem;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BSplitLayout::SplitterItem*
|
|
BSplitLayout::_SplitterItemAt(int32 index) const
|
|
{
|
|
return (SplitterItem*)fSplitterItems.ItemAt(index);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_GetSplitterValueRange(int32 index, ValueRange& range)
|
|
{
|
|
ItemLayoutInfo* previousInfo = _ItemLayoutInfo(ItemAt(index));
|
|
ItemLayoutInfo* nextInfo = _ItemLayoutInfo(ItemAt(index + 1));
|
|
if (fOrientation == B_HORIZONTAL) {
|
|
range.previousMin = (int32)previousInfo->min.width + 1;
|
|
range.previousMax = (int32)previousInfo->max.width + 1;
|
|
range.previousSize = previousInfo->layoutFrame.IntegerWidth() + 1;
|
|
range.nextMin = (int32)nextInfo->min.width + 1;
|
|
range.nextMax = (int32)nextInfo->max.width + 1;
|
|
range.nextSize = nextInfo->layoutFrame.IntegerWidth() + 1;
|
|
} else {
|
|
range.previousMin = (int32)previousInfo->min.height + 1;
|
|
range.previousMax = (int32)previousInfo->max.height + 1;
|
|
range.previousSize = previousInfo->layoutFrame.IntegerHeight() + 1;
|
|
range.nextMin = (int32)nextInfo->min.height + 1;
|
|
range.nextMax = (int32)nextInfo->max.height + 1;
|
|
range.nextSize = (int32)nextInfo->layoutFrame.IntegerHeight() + 1;
|
|
}
|
|
|
|
range.sumValue = range.previousSize + range.nextSize;
|
|
if (previousInfo->isVisible)
|
|
range.sumValue += (int32)fSpacing;
|
|
if (nextInfo->isVisible)
|
|
range.sumValue += (int32)fSpacing;
|
|
}
|
|
|
|
|
|
int32
|
|
BSplitLayout::_SplitterValue(int32 index) const
|
|
{
|
|
ItemLayoutInfo* info = _ItemLayoutInfo(ItemAt(index));
|
|
if (info && info->isVisible) {
|
|
if (fOrientation == B_HORIZONTAL)
|
|
return info->layoutFrame.IntegerWidth() + 1 + (int32)fSpacing;
|
|
else
|
|
return info->layoutFrame.IntegerHeight() + 1 + (int32)fSpacing;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_LayoutItem(BLayoutItem* item, BRect frame, bool visible)
|
|
{
|
|
// update the layout frame
|
|
ItemLayoutInfo* info = _ItemLayoutInfo(item);
|
|
info->isVisible = visible;
|
|
if (visible)
|
|
info->layoutFrame = frame;
|
|
else
|
|
info->layoutFrame = BRect(0, 0, -1, -1);
|
|
|
|
// update min/max
|
|
info->min = item->MinSize();
|
|
info->max = item->MaxSize();
|
|
|
|
if (item->HasHeightForWidth()) {
|
|
BSize size = _SubtractInsets(LayoutArea().Size());
|
|
float minHeight, maxHeight;
|
|
item->GetHeightForWidth(size.width, &minHeight, &maxHeight, NULL);
|
|
info->min.height = max_c(info->min.height, minHeight);
|
|
info->max.height = min_c(info->max.height, maxHeight);
|
|
}
|
|
|
|
// layout the item
|
|
if (visible)
|
|
item->AlignInFrame(frame);
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_LayoutItem(BLayoutItem* item, ItemLayoutInfo* info)
|
|
{
|
|
// update the visibility of the item
|
|
bool isVisible = item->IsVisible();
|
|
bool visibilityChanged = (info->isVisible != isVisible);
|
|
if (visibilityChanged)
|
|
item->SetVisible(info->isVisible);
|
|
|
|
// nothing more to do, if the item is not visible
|
|
if (!info->isVisible)
|
|
return;
|
|
|
|
item->AlignInFrame(info->layoutFrame);
|
|
|
|
// if the item became visible, we need to update its internal layout
|
|
if (visibilityChanged &&
|
|
(fOrientation != B_HORIZONTAL || !HasHeightForWidth())) {
|
|
item->Relayout(true);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
BSplitLayout::_SetSplitterValue(int32 index, int32 value)
|
|
{
|
|
// if both items are collapsed, nothing can be dragged
|
|
BLayoutItem* previousItem = ItemAt(index);
|
|
BLayoutItem* nextItem = ItemAt(index + 1);
|
|
ItemLayoutInfo* previousInfo = _ItemLayoutInfo(previousItem);
|
|
ItemLayoutInfo* nextInfo = _ItemLayoutInfo(nextItem);
|
|
ItemLayoutInfo* splitterInfo = _ItemLayoutInfo(_SplitterItemAt(index));
|
|
bool previousVisible = previousInfo->isVisible;
|
|
bool nextVisible = nextInfo->isVisible;
|
|
if (!previousVisible && !nextVisible)
|
|
return false;
|
|
|
|
ValueRange range;
|
|
_GetSplitterValueRange(index, range);
|
|
|
|
value = max_c(min_c(value, range.sumValue), -(int32)fSpacing);
|
|
|
|
int32 previousSize = value - (int32)fSpacing;
|
|
int32 nextSize = range.sumValue - value - (int32)fSpacing;
|
|
|
|
// Note: While this collapsed-check is mathmatically correct (i.e. we
|
|
// collapse an item, if it would become smaller than half its minimum
|
|
// size), we might want to change it, since for the user it looks like
|
|
// collapsing happens earlier. The reason being that the only visual mark
|
|
// the user has is the mouse cursor which indeed hasn't crossed the middle
|
|
// of the item yet.
|
|
bool previousCollapsed = (previousSize <= range.previousMin / 2)
|
|
&& previousInfo->isCollapsible;
|
|
bool nextCollapsed = (nextSize <= range.nextMin / 2)
|
|
&& nextInfo->isCollapsible;
|
|
if (previousCollapsed && nextCollapsed) {
|
|
// we cannot collapse both items; we have to decide for one
|
|
if (previousSize < nextSize) {
|
|
// collapse previous
|
|
nextCollapsed = false;
|
|
nextSize = range.sumValue - (int32)fSpacing;
|
|
} else {
|
|
// collapse next
|
|
previousCollapsed = false;
|
|
previousSize = range.sumValue - (int32)fSpacing;
|
|
}
|
|
}
|
|
|
|
if (previousCollapsed || nextCollapsed) {
|
|
// one collapsed item -- check whether that violates the constraints
|
|
// of the other one
|
|
int32 availableSpace = range.sumValue - (int32)fSpacing;
|
|
if (previousCollapsed) {
|
|
if (availableSpace < range.nextMin
|
|
|| availableSpace > range.nextMax) {
|
|
// we cannot collapse the previous item
|
|
previousCollapsed = false;
|
|
}
|
|
} else {
|
|
if (availableSpace < range.previousMin
|
|
|| availableSpace > range.previousMax) {
|
|
// we cannot collapse the next item
|
|
nextCollapsed = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(previousCollapsed || nextCollapsed)) {
|
|
// no collapsed item -- check whether there is a close solution
|
|
previousSize = value - (int32)fSpacing;
|
|
nextSize = range.sumValue - value - (int32)fSpacing;
|
|
|
|
if (range.previousMin + range.nextMin + 2 * fSpacing > range.sumValue) {
|
|
// we don't have enough space to uncollapse both items
|
|
int32 availableSpace = range.sumValue - (int32)fSpacing;
|
|
if (previousSize < nextSize && availableSpace >= range.nextMin
|
|
&& availableSpace <= range.nextMax
|
|
&& previousInfo->isCollapsible) {
|
|
previousCollapsed = true;
|
|
} else if (availableSpace >= range.previousMin
|
|
&& availableSpace <= range.previousMax
|
|
&& nextInfo->isCollapsible) {
|
|
nextCollapsed = true;
|
|
} else if (availableSpace >= range.nextMin
|
|
&& availableSpace <= range.nextMax
|
|
&& previousInfo->isCollapsible) {
|
|
previousCollapsed = true;
|
|
} else {
|
|
if (previousSize < nextSize && previousInfo->isCollapsible) {
|
|
previousCollapsed = true;
|
|
} else if (nextInfo->isCollapsible) {
|
|
nextCollapsed = true;
|
|
} else {
|
|
// Neither item is collapsible although there's not enough
|
|
// space: Give them both their minimum size.
|
|
previousSize = range.previousMin;
|
|
nextSize = range.nextMin;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// there is enough space for both items
|
|
// make sure the min constraints are satisfied
|
|
if (previousSize < range.previousMin) {
|
|
previousSize = range.previousMin;
|
|
nextSize = range.sumValue - previousSize - 2 * (int32)fSpacing;
|
|
} else if (nextSize < range.nextMin) {
|
|
nextSize = range.nextMin;
|
|
previousSize = range.sumValue - nextSize - 2 * (int32)fSpacing;
|
|
}
|
|
|
|
// if we can, also satisfy the max constraints
|
|
if (range.previousMax + range.nextMax + 2 * (int32)fSpacing
|
|
>= range.sumValue) {
|
|
if (previousSize > range.previousMax) {
|
|
previousSize = range.previousMax;
|
|
nextSize = range.sumValue - previousSize
|
|
- 2 * (int32)fSpacing;
|
|
} else if (nextSize > range.nextMax) {
|
|
nextSize = range.nextMax;
|
|
previousSize = range.sumValue - nextSize
|
|
- 2 * (int32)fSpacing;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// compute the size for one collapsed item; for none collapsed item we
|
|
// already have correct values
|
|
if (previousCollapsed || nextCollapsed) {
|
|
int32 availableSpace = range.sumValue - (int32)fSpacing;
|
|
if (previousCollapsed) {
|
|
previousSize = 0;
|
|
nextSize = availableSpace;
|
|
} else {
|
|
previousSize = availableSpace;
|
|
nextSize = 0;
|
|
}
|
|
}
|
|
|
|
int32 newValue = previousSize + (previousCollapsed ? 0 : (int32)fSpacing);
|
|
if (newValue == fDraggingCurrentValue) {
|
|
// nothing changed
|
|
return false;
|
|
}
|
|
|
|
// something changed: we need to recompute the layout
|
|
int32 baseOffset = -fDraggingCurrentValue;
|
|
// offset to the current splitter position
|
|
int32 splitterOffset = baseOffset + newValue;
|
|
int32 nextOffset = splitterOffset + (int32)fSplitterSize + (int32)fSpacing;
|
|
|
|
BRect splitterFrame(splitterInfo->layoutFrame);
|
|
if (fOrientation == B_HORIZONTAL) {
|
|
// horizontal layout
|
|
// previous item
|
|
float left = splitterFrame.left + baseOffset;
|
|
previousInfo->layoutFrame.Set(
|
|
left,
|
|
splitterFrame.top,
|
|
left + previousSize - 1,
|
|
splitterFrame.bottom);
|
|
|
|
// next item
|
|
left = splitterFrame.left + nextOffset;
|
|
nextInfo->layoutFrame.Set(
|
|
left,
|
|
splitterFrame.top,
|
|
left + nextSize - 1,
|
|
splitterFrame.bottom);
|
|
|
|
// splitter
|
|
splitterInfo->layoutFrame.left += splitterOffset;
|
|
splitterInfo->layoutFrame.right += splitterOffset;
|
|
} else {
|
|
// vertical layout
|
|
// previous item
|
|
float top = splitterFrame.top + baseOffset;
|
|
previousInfo->layoutFrame.Set(
|
|
splitterFrame.left,
|
|
top,
|
|
splitterFrame.right,
|
|
top + previousSize - 1);
|
|
|
|
// next item
|
|
top = splitterFrame.top + nextOffset;
|
|
nextInfo->layoutFrame.Set(
|
|
splitterFrame.left,
|
|
top,
|
|
splitterFrame.right,
|
|
top + nextSize - 1);
|
|
|
|
// splitter
|
|
splitterInfo->layoutFrame.top += splitterOffset;
|
|
splitterInfo->layoutFrame.bottom += splitterOffset;
|
|
}
|
|
|
|
previousInfo->isVisible = !previousCollapsed;
|
|
nextInfo->isVisible = !nextCollapsed;
|
|
|
|
bool heightForWidth = (fOrientation == B_HORIZONTAL && HasHeightForWidth());
|
|
|
|
// If the item visibility is to be changed, we need to update the splitter
|
|
// values now, since the visibility change will cause an invalidation.
|
|
if (previousVisible != previousInfo->isVisible
|
|
|| nextVisible != nextInfo->isVisible || heightForWidth) {
|
|
_UpdateSplitterWeights();
|
|
}
|
|
|
|
// If we have height for width items, we need to invalidate the previous
|
|
// and the next item. Actually we would only need to invalidate height for
|
|
// width items, but since non height for width items might be aligned with
|
|
// height for width items, we need to trigger a layout that creates a
|
|
// context that spans all aligned items.
|
|
// We invalidate already here, so that changing the items' size won't cause
|
|
// an immediate relayout.
|
|
if (heightForWidth) {
|
|
previousItem->InvalidateLayout();
|
|
nextItem->InvalidateLayout();
|
|
}
|
|
|
|
// do the layout
|
|
_LayoutItem(previousItem, previousInfo);
|
|
_LayoutItem(_SplitterItemAt(index), splitterInfo);
|
|
_LayoutItem(nextItem, nextInfo);
|
|
|
|
fDraggingCurrentValue = newValue;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BSplitLayout::ItemLayoutInfo*
|
|
BSplitLayout::_ItemLayoutInfo(BLayoutItem* item) const
|
|
{
|
|
return (ItemLayoutInfo*)item->LayoutData();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_UpdateSplitterWeights()
|
|
{
|
|
int32 count = CountItems();
|
|
for (int32 i = 0; i < count; i++) {
|
|
float weight;
|
|
if (fOrientation == B_HORIZONTAL)
|
|
weight = _ItemLayoutInfo(ItemAt(i))->layoutFrame.Width() + 1;
|
|
else
|
|
weight = _ItemLayoutInfo(ItemAt(i))->layoutFrame.Height() + 1;
|
|
|
|
SetItemWeight(i, weight, false);
|
|
}
|
|
|
|
// Just updating the splitter weights is fine in principle. The next
|
|
// LayoutItems() will use the correct values. But, if our orientation is
|
|
// vertical, the cached height for width info needs to be flushed, or the
|
|
// obsolete cached values will be used.
|
|
if (fOrientation == B_VERTICAL)
|
|
_InvalidateCachedHeightForWidth();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_ValidateMinMax()
|
|
{
|
|
if (fHorizontalLayouter != NULL)
|
|
return;
|
|
|
|
fLayoutValid = false;
|
|
|
|
fVisibleItems.MakeEmpty();
|
|
fHeightForWidthItems.MakeEmpty();
|
|
|
|
_InvalidateCachedHeightForWidth();
|
|
|
|
// filter the visible items
|
|
int32 itemCount = CountItems();
|
|
for (int32 i = 0; i < itemCount; i++) {
|
|
BLayoutItem* item = ItemAt(i);
|
|
if (item->IsVisible())
|
|
fVisibleItems.AddItem(item);
|
|
|
|
// Add "height for width" items even, if they aren't visible. Otherwise
|
|
// we may get our parent into trouble, since we could change from
|
|
// "height for width" to "not height for width".
|
|
if (item->HasHeightForWidth())
|
|
fHeightForWidthItems.AddItem(item);
|
|
}
|
|
itemCount = fVisibleItems.CountItems();
|
|
|
|
// create the layouters
|
|
Layouter* itemLayouter = new SimpleLayouter(itemCount, 0);
|
|
|
|
if (fOrientation == B_HORIZONTAL) {
|
|
fHorizontalLayouter = itemLayouter;
|
|
fVerticalLayouter = new OneElementLayouter();
|
|
} else {
|
|
fHorizontalLayouter = new OneElementLayouter();
|
|
fVerticalLayouter = itemLayouter;
|
|
}
|
|
|
|
// tell the layouters about our constraints
|
|
if (itemCount > 0) {
|
|
for (int32 i = 0; i < itemCount; i++) {
|
|
BLayoutItem* item = (BLayoutItem*)fVisibleItems.ItemAt(i);
|
|
BSize min = item->MinSize();
|
|
BSize max = item->MaxSize();
|
|
BSize preferred = item->PreferredSize();
|
|
|
|
fHorizontalLayouter->AddConstraints(i, 1, min.width, max.width,
|
|
preferred.width);
|
|
fVerticalLayouter->AddConstraints(i, 1, min.height, max.height,
|
|
preferred.height);
|
|
|
|
float weight = ItemWeight(item);
|
|
fHorizontalLayouter->SetWeight(i, weight);
|
|
fVerticalLayouter->SetWeight(i, weight);
|
|
}
|
|
}
|
|
|
|
fMin.width = fHorizontalLayouter->MinSize();
|
|
fMin.height = fVerticalLayouter->MinSize();
|
|
fMax.width = fHorizontalLayouter->MaxSize();
|
|
fMax.height = fVerticalLayouter->MaxSize();
|
|
fPreferred.width = fHorizontalLayouter->PreferredSize();
|
|
fPreferred.height = fVerticalLayouter->PreferredSize();
|
|
|
|
fHorizontalLayoutInfo = fHorizontalLayouter->CreateLayoutInfo();
|
|
if (fHeightForWidthItems.IsEmpty())
|
|
fVerticalLayoutInfo = fVerticalLayouter->CreateLayoutInfo();
|
|
|
|
ResetLayoutInvalidation();
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_InternalGetHeightForWidth(float width, bool realLayout,
|
|
float* minHeight, float* maxHeight, float* preferredHeight)
|
|
{
|
|
if ((realLayout && fHeightForWidthVerticalLayouterWidth != width)
|
|
|| (!realLayout && fCachedHeightForWidthWidth != width)) {
|
|
// The general strategy is to clone the vertical layouter, which only
|
|
// knows the general min/max constraints, do a horizontal layout for the
|
|
// given width, and add the children's height for width constraints to
|
|
// the cloned vertical layouter. If this method is invoked internally,
|
|
// we keep the cloned vertical layouter, for it will be used for doing
|
|
// the layout. Otherwise we just drop it after we've got the height for
|
|
// width info.
|
|
|
|
// clone the vertical layouter and get the horizontal layout info to be used
|
|
LayoutInfo* horizontalLayoutInfo = NULL;
|
|
Layouter* verticalLayouter = fVerticalLayouter->CloneLayouter();
|
|
if (realLayout) {
|
|
horizontalLayoutInfo = fHorizontalLayoutInfo;
|
|
delete fHeightForWidthVerticalLayouter;
|
|
fHeightForWidthVerticalLayouter = verticalLayouter;
|
|
delete fVerticalLayoutInfo;
|
|
fVerticalLayoutInfo = verticalLayouter->CreateLayoutInfo();
|
|
fHeightForWidthVerticalLayouterWidth = width;
|
|
} else {
|
|
if (fHeightForWidthHorizontalLayoutInfo == NULL) {
|
|
delete fHeightForWidthHorizontalLayoutInfo;
|
|
fHeightForWidthHorizontalLayoutInfo
|
|
= fHorizontalLayouter->CreateLayoutInfo();
|
|
}
|
|
horizontalLayoutInfo = fHeightForWidthHorizontalLayoutInfo;
|
|
}
|
|
|
|
// do the horizontal layout (already done when doing this for the real
|
|
// layout)
|
|
if (!realLayout)
|
|
fHorizontalLayouter->Layout(horizontalLayoutInfo, width);
|
|
|
|
// add the children's height for width constraints
|
|
int32 count = fHeightForWidthItems.CountItems();
|
|
for (int32 i = 0; i < count; i++) {
|
|
BLayoutItem* item = (BLayoutItem*)fHeightForWidthItems.ItemAt(i);
|
|
int32 index = fVisibleItems.IndexOf(item);
|
|
if (index >= 0) {
|
|
float itemMinHeight, itemMaxHeight, itemPreferredHeight;
|
|
item->GetHeightForWidth(
|
|
horizontalLayoutInfo->ElementSize(index),
|
|
&itemMinHeight, &itemMaxHeight, &itemPreferredHeight);
|
|
verticalLayouter->AddConstraints(index, 1, itemMinHeight,
|
|
itemMaxHeight, itemPreferredHeight);
|
|
}
|
|
}
|
|
|
|
// get the height for width info
|
|
fCachedHeightForWidthWidth = width;
|
|
fCachedMinHeightForWidth = verticalLayouter->MinSize();
|
|
fCachedMaxHeightForWidth = verticalLayouter->MaxSize();
|
|
fCachedPreferredHeightForWidth = verticalLayouter->PreferredSize();
|
|
}
|
|
|
|
if (minHeight)
|
|
*minHeight = fCachedMinHeightForWidth;
|
|
if (maxHeight)
|
|
*maxHeight = fCachedMaxHeightForWidth;
|
|
if (preferredHeight)
|
|
*preferredHeight = fCachedPreferredHeightForWidth;
|
|
}
|
|
|
|
|
|
float
|
|
BSplitLayout::_SplitterSpace() const
|
|
{
|
|
int32 splitters = fSplitterItems.CountItems();
|
|
float space = 0;
|
|
if (splitters > 0) {
|
|
space = (fVisibleItems.CountItems() + splitters - 1) * fSpacing
|
|
+ splitters * fSplitterSize;
|
|
}
|
|
|
|
return space;
|
|
}
|
|
|
|
|
|
BSize
|
|
BSplitLayout::_AddInsets(BSize size)
|
|
{
|
|
size.width = BLayoutUtils::AddDistances(size.width,
|
|
fLeftInset + fRightInset - 1);
|
|
size.height = BLayoutUtils::AddDistances(size.height,
|
|
fTopInset + fBottomInset - 1);
|
|
|
|
float spacing = _SplitterSpace();
|
|
if (fOrientation == B_HORIZONTAL)
|
|
size.width = BLayoutUtils::AddDistances(size.width, spacing - 1);
|
|
else
|
|
size.height = BLayoutUtils::AddDistances(size.height, spacing - 1);
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
void
|
|
BSplitLayout::_AddInsets(float* minHeight, float* maxHeight,
|
|
float* preferredHeight)
|
|
{
|
|
float insets = fTopInset + fBottomInset - 1;
|
|
if (fOrientation == B_VERTICAL)
|
|
insets += _SplitterSpace();
|
|
if (minHeight)
|
|
*minHeight = BLayoutUtils::AddDistances(*minHeight, insets);
|
|
if (maxHeight)
|
|
*maxHeight = BLayoutUtils::AddDistances(*maxHeight, insets);
|
|
if (preferredHeight)
|
|
*preferredHeight = BLayoutUtils::AddDistances(*preferredHeight, insets);
|
|
}
|
|
|
|
|
|
BSize
|
|
BSplitLayout::_SubtractInsets(BSize size)
|
|
{
|
|
size.width = BLayoutUtils::SubtractDistances(size.width,
|
|
fLeftInset + fRightInset - 1);
|
|
size.height = BLayoutUtils::SubtractDistances(size.height,
|
|
fTopInset + fBottomInset - 1);
|
|
|
|
float spacing = _SplitterSpace();
|
|
if (fOrientation == B_HORIZONTAL)
|
|
size.width = BLayoutUtils::SubtractDistances(size.width, spacing - 1);
|
|
else
|
|
size.height = BLayoutUtils::SubtractDistances(size.height, spacing - 1);
|
|
|
|
return size;
|
|
}
|
|
|