Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2006, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "RenderSkinCombo.h"
     28 
     29 #include "Document.h"
     30 #include "Element.h"
     31 #include "Node.h"
     32 #include "NodeRenderStyle.h"
     33 #include "RenderStyle.h"
     34 #include "SkCanvas.h"
     35 #include "SkNinePatch.h"
     36 #include <utils/AssetManager.h>
     37 #include <wtf/text/CString.h>
     38 
     39 extern android::AssetManager* globalAssetManager();
     40 
     41 namespace WebCore {
     42 
     43 // Indicates if the entire asset is being drawn, or if the border is being
     44 // excluded and just the arrow drawn.
     45 enum BorderStyle {
     46     FullAsset,
     47     NoBorder,
     48     BorderStyleCount // Keep at the end.
     49 };
     50 
     51 // There are 2.5 different concepts of a 'border' here, which results
     52 // in rather a lot of magic constants.
     53 
     54 // Firstly, we have the extra padding that webkit needs to know about,
     55 // which defines how much bigger this element is made by the
     56 // asset. This is actually a bit broader than the actual border on the
     57 // asset, to make things look less cramped. The border is the same
     58 // width on all sides, except on the right when it's significantly
     59 // wider to allow for the arrow.
     60 const int RenderSkinCombo::arrowMargin[ResolutionCount] = {
     61     22, // Medium resolution
     62     34, // High resolution
     63     46 // Extra high resolution
     64 };
     65 const int RenderSkinCombo::padMargin[ResolutionCount] = {
     66     2, // Medium resolution
     67     5, // High resolution
     68     6 // Extra high resolution
     69 };
     70 
     71 namespace {
     72 // Then we have the borders used for the 9-patch stretch. The
     73 // rectangle at the centre of these borders is entirely below and to
     74 // the left of the arrow in the asset. Hence the border widths are the
     75 // same for the bottom and left, but are different for the top. The
     76 // right hand border width happens to be the same as arrowMargin
     77 // defined above.
     78 const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch
     79     3, // Medium resolution
     80     5, // High resolution
     81     6 // Extra high resolution
     82 
     83 };
     84 const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch
     85     15, // Medium resolution
     86     23, // High resolution
     87     34 // Extra high resolution
     88 };
     89 
     90 // Finally, if the border is defined by the CSS, we only draw the
     91 // arrow and not the border. We do this by drawing the relevant subset
     92 // of the bitmap, which must now be precisely determined by what's in
     93 // the asset with no extra padding to make things look properly
     94 // spaced. The border to remove at the top, right and bottom of the
     95 // image is the same as stretchMargin above, but we need to know the width
     96 // of the arrow.
     97 const int arrowWidth[RenderSkinAndroid::ResolutionCount] = {
     98     22, // Medium resolution
     99     31, // High resolution
    100     42 // Extra high resolution
    101 };
    102 
    103 // Store the calculated 9 patch margins for each border style.
    104 SkIRect margin[BorderStyleCount];
    105 
    106 SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled)
    107 bool isDecodingAttempted = false; // True if we've tried to decode the assets
    108 bool isDecoded = false;           // True if all assets were decoded
    109 
    110 } // namespace
    111 
    112 void RenderSkinCombo::Decode()
    113 {
    114     if (isDecodingAttempted)
    115         return;
    116 
    117     isDecodingAttempted = true;
    118     isDecoded = false;
    119 
    120     android::AssetManager* am = globalAssetManager();
    121 
    122     String drawableDirectory = RenderSkinAndroid::DrawableDirectory();
    123     Resolution res = RenderSkinAndroid::DrawableResolution();
    124 
    125     isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
    126     isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
    127 
    128     int width = bitmaps[kNormal][FullAsset].width();
    129     int height = bitmaps[kNormal][FullAsset].height();
    130     SkIRect subset;
    131     subset.set(width - arrowWidth[res], 0, width, height);
    132     bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
    133     bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
    134 
    135     // Calculate 9 patch margins.
    136     SkIRect fullAssetMargin;
    137     fullAssetMargin.fLeft = stretchMargin[res];
    138     fullAssetMargin.fTop = stretchTop[res];
    139     fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res];
    140     fullAssetMargin.fBottom = stretchMargin[res];
    141 
    142     SkIRect noBorderMargin;
    143     noBorderMargin.fLeft = 0;
    144     noBorderMargin.fTop = stretchTop[res];
    145     noBorderMargin.fRight = 0;
    146     noBorderMargin.fBottom = stretchMargin[res];
    147 
    148     margin[FullAsset] = fullAssetMargin;
    149     margin[NoBorder] = noBorderMargin;
    150 }
    151 
    152 bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
    153 {
    154     if (!isDecodingAttempted)
    155         Decode();
    156 
    157     if (!isDecoded)
    158         return true;
    159 
    160     State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
    161     height = std::max(height, (stretchMargin[RenderSkinAndroid::DrawableResolution()]<<1) + 1);
    162 
    163     SkRect bounds;
    164     BorderStyle drawBorder = FullAsset;
    165 
    166     bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
    167     RenderStyle* style = element->renderStyle();
    168     SkPaint paint;
    169     paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb());
    170     canvas->drawRect(bounds, paint);
    171 
    172     bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height));
    173 
    174     // If this is an appearance where RenderTheme::paint returns true
    175     // without doing anything, this means that
    176     // RenderBox::PaintBoxDecorationWithSize will end up painting the
    177     // border, so we shouldn't paint a border here.
    178     if (style->appearance() == MenulistButtonPart ||
    179         style->appearance() == ListboxPart ||
    180         style->appearance() == TextFieldPart ||
    181         style->appearance() == TextAreaPart) {
    182         bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth());
    183         bounds.fRight -= SkIntToScalar(style->borderRightWidth());
    184         bounds.fTop += SkIntToScalar(style->borderTopWidth());
    185         bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
    186         drawBorder = NoBorder;
    187     }
    188     SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]);
    189     return false;
    190 }
    191 
    192 }   // namspace WebCore
    193