188f12ad8SIngo Weinhold/*
288f12ad8SIngo Weinhold * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
388f12ad8SIngo Weinhold * Distributed under the terms of the MIT License.
488f12ad8SIngo Weinhold */
588f12ad8SIngo Weinhold
688f12ad8SIngo Weinhold
788f12ad8SIngo Weinhold#include <ViewPort.h>
888f12ad8SIngo Weinhold
988f12ad8SIngo Weinhold#include <algorithm>
1088f12ad8SIngo Weinhold
1188f12ad8SIngo Weinhold#include <AbstractLayout.h>
1288f12ad8SIngo Weinhold#include <ScrollBar.h>
1388f12ad8SIngo Weinhold
1488f12ad8SIngo Weinhold#include "ViewLayoutItem.h"
1588f12ad8SIngo Weinhold
1688f12ad8SIngo Weinhold
1788f12ad8SIngo Weinholdnamespace BPrivate {
1888f12ad8SIngo Weinhold
1988f12ad8SIngo Weinhold
2088f12ad8SIngo Weinhold// #pragma mark - ViewPortLayout
2188f12ad8SIngo Weinhold
2288f12ad8SIngo Weinhold
2388f12ad8SIngo Weinholdclass BViewPort::ViewPortLayout : public BAbstractLayout {
2488f12ad8SIngo Weinholdpublic:
2588f12ad8SIngo Weinhold	ViewPortLayout(BViewPort* viewPort)
2688f12ad8SIngo Weinhold		:
2788f12ad8SIngo Weinhold		BAbstractLayout(),
2888f12ad8SIngo Weinhold		fViewPort(viewPort),
2988f12ad8SIngo Weinhold		fHasViewChild(false),
3088f12ad8SIngo Weinhold		fIsCacheValid(false),
3188f12ad8SIngo Weinhold		fMin(),
3288f12ad8SIngo Weinhold		fMax(),
3388f12ad8SIngo Weinhold		fPreferred()
3488f12ad8SIngo Weinhold	{
3588f12ad8SIngo Weinhold	}
3688f12ad8SIngo Weinhold
3788f12ad8SIngo Weinhold	BView* ChildView() const
3888f12ad8SIngo Weinhold	{
3988f12ad8SIngo Weinhold		if (!fHasViewChild)
4088f12ad8SIngo Weinhold			return NULL;
4188f12ad8SIngo Weinhold		if (BViewLayoutItem* item = dynamic_cast<BViewLayoutItem*>(ItemAt(0)))
4288f12ad8SIngo Weinhold			return item->View();
4388f12ad8SIngo Weinhold		return NULL;
4488f12ad8SIngo Weinhold	}
4588f12ad8SIngo Weinhold
4688f12ad8SIngo Weinhold	void SetChildView(BView* view)
4788f12ad8SIngo Weinhold	{
4888f12ad8SIngo Weinhold		_UnsetChild();
4988f12ad8SIngo Weinhold
5088f12ad8SIngo Weinhold		if (view != NULL && AddView(0, view) != NULL)
5188f12ad8SIngo Weinhold			fHasViewChild = true;
5288f12ad8SIngo Weinhold	}
5388f12ad8SIngo Weinhold
5488f12ad8SIngo Weinhold	BLayoutItem* ChildItem() const
5588f12ad8SIngo Weinhold	{
5688f12ad8SIngo Weinhold		return ItemAt(0);
5788f12ad8SIngo Weinhold	}
5888f12ad8SIngo Weinhold
5988f12ad8SIngo Weinhold	void SetChildItem(BLayoutItem* item)
6088f12ad8SIngo Weinhold	{
6188f12ad8SIngo Weinhold		_UnsetChild();
6288f12ad8SIngo Weinhold
6388f12ad8SIngo Weinhold		if (item != NULL)
6488f12ad8SIngo Weinhold			AddItem(0, item);
6588f12ad8SIngo Weinhold	}
6688f12ad8SIngo Weinhold
6788f12ad8SIngo Weinhold	virtual BSize BaseMinSize()
6888f12ad8SIngo Weinhold	{
6988f12ad8SIngo Weinhold		_ValidateMinMax();
7088f12ad8SIngo Weinhold		return fMin;
7188f12ad8SIngo Weinhold	}
7288f12ad8SIngo Weinhold
7388f12ad8SIngo Weinhold	virtual BSize BaseMaxSize()
7488f12ad8SIngo Weinhold	{
7588f12ad8SIngo Weinhold		_ValidateMinMax();
7688f12ad8SIngo Weinhold		return fMax;
7788f12ad8SIngo Weinhold	}
7888f12ad8SIngo Weinhold
7988f12ad8SIngo Weinhold	virtual BSize BasePreferredSize()
8088f12ad8SIngo Weinhold	{
8188f12ad8SIngo Weinhold		_ValidateMinMax();
8288f12ad8SIngo Weinhold		return fPreferred;
8388f12ad8SIngo Weinhold	}
8488f12ad8SIngo Weinhold
8588f12ad8SIngo Weinhold	virtual BAlignment BaseAlignment()
8688f12ad8SIngo Weinhold	{
8788f12ad8SIngo Weinhold		return BAbstractLayout::BaseAlignment();
8888f12ad8SIngo Weinhold	}
8988f12ad8SIngo Weinhold
9088f12ad8SIngo Weinhold	virtual bool HasHeightForWidth()
9188f12ad8SIngo Weinhold	{
9288f12ad8SIngo Weinhold		_ValidateMinMax();
9388f12ad8SIngo Weinhold		return false;
9488f12ad8SIngo Weinhold		// TODO: Support height-for-width!
9588f12ad8SIngo Weinhold	}
9688f12ad8SIngo Weinhold
9788f12ad8SIngo Weinhold	virtual void GetHeightForWidth(float width, float* min, float* max,
9888f12ad8SIngo Weinhold		float* preferred)
9988f12ad8SIngo Weinhold	{
10088f12ad8SIngo Weinhold		if (!HasHeightForWidth())
10188f12ad8SIngo Weinhold			return;
10288f12ad8SIngo Weinhold
10388f12ad8SIngo Weinhold		// TODO: Support height-for-width!
10488f12ad8SIngo Weinhold	}
10588f12ad8SIngo Weinhold
10688f12ad8SIngo Weinhold	virtual void LayoutInvalidated(bool children)
10788f12ad8SIngo Weinhold	{
10888f12ad8SIngo Weinhold		fIsCacheValid = false;
10988f12ad8SIngo Weinhold	}
11088f12ad8SIngo Weinhold
11188f12ad8SIngo Weinhold	virtual void DoLayout()
11288f12ad8SIngo Weinhold	{
11388f12ad8SIngo Weinhold		_ValidateMinMax();
11488f12ad8SIngo Weinhold
11588f12ad8SIngo Weinhold		BLayoutItem* child = ItemAt(0);
11688f12ad8SIngo Weinhold		if (child == NULL)
11788f12ad8SIngo Weinhold			return;
11888f12ad8SIngo Weinhold
11988f12ad8SIngo Weinhold		// Determine the layout area: LayoutArea() will only give us the size
12088f12ad8SIngo Weinhold		// of the view port's frame.
12188f12ad8SIngo Weinhold		BSize viewSize = LayoutArea().Size();
12288f12ad8SIngo Weinhold		BSize layoutSize = viewSize;
12388f12ad8SIngo Weinhold
12488f12ad8SIngo Weinhold		BSize childMin = child->MinSize();
12588f12ad8SIngo Weinhold		BSize childMax = child->MaxSize();
12688f12ad8SIngo Weinhold
12788f12ad8SIngo Weinhold		// apply the maximum constraints
12888f12ad8SIngo Weinhold		layoutSize.width = std::min(layoutSize.width, childMax.width);
12988f12ad8SIngo Weinhold		layoutSize.height = std::min(layoutSize.height, childMax.height);
13088f12ad8SIngo Weinhold
13188f12ad8SIngo Weinhold		// apply the minimum constraints
13288f12ad8SIngo Weinhold		layoutSize.width = std::max(layoutSize.width, childMin.width);
13388f12ad8SIngo Weinhold		layoutSize.height = std::max(layoutSize.height, childMin.height);
13488f12ad8SIngo Weinhold
13588f12ad8SIngo Weinhold		// TODO: Support height-for-width!
13688f12ad8SIngo Weinhold
13788f12ad8SIngo Weinhold		child->AlignInFrame(BRect(BPoint(0, 0), layoutSize));
13888f12ad8SIngo Weinhold
13988f12ad8SIngo Weinhold		_UpdateScrollBar(fViewPort->ScrollBar(B_HORIZONTAL), viewSize.width,
14088f12ad8SIngo Weinhold			layoutSize.width);
14188f12ad8SIngo Weinhold		_UpdateScrollBar(fViewPort->ScrollBar(B_VERTICAL), viewSize.height,
14288f12ad8SIngo Weinhold			layoutSize.height);
14388f12ad8SIngo Weinhold	}
14488f12ad8SIngo Weinhold
14588f12ad8SIngo Weinholdprivate:
14688f12ad8SIngo Weinhold	void _UnsetChild()
14788f12ad8SIngo Weinhold	{
14888f12ad8SIngo Weinhold		if (CountItems() > 0) {
14988f12ad8SIngo Weinhold			BLayoutItem* item = RemoveItem((int32)0);
15088f12ad8SIngo Weinhold			if (fHasViewChild)
15188f12ad8SIngo Weinhold				delete item;
15288f12ad8SIngo Weinhold			fHasViewChild = false;
15388f12ad8SIngo Weinhold		}
15488f12ad8SIngo Weinhold	}
15588f12ad8SIngo Weinhold
15688f12ad8SIngo Weinhold	void _ValidateMinMax()
15788f12ad8SIngo Weinhold	{
15888f12ad8SIngo Weinhold		if (fIsCacheValid)
15988f12ad8SIngo Weinhold			return;
16088f12ad8SIngo Weinhold
16188f12ad8SIngo Weinhold		if (BLayoutItem* child = ItemAt(0)) {
16288f12ad8SIngo Weinhold			fMin = child->MinSize();
16388f12ad8SIngo Weinhold			if (_IsHorizontallyScrollable())
16488f12ad8SIngo Weinhold				fMin.width = -1;
16588f12ad8SIngo Weinhold			if (_IsVerticallyScrollable())
16688f12ad8SIngo Weinhold				fMin.height = -1;
16788f12ad8SIngo Weinhold			fMax = child->MaxSize();
16888f12ad8SIngo Weinhold			fPreferred = child->PreferredSize();
16988f12ad8SIngo Weinhold			// TODO: Support height-for-width!
17088f12ad8SIngo Weinhold		} else {
17188f12ad8SIngo Weinhold			fMin.Set(-1, -1);
17288f12ad8SIngo Weinhold			fMax.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
17388f12ad8SIngo Weinhold			fPreferred.Set(20, 20);
17488f12ad8SIngo Weinhold		}
17588f12ad8SIngo Weinhold
17688f12ad8SIngo Weinhold		fIsCacheValid = true;
17788f12ad8SIngo Weinhold	}
17888f12ad8SIngo Weinhold
17988f12ad8SIngo Weinhold	bool _IsHorizontallyScrollable() const
18088f12ad8SIngo Weinhold	{
18188f12ad8SIngo Weinhold		return fViewPort->ScrollBar(B_HORIZONTAL) != NULL;
18288f12ad8SIngo Weinhold	}
18388f12ad8SIngo Weinhold
18488f12ad8SIngo Weinhold	bool _IsVerticallyScrollable() const
18588f12ad8SIngo Weinhold	{
18688f12ad8SIngo Weinhold		return fViewPort->ScrollBar(B_VERTICAL) != NULL;
18788f12ad8SIngo Weinhold	}
18888f12ad8SIngo Weinhold
18988f12ad8SIngo Weinhold	void _UpdateScrollBar(BScrollBar* scrollBar, float viewPortSize,
19088f12ad8SIngo Weinhold		float dataSize)
19188f12ad8SIngo Weinhold	{
19288f12ad8SIngo Weinhold		if (scrollBar == NULL)
19388f12ad8SIngo Weinhold			return;
19488f12ad8SIngo Weinhold
19588f12ad8SIngo Weinhold		if (viewPortSize < dataSize) {
19688f12ad8SIngo Weinhold			scrollBar->SetRange(0, dataSize - viewPortSize);
19788f12ad8SIngo Weinhold			scrollBar->SetProportion(viewPortSize / dataSize);
19888f12ad8SIngo Weinhold			float smallStep;
19988f12ad8SIngo Weinhold			scrollBar->GetSteps(&smallStep, NULL);
20088f12ad8SIngo Weinhold			scrollBar->SetSteps(smallStep, viewPortSize);
20188f12ad8SIngo Weinhold		} else {
20288f12ad8SIngo Weinhold			scrollBar->SetRange(0, 0);
20388f12ad8SIngo Weinhold			scrollBar->SetProportion(1);
20488f12ad8SIngo Weinhold		}
20588f12ad8SIngo Weinhold	}
20688f12ad8SIngo Weinhold
20788f12ad8SIngo Weinholdprivate:
20888f12ad8SIngo Weinhold	BViewPort*	fViewPort;
20988f12ad8SIngo Weinhold	bool		fHasViewChild;
21088f12ad8SIngo Weinhold	bool		fIsCacheValid;
21188f12ad8SIngo Weinhold	BSize		fMin;
21288f12ad8SIngo Weinhold	BSize		fMax;
21388f12ad8SIngo Weinhold	BSize		fPreferred;
21488f12ad8SIngo Weinhold};
21588f12ad8SIngo Weinhold
21688f12ad8SIngo Weinhold
21788f12ad8SIngo Weinhold// #pragma mark - BViewPort
21888f12ad8SIngo Weinhold
21988f12ad8SIngo Weinhold
22088f12ad8SIngo WeinholdBViewPort::BViewPort(BView* child)
22188f12ad8SIngo Weinhold	:
22288f12ad8SIngo Weinhold	BView(NULL, 0),
22388f12ad8SIngo Weinhold	fChild(NULL)
22488f12ad8SIngo Weinhold{
22588f12ad8SIngo Weinhold	_Init();
22688f12ad8SIngo Weinhold	SetChildView(child);
22788f12ad8SIngo Weinhold}
22888f12ad8SIngo Weinhold
22988f12ad8SIngo Weinhold
23088f12ad8SIngo WeinholdBViewPort::BViewPort(BLayoutItem* child)
23188f12ad8SIngo Weinhold	:
23288f12ad8SIngo Weinhold	BView(NULL, 0),
23388f12ad8SIngo Weinhold	fChild(NULL)
23488f12ad8SIngo Weinhold{
23588f12ad8SIngo Weinhold	_Init();
23688f12ad8SIngo Weinhold	SetChildItem(child);
23788f12ad8SIngo Weinhold}
23888f12ad8SIngo Weinhold
23988f12ad8SIngo Weinhold
24088f12ad8SIngo WeinholdBViewPort::BViewPort(const char* name, BView* child)
24188f12ad8SIngo Weinhold	:
24288f12ad8SIngo Weinhold	BView(name, 0),
24388f12ad8SIngo Weinhold	fChild(NULL)
24488f12ad8SIngo Weinhold{
24588f12ad8SIngo Weinhold	_Init();
24688f12ad8SIngo Weinhold	SetChildView(child);
24788f12ad8SIngo Weinhold}
24888f12ad8SIngo Weinhold
24988f12ad8SIngo Weinhold
25088f12ad8SIngo WeinholdBViewPort::BViewPort(const char* name, BLayoutItem* child)
25188f12ad8SIngo Weinhold	:
25288f12ad8SIngo Weinhold	BView(name, 0),
25388f12ad8SIngo Weinhold	fChild(NULL)
25488f12ad8SIngo Weinhold{
25588f12ad8SIngo Weinhold	_Init();
25688f12ad8SIngo Weinhold	SetChildItem(child);
25788f12ad8SIngo Weinhold}
25888f12ad8SIngo Weinhold
25988f12ad8SIngo Weinhold
26088f12ad8SIngo WeinholdBViewPort::~BViewPort()
26188f12ad8SIngo Weinhold{
26288f12ad8SIngo Weinhold}
26388f12ad8SIngo Weinhold
26488f12ad8SIngo Weinhold
26588f12ad8SIngo WeinholdBView*
26688f12ad8SIngo WeinholdBViewPort::ChildView() const
26788f12ad8SIngo Weinhold{
26888f12ad8SIngo Weinhold	return fLayout->ChildView();
26988f12ad8SIngo Weinhold}
27088f12ad8SIngo Weinhold
27188f12ad8SIngo Weinhold
27288f12ad8SIngo Weinholdvoid
27388f12ad8SIngo WeinholdBViewPort::SetChildView(BView* child)
27488f12ad8SIngo Weinhold{
27588f12ad8SIngo Weinhold	fLayout->SetChildView(child);
27688f12ad8SIngo Weinhold	InvalidateLayout();
27788f12ad8SIngo Weinhold}
27888f12ad8SIngo Weinhold
27988f12ad8SIngo Weinhold
28088f12ad8SIngo WeinholdBLayoutItem*
28188f12ad8SIngo WeinholdBViewPort::ChildItem() const
28288f12ad8SIngo Weinhold{
28388f12ad8SIngo Weinhold	return fLayout->ChildItem();
28488f12ad8SIngo Weinhold}
28588f12ad8SIngo Weinhold
28688f12ad8SIngo Weinhold
28788f12ad8SIngo Weinholdvoid
28888f12ad8SIngo WeinholdBViewPort::SetChildItem(BLayoutItem* child)
28988f12ad8SIngo Weinhold{
29088f12ad8SIngo Weinhold	fLayout->SetChildItem(child);
29188f12ad8SIngo Weinhold	InvalidateLayout();
29288f12ad8SIngo Weinhold}
29388f12ad8SIngo Weinhold
29488f12ad8SIngo Weinhold
29588f12ad8SIngo Weinholdvoid
29688f12ad8SIngo WeinholdBViewPort::_Init()
29788f12ad8SIngo Weinhold{
29888f12ad8SIngo Weinhold	fLayout = new ViewPortLayout(this);
29988f12ad8SIngo Weinhold	SetLayout(fLayout);
30088f12ad8SIngo Weinhold}
30188f12ad8SIngo Weinhold
30288f12ad8SIngo Weinhold
30388f12ad8SIngo Weinhold}	// namespace BPrivate
30488f12ad8SIngo Weinhold
30588f12ad8SIngo Weinhold
30688f12ad8SIngo Weinholdusing ::BPrivate::BViewPort;
307