Home | History | Annotate | Download | only in views
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "SkStackViewLayout.h"
      9 
     10 SkStackViewLayout::SkStackViewLayout()
     11 {
     12     fMargin.set(0, 0, 0, 0);
     13     fSpacer    = 0;
     14     fOrient    = kHorizontal_Orient;
     15     fPack    = kStart_Pack;
     16     fAlign    = kStart_Align;
     17     fRound    = false;
     18 }
     19 
     20 void SkStackViewLayout::setOrient(Orient ori)
     21 {
     22     SkASSERT((unsigned)ori < kOrientCount);
     23     fOrient = SkToU8(ori);
     24 }
     25 
     26 void SkStackViewLayout::getMargin(SkRect* margin) const
     27 {
     28     if (margin)
     29         *margin = fMargin;
     30 }
     31 
     32 void SkStackViewLayout::setMargin(const SkRect& margin)
     33 {
     34     fMargin = margin;
     35 }
     36 
     37 void SkStackViewLayout::setSpacer(SkScalar spacer)
     38 {
     39     fSpacer = spacer;
     40 }
     41 
     42 void SkStackViewLayout::setPack(Pack pack)
     43 {
     44     SkASSERT((unsigned)pack < kPackCount);
     45     fPack = SkToU8(pack);
     46 }
     47 
     48 void SkStackViewLayout::setAlign(Align align)
     49 {
     50     SkASSERT((unsigned)align < kAlignCount);
     51     fAlign = SkToU8(align);
     52 }
     53 
     54 void SkStackViewLayout::setRound(bool r)
     55 {
     56     fRound = SkToU8(r);
     57 }
     58 
     59 ////////////////////////////////////////////////////////////////////////////////
     60 
     61 typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
     62 typedef SkScalar (SkView::*GetSizeProc)() const;
     63 typedef void (SkView::*SetLocProc)(SkScalar coord);
     64 typedef void (SkView::*SetSizeProc)(SkScalar coord);
     65 
     66 static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
     67 static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
     68 static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
     69 static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
     70 
     71 /*    Measure the main-dimension for all the children. If a child is marked flex in that direction
     72     ignore its current value but increment the counter for flexChildren
     73 */
     74 static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
     75                                        uint32_t flexMask, int* flexCount)
     76 {
     77     SkView::B2FIter    iter(parent);
     78     SkView*            child;
     79     SkScalar        limit = 0;
     80     int                n = 0, flex = 0;
     81 
     82     while ((child = iter.next()) != NULL)
     83     {
     84         n += 1;
     85         if (child->getFlags() & flexMask)
     86             flex += 1;
     87         else
     88             limit += (child->*sizeProc)();
     89     }
     90     if (count)
     91         *count = n;
     92     if (flexCount)
     93         *flexCount = flex;
     94     return limit;
     95 }
     96 
     97 void SkStackViewLayout::onLayoutChildren(SkView* parent)
     98 {
     99     static AlignProc gAlignProcs[] = {
    100         left_align_proc,
    101         center_align_proc,
    102         right_align_proc,
    103         fill_align_proc
    104     };
    105 
    106     SkScalar            startM, endM, crossStartM, crossLimit;
    107     GetSizeProc            mainGetSizeP, crossGetSizeP;
    108     SetLocProc            mainLocP, crossLocP;
    109     SetSizeProc            mainSetSizeP, crossSetSizeP;
    110     SkView::Flag_Mask    flexMask;
    111 
    112     if (fOrient == kHorizontal_Orient)
    113     {
    114         startM        = fMargin.fLeft;
    115         endM        = fMargin.fRight;
    116         crossStartM    = fMargin.fTop;
    117         crossLimit    = -fMargin.fTop - fMargin.fBottom;
    118 
    119         mainGetSizeP    = &SkView::width;
    120         crossGetSizeP    = &SkView::height;
    121         mainLocP    = &SkView::setLocX;
    122         crossLocP    = &SkView::setLocY;
    123 
    124         mainSetSizeP  = &SkView::setWidth;
    125         crossSetSizeP = &SkView::setHeight;
    126 
    127         flexMask    = SkView::kFlexH_Mask;
    128     }
    129     else
    130     {
    131         startM        = fMargin.fTop;
    132         endM        = fMargin.fBottom;
    133         crossStartM    = fMargin.fLeft;
    134         crossLimit    = -fMargin.fLeft - fMargin.fRight;
    135 
    136         mainGetSizeP    = &SkView::height;
    137         crossGetSizeP    = &SkView::width;
    138         mainLocP    = &SkView::setLocY;
    139         crossLocP    = &SkView::setLocX;
    140 
    141         mainSetSizeP  = &SkView::setHeight;
    142         crossSetSizeP = &SkView::setWidth;
    143 
    144         flexMask    = SkView::kFlexV_Mask;
    145     }
    146     crossLimit += (parent->*crossGetSizeP)();
    147     if (fAlign != kStretch_Align)
    148         crossSetSizeP = NULL;
    149 
    150     int            childCount, flexCount;
    151     SkScalar    childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
    152 
    153     if (childCount == 0)
    154         return;
    155 
    156     childLimit += (childCount - 1) * fSpacer;
    157 
    158     SkScalar        parentLimit = (parent->*mainGetSizeP)() - startM - endM;
    159     SkScalar        pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
    160     SkScalar        flexAmount = 0;
    161     SkView::B2FIter    iter(parent);
    162     SkView*            child;
    163 
    164     if (flexCount > 0 && parentLimit > childLimit)
    165         flexAmount = (parentLimit - childLimit) / flexCount;
    166 
    167     while ((child = iter.next()) != NULL)
    168     {
    169         if (fRound)
    170             pos = SkIntToScalar(SkScalarRound(pos));
    171         (child->*mainLocP)(pos);
    172         SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
    173         if (fRound)
    174             crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
    175         (child->*crossLocP)(crossLoc);
    176 
    177         if (crossSetSizeP)
    178             (child->*crossSetSizeP)(crossLimit);
    179         if (child->getFlags() & flexMask)
    180             (child->*mainSetSizeP)(flexAmount);
    181         pos += (child->*mainGetSizeP)() + fSpacer;
    182     }
    183 }
    184 
    185 //////////////////////////////////////////////////////////////////////////////////////
    186 
    187 #ifdef SK_DEBUG
    188     static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
    189     {
    190         const char* value = dom.findAttr(node, attr);
    191         if (value)
    192             SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
    193     }
    194 #else
    195     #define assert_no_attr(dom, node, attr)
    196 #endif
    197 
    198 void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
    199 {
    200     int            index;
    201     SkScalar    value[4];
    202 
    203     if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
    204         this->setOrient((Orient)index);
    205     else {
    206         assert_no_attr(dom, node, "orient");
    207         }
    208 
    209     if (dom.findScalars(node, "margin", value, 4))
    210     {
    211         SkRect    margin;
    212         margin.set(value[0], value[1], value[2], value[3]);
    213         this->setMargin(margin);
    214     }
    215     else {
    216         assert_no_attr(dom, node, "margin");
    217         }
    218 
    219     if (dom.findScalar(node, "spacer", value))
    220         this->setSpacer(value[0]);
    221     else {
    222         assert_no_attr(dom, node, "spacer");
    223         }
    224 
    225     if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
    226         this->setPack((Pack)index);
    227     else {
    228         assert_no_attr(dom, node, "pack");
    229         }
    230 
    231     if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
    232         this->setAlign((Align)index);
    233     else {
    234         assert_no_attr(dom, node, "align");
    235         }
    236 }
    237 
    238 ///////////////////////////////////////////////////////////////////////////////////////////
    239 
    240 SkFillViewLayout::SkFillViewLayout()
    241 {
    242     fMargin.setEmpty();
    243 }
    244 
    245 void SkFillViewLayout::getMargin(SkRect* r) const
    246 {
    247     if (r)
    248         *r = fMargin;
    249 }
    250 
    251 void SkFillViewLayout::setMargin(const SkRect& margin)
    252 {
    253     fMargin = margin;
    254 }
    255 
    256 void SkFillViewLayout::onLayoutChildren(SkView* parent)
    257 {
    258     SkView::B2FIter    iter(parent);
    259     SkView*            child;
    260 
    261     while ((child = iter.next()) != NULL)
    262     {
    263         child->setLoc(fMargin.fLeft, fMargin.fTop);
    264         child->setSize(    parent->width() - fMargin.fRight - fMargin.fLeft,
    265                         parent->height() - fMargin.fBottom - fMargin.fTop);
    266     }
    267 }
    268 
    269 void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
    270 {
    271     this->INHERITED::onInflate(dom, node);
    272     (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
    273 }
    274