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