/*
* 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 = SkScalarRoundToScalar(pos);
(child->*mainLocP)(pos);
SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
if (fRound)
crossLoc = SkScalarRoundToScalar(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);
}