* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ViewPort.h>
#include <algorithm>
#include <AbstractLayout.h>
#include <ScrollBar.h>
#include "ViewLayoutItem.h"
namespace BPrivate {
class BViewPort::ViewPortLayout : public BAbstractLayout {
public:
ViewPortLayout(BViewPort* viewPort)
:
BAbstractLayout(),
fViewPort(viewPort),
fHasViewChild(false),
fIsCacheValid(false),
fMin(),
fMax(),
fPreferred()
{
}
BView* ChildView() const
{
if (!fHasViewChild)
return NULL;
if (BViewLayoutItem* item = dynamic_cast<BViewLayoutItem*>(ItemAt(0)))
return item->View();
return NULL;
}
void SetChildView(BView* view)
{
_UnsetChild();
if (view != NULL && AddView(0, view) != NULL)
fHasViewChild = true;
}
BLayoutItem* ChildItem() const
{
return ItemAt(0);
}
void SetChildItem(BLayoutItem* item)
{
_UnsetChild();
if (item != NULL)
AddItem(0, item);
}
virtual BSize BaseMinSize()
{
_ValidateMinMax();
return fMin;
}
virtual BSize BaseMaxSize()
{
_ValidateMinMax();
return fMax;
}
virtual BSize BasePreferredSize()
{
_ValidateMinMax();
return fPreferred;
}
virtual BAlignment BaseAlignment()
{
return BAbstractLayout::BaseAlignment();
}
virtual bool HasHeightForWidth()
{
_ValidateMinMax();
return false;
}
virtual void GetHeightForWidth(float width, float* min, float* max,
float* preferred)
{
if (!HasHeightForWidth())
return;
}
virtual void LayoutInvalidated(bool children)
{
fIsCacheValid = false;
}
virtual void DoLayout()
{
_ValidateMinMax();
BLayoutItem* child = ItemAt(0);
if (child == NULL)
return;
BSize viewSize = LayoutArea().Size();
BSize layoutSize = viewSize;
BSize childMin = child->MinSize();
BSize childMax = child->MaxSize();
layoutSize.width = std::min(layoutSize.width, childMax.width);
layoutSize.height = std::min(layoutSize.height, childMax.height);
layoutSize.width = std::max(layoutSize.width, childMin.width);
layoutSize.height = std::max(layoutSize.height, childMin.height);
child->AlignInFrame(BRect(BPoint(0, 0), layoutSize));
_UpdateScrollBar(fViewPort->ScrollBar(B_HORIZONTAL), viewSize.width,
layoutSize.width);
_UpdateScrollBar(fViewPort->ScrollBar(B_VERTICAL), viewSize.height,
layoutSize.height);
}
private:
void _UnsetChild()
{
if (CountItems() > 0) {
BLayoutItem* item = RemoveItem((int32)0);
if (fHasViewChild)
delete item;
fHasViewChild = false;
}
}
void _ValidateMinMax()
{
if (fIsCacheValid)
return;
if (BLayoutItem* child = ItemAt(0)) {
fMin = child->MinSize();
if (_IsHorizontallyScrollable())
fMin.width = -1;
if (_IsVerticallyScrollable())
fMin.height = -1;
fMax = child->MaxSize();
fPreferred = child->PreferredSize();
} else {
fMin.Set(-1, -1);
fMax.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
fPreferred.Set(20, 20);
}
fIsCacheValid = true;
}
bool _IsHorizontallyScrollable() const
{
return fViewPort->ScrollBar(B_HORIZONTAL) != NULL;
}
bool _IsVerticallyScrollable() const
{
return fViewPort->ScrollBar(B_VERTICAL) != NULL;
}
void _UpdateScrollBar(BScrollBar* scrollBar, float viewPortSize,
float dataSize)
{
if (scrollBar == NULL)
return;
if (viewPortSize < dataSize) {
scrollBar->SetRange(0, dataSize - viewPortSize);
scrollBar->SetProportion(viewPortSize / dataSize);
float smallStep;
scrollBar->GetSteps(&smallStep, NULL);
scrollBar->SetSteps(smallStep, viewPortSize);
} else {
scrollBar->SetRange(0, 0);
scrollBar->SetProportion(1);
}
}
private:
BViewPort* fViewPort;
bool fHasViewChild;
bool fIsCacheValid;
BSize fMin;
BSize fMax;
BSize fPreferred;
};
BViewPort::BViewPort(BView* child)
:
BView(NULL, 0),
fChild(NULL)
{
_Init();
SetChildView(child);
}
BViewPort::BViewPort(BLayoutItem* child)
:
BView(NULL, 0),
fChild(NULL)
{
_Init();
SetChildItem(child);
}
BViewPort::BViewPort(const char* name, BView* child)
:
BView(name, 0),
fChild(NULL)
{
_Init();
SetChildView(child);
}
BViewPort::BViewPort(const char* name, BLayoutItem* child)
:
BView(name, 0),
fChild(NULL)
{
_Init();
SetChildItem(child);
}
BViewPort::~BViewPort()
{
}
BView*
BViewPort::ChildView() const
{
return fLayout->ChildView();
}
void
BViewPort::SetChildView(BView* child)
{
fLayout->SetChildView(child);
InvalidateLayout();
}
BLayoutItem*
BViewPort::ChildItem() const
{
return fLayout->ChildItem();
}
void
BViewPort::SetChildItem(BLayoutItem* child)
{
fLayout->SetChildItem(child);
InvalidateLayout();
}
void
BViewPort::_Init()
{
fLayout = new ViewPortLayout(this);
SetLayout(fLayout);
}
}
using ::BPrivate::BViewPort;