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