1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SkStackViewLayout.h"
9
10SkStackViewLayout::SkStackViewLayout()
11{
12	fMargin.set(0, 0, 0, 0);
13	fSpacer	= 0;
14	fOrient	= kHorizontal_Orient;
15	fPack	= kStart_Pack;
16	fAlign	= kStart_Align;
17	fRound	= false;
18}
19
20void SkStackViewLayout::setOrient(Orient ori)
21{
22	SkASSERT((unsigned)ori < kOrientCount);
23	fOrient = SkToU8(ori);
24}
25
26void SkStackViewLayout::getMargin(SkRect* margin) const
27{
28	if (margin)
29		*margin = fMargin;
30}
31
32void SkStackViewLayout::setMargin(const SkRect& margin)
33{
34	fMargin = margin;
35}
36
37void SkStackViewLayout::setSpacer(SkScalar spacer)
38{
39	fSpacer = spacer;
40}
41
42void SkStackViewLayout::setPack(Pack pack)
43{
44	SkASSERT((unsigned)pack < kPackCount);
45	fPack = SkToU8(pack);
46}
47
48void SkStackViewLayout::setAlign(Align align)
49{
50	SkASSERT((unsigned)align < kAlignCount);
51	fAlign = SkToU8(align);
52}
53
54void SkStackViewLayout::setRound(bool r)
55{
56	fRound = SkToU8(r);
57}
58
59////////////////////////////////////////////////////////////////////////////////
60
61typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
62typedef SkScalar (SkView::*GetSizeProc)() const;
63typedef void (SkView::*SetLocProc)(SkScalar coord);
64typedef void (SkView::*SetSizeProc)(SkScalar coord);
65
66static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
67static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
68static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
69static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
70
71/*	Measure the main-dimension for all the children. If a child is marked flex in that direction
72	ignore its current value but increment the counter for flexChildren
73*/
74static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
75									   uint32_t flexMask, int* flexCount)
76{
77	SkView::B2FIter	iter(parent);
78	SkView*			child;
79	SkScalar		limit = 0;
80	int				n = 0, flex = 0;
81
82	while ((child = iter.next()) != NULL)
83	{
84		n += 1;
85		if (child->getFlags() & flexMask)
86			flex += 1;
87		else
88			limit += (child->*sizeProc)();
89	}
90	if (count)
91		*count = n;
92	if (flexCount)
93		*flexCount = flex;
94	return limit;
95}
96
97void SkStackViewLayout::onLayoutChildren(SkView* parent)
98{
99	static AlignProc gAlignProcs[] = {
100		left_align_proc,
101		center_align_proc,
102		right_align_proc,
103		fill_align_proc
104	};
105
106	SkScalar			startM, endM, crossStartM, crossLimit;
107	GetSizeProc			mainGetSizeP, crossGetSizeP;
108	SetLocProc			mainLocP, crossLocP;
109	SetSizeProc			mainSetSizeP, crossSetSizeP;
110	SkView::Flag_Mask	flexMask;
111
112	if (fOrient == kHorizontal_Orient)
113	{
114		startM		= fMargin.fLeft;
115		endM		= fMargin.fRight;
116		crossStartM	= fMargin.fTop;
117		crossLimit	= -fMargin.fTop - fMargin.fBottom;
118
119		mainGetSizeP	= &SkView::width;
120		crossGetSizeP	= &SkView::height;
121		mainLocP	= &SkView::setLocX;
122		crossLocP	= &SkView::setLocY;
123
124		mainSetSizeP  = &SkView::setWidth;
125		crossSetSizeP = &SkView::setHeight;
126
127		flexMask	= SkView::kFlexH_Mask;
128	}
129	else
130	{
131		startM		= fMargin.fTop;
132		endM		= fMargin.fBottom;
133		crossStartM	= fMargin.fLeft;
134		crossLimit	= -fMargin.fLeft - fMargin.fRight;
135
136		mainGetSizeP	= &SkView::height;
137		crossGetSizeP	= &SkView::width;
138		mainLocP	= &SkView::setLocY;
139		crossLocP	= &SkView::setLocX;
140
141		mainSetSizeP  = &SkView::setHeight;
142		crossSetSizeP = &SkView::setWidth;
143
144		flexMask	= SkView::kFlexV_Mask;
145	}
146	crossLimit += (parent->*crossGetSizeP)();
147	if (fAlign != kStretch_Align)
148		crossSetSizeP = NULL;
149
150	int			childCount, flexCount;
151	SkScalar	childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
152
153	if (childCount == 0)
154		return;
155
156	childLimit += (childCount - 1) * fSpacer;
157
158	SkScalar		parentLimit = (parent->*mainGetSizeP)() - startM - endM;
159	SkScalar		pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
160	SkScalar		flexAmount = 0;
161	SkView::B2FIter	iter(parent);
162	SkView*			child;
163
164	if (flexCount > 0 && parentLimit > childLimit)
165		flexAmount = (parentLimit - childLimit) / flexCount;
166
167	while ((child = iter.next()) != NULL)
168	{
169		if (fRound)
170			pos = SkIntToScalar(SkScalarRound(pos));
171		(child->*mainLocP)(pos);
172		SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
173		if (fRound)
174			crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
175		(child->*crossLocP)(crossLoc);
176
177		if (crossSetSizeP)
178			(child->*crossSetSizeP)(crossLimit);
179		if (child->getFlags() & flexMask)
180			(child->*mainSetSizeP)(flexAmount);
181		pos += (child->*mainGetSizeP)() + fSpacer;
182	}
183}
184
185//////////////////////////////////////////////////////////////////////////////////////
186
187#ifdef SK_DEBUG
188	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
189	{
190		const char* value = dom.findAttr(node, attr);
191		if (value)
192			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
193	}
194#else
195	#define assert_no_attr(dom, node, attr)
196#endif
197
198void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
199{
200	int			index;
201	SkScalar	value[4];
202
203	if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
204		this->setOrient((Orient)index);
205	else {
206		assert_no_attr(dom, node, "orient");
207        }
208
209	if (dom.findScalars(node, "margin", value, 4))
210	{
211		SkRect	margin;
212		margin.set(value[0], value[1], value[2], value[3]);
213		this->setMargin(margin);
214	}
215	else {
216		assert_no_attr(dom, node, "margin");
217        }
218
219	if (dom.findScalar(node, "spacer", value))
220		this->setSpacer(value[0]);
221	else {
222		assert_no_attr(dom, node, "spacer");
223        }
224
225	if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
226		this->setPack((Pack)index);
227	else {
228		assert_no_attr(dom, node, "pack");
229        }
230
231	if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
232		this->setAlign((Align)index);
233	else {
234		assert_no_attr(dom, node, "align");
235        }
236}
237
238///////////////////////////////////////////////////////////////////////////////////////////
239
240SkFillViewLayout::SkFillViewLayout()
241{
242	fMargin.setEmpty();
243}
244
245void SkFillViewLayout::getMargin(SkRect* r) const
246{
247	if (r)
248		*r = fMargin;
249}
250
251void SkFillViewLayout::setMargin(const SkRect& margin)
252{
253	fMargin = margin;
254}
255
256void SkFillViewLayout::onLayoutChildren(SkView* parent)
257{
258	SkView::B2FIter	iter(parent);
259	SkView*			child;
260
261	while ((child = iter.next()) != NULL)
262	{
263		child->setLoc(fMargin.fLeft, fMargin.fTop);
264		child->setSize(	parent->width() - fMargin.fRight - fMargin.fLeft,
265						parent->height() - fMargin.fBottom - fMargin.fTop);
266	}
267}
268
269void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
270{
271	this->INHERITED::onInflate(dom, node);
272	(void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
273}
274
275