aboutsummaryrefslogtreecommitdiffstats
path: root/src/views/SkStackViewLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/views/SkStackViewLayout.cpp')
-rw-r--r--src/views/SkStackViewLayout.cpp262
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);
+}
+