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 = 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 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 } 274