| |
| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #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); |
| } |