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