|
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 |
|
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 |
|
20 void SkStackViewLayout::setOrient(Orient ori) |
|
21 { |
|
22 SkASSERT((unsigned)ori < kOrientCount); |
|
23 fOrient = SkToU8(ori); |
|
24 } |
|
25 |
|
26 void SkStackViewLayout::getMargin(SkRect* margin) const |
|
27 { |
|
28 if (margin) |
|
29 *margin = fMargin; |
|
30 } |
|
31 |
|
32 void SkStackViewLayout::setMargin(const SkRect& margin) |
|
33 { |
|
34 fMargin = margin; |
|
35 } |
|
36 |
|
37 void SkStackViewLayout::setSpacer(SkScalar spacer) |
|
38 { |
|
39 fSpacer = spacer; |
|
40 } |
|
41 |
|
42 void SkStackViewLayout::setPack(Pack pack) |
|
43 { |
|
44 SkASSERT((unsigned)pack < kPackCount); |
|
45 fPack = SkToU8(pack); |
|
46 } |
|
47 |
|
48 void SkStackViewLayout::setAlign(Align align) |
|
49 { |
|
50 SkASSERT((unsigned)align < kAlignCount); |
|
51 fAlign = SkToU8(align); |
|
52 } |
|
53 |
|
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 |
|
66 static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } |
|
67 static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); } |
|
68 static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; } |
|
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 */ |
|
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 |
|
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 = SkScalarRoundToScalar(pos); |
|
171 (child->*mainLocP)(pos); |
|
172 SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit); |
|
173 if (fRound) |
|
174 crossLoc = SkScalarRoundToScalar(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 |
|
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 |
|
240 SkFillViewLayout::SkFillViewLayout() |
|
241 { |
|
242 fMargin.setEmpty(); |
|
243 } |
|
244 |
|
245 void SkFillViewLayout::getMargin(SkRect* r) const |
|
246 { |
|
247 if (r) |
|
248 *r = fMargin; |
|
249 } |
|
250 |
|
251 void SkFillViewLayout::setMargin(const SkRect& margin) |
|
252 { |
|
253 fMargin = margin; |
|
254 } |
|
255 |
|
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 |
|
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 } |