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
Decode()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
Draw(SkCanvas * canvas,Node * element,int x,int y,int width,int height)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