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()) != nullptr)
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 = nullptr;
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()) != nullptr)
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
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()) != nullptr)
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