diff options
Diffstat (limited to 'src/views/SkStackViewLayout.cpp')
-rw-r--r-- | src/views/SkStackViewLayout.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp new file mode 100644 index 0000000..ae2e9e8 --- /dev/null +++ b/src/views/SkStackViewLayout.cpp @@ -0,0 +1,262 @@ +#include "SkStackViewLayout.h" + +SkStackViewLayout::SkStackViewLayout() +{ + fMargin.set(0, 0, 0, 0); + fSpacer = 0; + fOrient = kHorizontal_Orient; + fPack = kStart_Pack; + fAlign = kStart_Align; + fRound = false; +} + +void SkStackViewLayout::setOrient(Orient ori) +{ + SkASSERT((unsigned)ori < kOrientCount); + fOrient = SkToU8(ori); +} + +void SkStackViewLayout::getMargin(SkRect* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStackViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkStackViewLayout::setSpacer(SkScalar spacer) +{ + fSpacer = spacer; +} + +void SkStackViewLayout::setPack(Pack pack) +{ + SkASSERT((unsigned)pack < kPackCount); + fPack = SkToU8(pack); +} + +void SkStackViewLayout::setAlign(Align align) +{ + SkASSERT((unsigned)align < kAlignCount); + fAlign = SkToU8(align); +} + +void SkStackViewLayout::setRound(bool r) +{ + fRound = SkToU8(r); +} + +//////////////////////////////////////////////////////////////////////////////// + +typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit); +typedef SkScalar (SkView::*GetSizeProc)() const; +typedef void (SkView::*SetLocProc)(SkScalar coord); +typedef void (SkView::*SetSizeProc)(SkScalar coord); + +static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } +static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); } +static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; } +static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } + +/* Measure the main-dimension for all the children. If a child is marked flex in that direction + ignore its current value but increment the counter for flexChildren +*/ +static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count, + uint32_t flexMask, int* flexCount) +{ + SkView::B2FIter iter(parent); + SkView* child; + SkScalar limit = 0; + int n = 0, flex = 0; + + while ((child = iter.next()) != NULL) + { + n += 1; + if (child->getFlags() & flexMask) + flex += 1; + else + limit += (child->*sizeProc)(); + } + if (count) + *count = n; + if (flexCount) + *flexCount = flex; + return limit; +} + +void SkStackViewLayout::onLayoutChildren(SkView* parent) +{ + static AlignProc gAlignProcs[] = { + left_align_proc, + center_align_proc, + right_align_proc, + fill_align_proc + }; + + SkScalar startM, endM, crossStartM, crossLimit; + GetSizeProc mainGetSizeP, crossGetSizeP; + SetLocProc mainLocP, crossLocP; + SetSizeProc mainSetSizeP, crossSetSizeP; + SkView::Flag_Mask flexMask; + + if (fOrient == kHorizontal_Orient) + { + startM = fMargin.fLeft; + endM = fMargin.fRight; + crossStartM = fMargin.fTop; + crossLimit = -fMargin.fTop - fMargin.fBottom; + + mainGetSizeP = &SkView::width; + crossGetSizeP = &SkView::height; + mainLocP = &SkView::setLocX; + crossLocP = &SkView::setLocY; + + mainSetSizeP = &SkView::setWidth; + crossSetSizeP = &SkView::setHeight; + + flexMask = SkView::kFlexH_Mask; + } + else + { + startM = fMargin.fTop; + endM = fMargin.fBottom; + crossStartM = fMargin.fLeft; + crossLimit = -fMargin.fLeft - fMargin.fRight; + + mainGetSizeP = &SkView::height; + crossGetSizeP = &SkView::width; + mainLocP = &SkView::setLocY; + crossLocP = &SkView::setLocX; + + mainSetSizeP = &SkView::setHeight; + crossSetSizeP = &SkView::setWidth; + + flexMask = SkView::kFlexV_Mask; + } + crossLimit += (parent->*crossGetSizeP)(); + if (fAlign != kStretch_Align) + crossSetSizeP = NULL; + + int childCount, flexCount; + SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount); + + if (childCount == 0) + return; + + childLimit += (childCount - 1) * fSpacer; + + SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM; + SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit); + SkScalar flexAmount = 0; + SkView::B2FIter iter(parent); + SkView* child; + + if (flexCount > 0 && parentLimit > childLimit) + flexAmount = (parentLimit - childLimit) / flexCount; + + while ((child = iter.next()) != NULL) + { + if (fRound) + pos = SkIntToScalar(SkScalarRound(pos)); + (child->*mainLocP)(pos); + SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit); + if (fRound) + crossLoc = SkIntToScalar(SkScalarRound(crossLoc)); + (child->*crossLocP)(crossLoc); + + if (crossSetSizeP) + (child->*crossSetSizeP)(crossLimit); + if (child->getFlags() & flexMask) + (child->*mainSetSizeP)(flexAmount); + pos += (child->*mainGetSizeP)() + fSpacer; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif + +void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + int index; + SkScalar value[4]; + + if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0) + this->setOrient((Orient)index); + else + assert_no_attr(dom, node, "orient"); + + if (dom.findScalars(node, "margin", value, 4)) + { + SkRect margin; + margin.set(value[0], value[1], value[2], value[3]); + this->setMargin(margin); + } + else + assert_no_attr(dom, node, "margin"); + + if (dom.findScalar(node, "spacer", value)) + this->setSpacer(value[0]); + else + assert_no_attr(dom, node, "spacer"); + + if ((index = dom.findList(node, "pack", "start,center,end")) >= 0) + this->setPack((Pack)index); + else + assert_no_attr(dom, node, "pack"); + + if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0) + this->setAlign((Align)index); + else + assert_no_attr(dom, node, "align"); +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkFillViewLayout::SkFillViewLayout() +{ + fMargin.setEmpty(); +} + +void SkFillViewLayout::getMargin(SkRect* r) const +{ + if (r) + *r = fMargin; +} + +void SkFillViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkFillViewLayout::onLayoutChildren(SkView* parent) +{ + SkView::B2FIter iter(parent); + SkView* child; + + while ((child = iter.next()) != NULL) + { + child->setLoc(fMargin.fLeft, fMargin.fTop); + child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft, + parent->height() - fMargin.fBottom - fMargin.fTop); + } +} + +void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4); +} + |