1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <ViewPort.h>
8
9#include <algorithm>
10
11#include <AbstractLayout.h>
12#include <ScrollBar.h>
13
14#include "ViewLayoutItem.h"
15
16
17namespace BPrivate {
18
19
20// #pragma mark - ViewPortLayout
21
22
23class BViewPort::ViewPortLayout : public BAbstractLayout {
24public:
25	ViewPortLayout(BViewPort* viewPort)
26		:
27		BAbstractLayout(),
28		fViewPort(viewPort),
29		fHasViewChild(false),
30		fIsCacheValid(false),
31		fMin(),
32		fMax(),
33		fPreferred()
34	{
35	}
36
37	BView* ChildView() const
38	{
39		if (!fHasViewChild)
40			return NULL;
41		if (BViewLayoutItem* item = dynamic_cast<BViewLayoutItem*>(ItemAt(0)))
42			return item->View();
43		return NULL;
44	}
45
46	void SetChildView(BView* view)
47	{
48		_UnsetChild();
49
50		if (view != NULL && AddView(0, view) != NULL)
51			fHasViewChild = true;
52	}
53
54	BLayoutItem* ChildItem() const
55	{
56		return ItemAt(0);
57	}
58
59	void SetChildItem(BLayoutItem* item)
60	{
61		_UnsetChild();
62
63		if (item != NULL)
64			AddItem(0, item);
65	}
66
67	virtual BSize BaseMinSize()
68	{
69		_ValidateMinMax();
70		return fMin;
71	}
72
73	virtual BSize BaseMaxSize()
74	{
75		_ValidateMinMax();
76		return fMax;
77	}
78
79	virtual BSize BasePreferredSize()
80	{
81		_ValidateMinMax();
82		return fPreferred;
83	}
84
85	virtual BAlignment BaseAlignment()
86	{
87		return BAbstractLayout::BaseAlignment();
88	}
89
90	virtual bool HasHeightForWidth()
91	{
92		_ValidateMinMax();
93		return false;
94		// TODO: Support height-for-width!
95	}
96
97	virtual void GetHeightForWidth(float width, float* min, float* max,
98		float* preferred)
99	{
100		if (!HasHeightForWidth())
101			return;
102
103		// TODO: Support height-for-width!
104	}
105
106	virtual void LayoutInvalidated(bool children)
107	{
108		fIsCacheValid = false;
109	}
110
111	virtual void DoLayout()
112	{
113		_ValidateMinMax();
114
115		BLayoutItem* child = ItemAt(0);
116		if (child == NULL)
117			return;
118
119		// Determine the layout area: LayoutArea() will only give us the size
120		// of the view port's frame.
121		BSize viewSize = LayoutArea().Size();
122		BSize layoutSize = viewSize;
123
124		BSize childMin = child->MinSize();
125		BSize childMax = child->MaxSize();
126
127		// apply the maximum constraints
128		layoutSize.width = std::min(layoutSize.width, childMax.width);
129		layoutSize.height = std::min(layoutSize.height, childMax.height);
130
131		// apply the minimum constraints
132		layoutSize.width = std::max(layoutSize.width, childMin.width);
133		layoutSize.height = std::max(layoutSize.height, childMin.height);
134
135		// TODO: Support height-for-width!
136
137		child->AlignInFrame(BRect(BPoint(0, 0), layoutSize));
138
139		_UpdateScrollBar(fViewPort->ScrollBar(B_HORIZONTAL), viewSize.width,
140			layoutSize.width);
141		_UpdateScrollBar(fViewPort->ScrollBar(B_VERTICAL), viewSize.height,
142			layoutSize.height);
143	}
144
145private:
146	void _UnsetChild()
147	{
148		if (CountItems() > 0) {
149			BLayoutItem* item = RemoveItem((int32)0);
150			if (fHasViewChild)
151				delete item;
152			fHasViewChild = false;
153		}
154	}
155
156	void _ValidateMinMax()
157	{
158		if (fIsCacheValid)
159			return;
160
161		if (BLayoutItem* child = ItemAt(0)) {
162			fMin = child->MinSize();
163			if (_IsHorizontallyScrollable())
164				fMin.width = -1;
165			if (_IsVerticallyScrollable())
166				fMin.height = -1;
167			fMax = child->MaxSize();
168			fPreferred = child->PreferredSize();
169			// TODO: Support height-for-width!
170		} else {
171			fMin.Set(-1, -1);
172			fMax.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
173			fPreferred.Set(20, 20);
174		}
175
176		fIsCacheValid = true;
177	}
178
179	bool _IsHorizontallyScrollable() const
180	{
181		return fViewPort->ScrollBar(B_HORIZONTAL) != NULL;
182	}
183
184	bool _IsVerticallyScrollable() const
185	{
186		return fViewPort->ScrollBar(B_VERTICAL) != NULL;
187	}
188
189	void _UpdateScrollBar(BScrollBar* scrollBar, float viewPortSize,
190		float dataSize)
191	{
192		if (scrollBar == NULL)
193			return;
194
195		if (viewPortSize < dataSize) {
196			scrollBar->SetRange(0, dataSize - viewPortSize);
197			scrollBar->SetProportion(viewPortSize / dataSize);
198			float smallStep;
199			scrollBar->GetSteps(&smallStep, NULL);
200			scrollBar->SetSteps(smallStep, viewPortSize);
201		} else {
202			scrollBar->SetRange(0, 0);
203			scrollBar->SetProportion(1);
204		}
205	}
206
207private:
208	BViewPort*	fViewPort;
209	bool		fHasViewChild;
210	bool		fIsCacheValid;
211	BSize		fMin;
212	BSize		fMax;
213	BSize		fPreferred;
214};
215
216
217// #pragma mark - BViewPort
218
219
220BViewPort::BViewPort(BView* child)
221	:
222	BView(NULL, 0),
223	fChild(NULL)
224{
225	_Init();
226	SetChildView(child);
227}
228
229
230BViewPort::BViewPort(BLayoutItem* child)
231	:
232	BView(NULL, 0),
233	fChild(NULL)
234{
235	_Init();
236	SetChildItem(child);
237}
238
239
240BViewPort::BViewPort(const char* name, BView* child)
241	:
242	BView(name, 0),
243	fChild(NULL)
244{
245	_Init();
246	SetChildView(child);
247}
248
249
250BViewPort::BViewPort(const char* name, BLayoutItem* child)
251	:
252	BView(name, 0),
253	fChild(NULL)
254{
255	_Init();
256	SetChildItem(child);
257}
258
259
260BViewPort::~BViewPort()
261{
262}
263
264
265BView*
266BViewPort::ChildView() const
267{
268	return fLayout->ChildView();
269}
270
271
272void
273BViewPort::SetChildView(BView* child)
274{
275	fLayout->SetChildView(child);
276	InvalidateLayout();
277}
278
279
280BLayoutItem*
281BViewPort::ChildItem() const
282{
283	return fLayout->ChildItem();
284}
285
286
287void
288BViewPort::SetChildItem(BLayoutItem* child)
289{
290	fLayout->SetChildItem(child);
291	InvalidateLayout();
292}
293
294
295void
296BViewPort::_Init()
297{
298	fLayout = new ViewPortLayout(this);
299	SetLayout(fLayout);
300}
301
302
303}	// namespace BPrivate
304
305
306using ::BPrivate::BViewPort;
307