blob: 9a3a352853306d3358f43f787f210089e1238bf8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkStackViewLayout.h"
9
10SkStackViewLayout::SkStackViewLayout()
11{
rmistry@google.comd6176b02012-08-23 18:14:13 +000012 fMargin.set(0, 0, 0, 0);
13 fSpacer = 0;
14 fOrient = kHorizontal_Orient;
15 fPack = kStart_Pack;
16 fAlign = kStart_Align;
17 fRound = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +000018}
19
20void SkStackViewLayout::setOrient(Orient ori)
21{
rmistry@google.comd6176b02012-08-23 18:14:13 +000022 SkASSERT((unsigned)ori < kOrientCount);
23 fOrient = SkToU8(ori);
reed@android.com8a1c16f2008-12-17 15:59:43 +000024}
25
26void SkStackViewLayout::getMargin(SkRect* margin) const
27{
rmistry@google.comd6176b02012-08-23 18:14:13 +000028 if (margin)
29 *margin = fMargin;
reed@android.com8a1c16f2008-12-17 15:59:43 +000030}
31
32void SkStackViewLayout::setMargin(const SkRect& margin)
33{
rmistry@google.comd6176b02012-08-23 18:14:13 +000034 fMargin = margin;
reed@android.com8a1c16f2008-12-17 15:59:43 +000035}
36
37void SkStackViewLayout::setSpacer(SkScalar spacer)
38{
rmistry@google.comd6176b02012-08-23 18:14:13 +000039 fSpacer = spacer;
reed@android.com8a1c16f2008-12-17 15:59:43 +000040}
41
42void SkStackViewLayout::setPack(Pack pack)
43{
rmistry@google.comd6176b02012-08-23 18:14:13 +000044 SkASSERT((unsigned)pack < kPackCount);
45 fPack = SkToU8(pack);
reed@android.com8a1c16f2008-12-17 15:59:43 +000046}
47
48void SkStackViewLayout::setAlign(Align align)
49{
rmistry@google.comd6176b02012-08-23 18:14:13 +000050 SkASSERT((unsigned)align < kAlignCount);
51 fAlign = SkToU8(align);
reed@android.com8a1c16f2008-12-17 15:59:43 +000052}
53
54void SkStackViewLayout::setRound(bool r)
55{
rmistry@google.comd6176b02012-08-23 18:14:13 +000056 fRound = SkToU8(r);
reed@android.com8a1c16f2008-12-17 15:59:43 +000057}
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
rmistry@google.comd6176b02012-08-23 18:14:13 +000071/* 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
reed@android.com8a1c16f2008-12-17 15:59:43 +000073*/
74static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
rmistry@google.comd6176b02012-08-23 18:14:13 +000075 uint32_t flexMask, int* flexCount)
reed@android.com8a1c16f2008-12-17 15:59:43 +000076{
rmistry@google.comd6176b02012-08-23 18:14:13 +000077 SkView::B2FIter iter(parent);
78 SkView* child;
79 SkScalar limit = 0;
80 int n = 0, flex = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
rmistry@google.comd6176b02012-08-23 18:14:13 +000082 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;
reed@android.com8a1c16f2008-12-17 15:59:43 +000095}
96
97void SkStackViewLayout::onLayoutChildren(SkView* parent)
98{
rmistry@google.comd6176b02012-08-23 18:14:13 +000099 static AlignProc gAlignProcs[] = {
100 left_align_proc,
101 center_align_proc,
102 right_align_proc,
103 fill_align_proc
104 };
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105
rmistry@google.comd6176b02012-08-23 18:14:13 +0000106 SkScalar startM, endM, crossStartM, crossLimit;
107 GetSizeProc mainGetSizeP, crossGetSizeP;
108 SetLocProc mainLocP, crossLocP;
109 SetSizeProc mainSetSizeP, crossSetSizeP;
110 SkView::Flag_Mask flexMask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111
rmistry@google.comd6176b02012-08-23 18:14:13 +0000112 if (fOrient == kHorizontal_Orient)
113 {
114 startM = fMargin.fLeft;
115 endM = fMargin.fRight;
116 crossStartM = fMargin.fTop;
117 crossLimit = -fMargin.fTop - fMargin.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118
rmistry@google.comd6176b02012-08-23 18:14:13 +0000119 mainGetSizeP = &SkView::width;
120 crossGetSizeP = &SkView::height;
121 mainLocP = &SkView::setLocX;
122 crossLocP = &SkView::setLocY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
rmistry@google.comd6176b02012-08-23 18:14:13 +0000124 mainSetSizeP = &SkView::setWidth;
125 crossSetSizeP = &SkView::setHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127 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;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135
rmistry@google.comd6176b02012-08-23 18:14:13 +0000136 mainGetSizeP = &SkView::height;
137 crossGetSizeP = &SkView::width;
138 mainLocP = &SkView::setLocY;
139 crossLocP = &SkView::setLocX;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
rmistry@google.comd6176b02012-08-23 18:14:13 +0000141 mainSetSizeP = &SkView::setHeight;
142 crossSetSizeP = &SkView::setWidth;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143
rmistry@google.comd6176b02012-08-23 18:14:13 +0000144 flexMask = SkView::kFlexV_Mask;
145 }
146 crossLimit += (parent->*crossGetSizeP)();
147 if (fAlign != kStretch_Align)
148 crossSetSizeP = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
rmistry@google.comd6176b02012-08-23 18:14:13 +0000150 int childCount, flexCount;
151 SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152
rmistry@google.comd6176b02012-08-23 18:14:13 +0000153 if (childCount == 0)
154 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
rmistry@google.comd6176b02012-08-23 18:14:13 +0000156 childLimit += (childCount - 1) * fSpacer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157
rmistry@google.comd6176b02012-08-23 18:14:13 +0000158 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;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
rmistry@google.comd6176b02012-08-23 18:14:13 +0000164 if (flexCount > 0 && parentLimit > childLimit)
165 flexAmount = (parentLimit - childLimit) / flexCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166
rmistry@google.comd6176b02012-08-23 18:14:13 +0000167 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);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
rmistry@google.comd6176b02012-08-23 18:14:13 +0000177 if (crossSetSizeP)
178 (child->*crossSetSizeP)(crossLimit);
179 if (child->getFlags() & flexMask)
180 (child->*mainSetSizeP)(flexAmount);
181 pos += (child->*mainGetSizeP)() + fSpacer;
182 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183}
184
185//////////////////////////////////////////////////////////////////////////////////////
186
187#ifdef SK_DEBUG
rmistry@google.comd6176b02012-08-23 18:14:13 +0000188 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 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#else
rmistry@google.comd6176b02012-08-23 18:14:13 +0000195 #define assert_no_attr(dom, node, attr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196#endif
197
198void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
199{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000200 int index;
201 SkScalar value[4];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
rmistry@google.comd6176b02012-08-23 18:14:13 +0000203 if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
204 this->setOrient((Orient)index);
205 else {
206 assert_no_attr(dom, node, "orient");
tomhudson@google.com2c2508d2011-07-29 13:44:30 +0000207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208
rmistry@google.comd6176b02012-08-23 18:14:13 +0000209 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");
tomhudson@google.com2c2508d2011-07-29 13:44:30 +0000217 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218
rmistry@google.comd6176b02012-08-23 18:14:13 +0000219 if (dom.findScalar(node, "spacer", value))
220 this->setSpacer(value[0]);
221 else {
222 assert_no_attr(dom, node, "spacer");
tomhudson@google.com2c2508d2011-07-29 13:44:30 +0000223 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224
rmistry@google.comd6176b02012-08-23 18:14:13 +0000225 if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
226 this->setPack((Pack)index);
227 else {
228 assert_no_attr(dom, node, "pack");
tomhudson@google.com2c2508d2011-07-29 13:44:30 +0000229 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230
rmistry@google.comd6176b02012-08-23 18:14:13 +0000231 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");
tomhudson@google.com2c2508d2011-07-29 13:44:30 +0000235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236}
237
238///////////////////////////////////////////////////////////////////////////////////////////
239
240SkFillViewLayout::SkFillViewLayout()
241{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000242 fMargin.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243}
244
245void SkFillViewLayout::getMargin(SkRect* r) const
246{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000247 if (r)
248 *r = fMargin;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249}
250
251void SkFillViewLayout::setMargin(const SkRect& margin)
252{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000253 fMargin = margin;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254}
255
256void SkFillViewLayout::onLayoutChildren(SkView* parent)
257{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000258 SkView::B2FIter iter(parent);
259 SkView* child;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
rmistry@google.comd6176b02012-08-23 18:14:13 +0000261 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 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267}
268
269void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
270{
rmistry@google.comd6176b02012-08-23 18:14:13 +0000271 this->INHERITED::onInflate(dom, node);
272 (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273}