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
SkStackViewLayout()10 SkStackViewLayout::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
setOrient(Orient ori)20 void SkStackViewLayout::setOrient(Orient ori)
21 {
22 SkASSERT((unsigned)ori < kOrientCount);
23 fOrient = SkToU8(ori);
24 }
25
getMargin(SkRect * margin) const26 void SkStackViewLayout::getMargin(SkRect* margin) const
27 {
28 if (margin)
29 *margin = fMargin;
30 }
31
setMargin(const SkRect & margin)32 void SkStackViewLayout::setMargin(const SkRect& margin)
33 {
34 fMargin = margin;
35 }
36
setSpacer(SkScalar spacer)37 void SkStackViewLayout::setSpacer(SkScalar spacer)
38 {
39 fSpacer = spacer;
40 }
41
setPack(Pack pack)42 void SkStackViewLayout::setPack(Pack pack)
43 {
44 SkASSERT((unsigned)pack < kPackCount);
45 fPack = SkToU8(pack);
46 }
47
setAlign(Align align)48 void SkStackViewLayout::setAlign(Align align)
49 {
50 SkASSERT((unsigned)align < kAlignCount);
51 fAlign = SkToU8(align);
52 }
53
setRound(bool r)54 void SkStackViewLayout::setRound(bool r)
55 {
56 fRound = SkToU8(r);
57 }
58
59 ////////////////////////////////////////////////////////////////////////////////
60
61 typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
62 typedef SkScalar (SkView::*GetSizeProc)() const;
63 typedef void (SkView::*SetLocProc)(SkScalar coord);
64 typedef void (SkView::*SetSizeProc)(SkScalar coord);
65
left_align_proc(SkScalar childLimit,SkScalar parentLimit)66 static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
center_align_proc(SkScalar childLimit,SkScalar parentLimit)67 static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
right_align_proc(SkScalar childLimit,SkScalar parentLimit)68 static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
fill_align_proc(SkScalar childLimit,SkScalar parentLimit)69 static 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 */
compute_children_limit(SkView * parent,GetSizeProc sizeProc,int * count,uint32_t flexMask,int * flexCount)74 static 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
onLayoutChildren(SkView * parent)97 void 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
assert_no_attr(const SkDOM & dom,const SkDOM::Node * node,const char attr[])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
onInflate(const SkDOM & dom,const SkDOM::Node * node)198 void 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
SkFillViewLayout()240 SkFillViewLayout::SkFillViewLayout()
241 {
242 fMargin.setEmpty();
243 }
244
getMargin(SkRect * r) const245 void SkFillViewLayout::getMargin(SkRect* r) const
246 {
247 if (r)
248 *r = fMargin;
249 }
250
setMargin(const SkRect & margin)251 void SkFillViewLayout::setMargin(const SkRect& margin)
252 {
253 fMargin = margin;
254 }
255
onLayoutChildren(SkView * parent)256 void 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
onInflate(const SkDOM & dom,const SkDOM::Node * node)269 void 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