1 /*
2 * Copyright 2009, 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 "RenderThemeAndroid.h"
28
29 #include "Color.h"
30 #include "Element.h"
31 #include "GraphicsContext.h"
32 #include "HTMLNames.h"
33 #include "HTMLOptionElement.h"
34 #include "HTMLSelectElement.h"
35 #include "Node.h"
36 #include "PlatformGraphicsContext.h"
37 #if ENABLE(VIDEO)
38 #include "RenderMediaControls.h"
39 #endif
40 #include "RenderObject.h"
41 #include "RenderSkinAndroid.h"
42 #include "RenderSkinMediaButton.h"
43 #include "RenderSlider.h"
44 #include "RoundedIntRect.h"
45 #include "SkCanvas.h"
46 #include "UserAgentStyleSheets.h"
47 #include "WebCoreFrameBridge.h"
48
49 namespace WebCore {
50
51 // Add padding to the fontSize of ListBoxes to get their maximum sizes.
52 // Listboxes often have a specified size. Since we change them into
53 // dropdowns, we want a much smaller height, which encompasses the text.
54 const int listboxPadding = 5;
55
56 // This is the color of selection in a textfield. It was computed from
57 // frameworks/base/core/res/res/values/colors.xml, which uses #9983CC39
58 // (decimal a = 153, r = 131, g = 204, b = 57)
59 // for all four highlighted text values. Blending this with white yields:
60 // R = (131 * 153 + 255 * (255 - 153)) / 255 -> 180.6
61 // G = (204 * 153 + 255 * (255 - 153)) / 255 -> 224.4
62 // B = ( 57 * 153 + 255 * (255 - 153)) / 255 -> 136.2
63
64 const RGBA32 selectionColor = makeRGB(181, 224, 136);
65
66 // Colors copied from the holo resources
67 const RGBA32 defaultBgColor = makeRGBA(204, 204, 204, 197);
68 const RGBA32 defaultBgBright = makeRGBA(213, 213, 213, 221);
69 const RGBA32 defaultBgDark = makeRGBA(92, 92, 92, 160);
70 const RGBA32 defaultBgMedium = makeRGBA(132, 132, 132, 111);
71 const RGBA32 defaultFgColor = makeRGBA(101, 101, 101, 225);
72 const RGBA32 defaultCheckColor = makeRGBA(0, 153, 204, 255);
73 const RGBA32 defaultCheckColorShadow = makeRGBA(29, 123, 154, 192);
74
75 const RGBA32 disabledBgColor = makeRGBA(205, 205, 205, 107);
76 const RGBA32 disabledBgBright = makeRGBA(213, 213, 213, 133);
77 const RGBA32 disabledBgDark = makeRGBA(92, 92, 92, 96);
78 const RGBA32 disabledBgMedium = makeRGBA(132, 132, 132, 111);
79 const RGBA32 disabledFgColor = makeRGBA(61, 61, 61, 68);
80 const RGBA32 disabledCheckColor = makeRGBA(61, 61, 61, 128);
81 const RGBA32 disabledCheckColorShadow = disabledCheckColor;
82
83 const int paddingButton = 2;
84 const int cornerButton = 2;
85
86 // scale factors for various resolutions
87 const float scaleFactor[RenderSkinAndroid::ResolutionCount] = {
88 1.0f, // medium res
89 1.5f, // high res
90 2.0f // extra high res
91 };
92
getWebFrame(const Node * node)93 static android::WebFrame* getWebFrame(const Node* node)
94 {
95 if (!node)
96 return 0;
97 return android::WebFrame::getWebFrame(node->document()->frame());
98 }
99
100 // Draws a nice, mitered line.
101 // This is a partial copy from RenderObject::drawLineForBoxSide
drawLineForBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,BoxSide side,Color color,int adjacentWidth1,int adjacentWidth2)102 static void drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1,
103 int x2, int y2, BoxSide side, Color color,
104 int adjacentWidth1, int adjacentWidth2)
105 {
106 static const bool antialias = false;
107 graphicsContext->setFillColor(color, graphicsContext->fillColorSpace());
108 if (!adjacentWidth1 && !adjacentWidth2) {
109 // Turn off antialiasing to match the behavior of drawConvexPolygon();
110 // this matters for rects in transformed contexts.
111 bool wasAntialiased = graphicsContext->shouldAntialias();
112 graphicsContext->setShouldAntialias(antialias);
113 graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1));
114 graphicsContext->setShouldAntialias(wasAntialiased);
115 return;
116 }
117 FloatPoint quad[4];
118 switch (side) {
119 case BSTop:
120 quad[0] = FloatPoint(x1 + max(-adjacentWidth1, 0), y1);
121 quad[1] = FloatPoint(x1 + max(adjacentWidth1, 0), y2);
122 quad[2] = FloatPoint(x2 - max(adjacentWidth2, 0), y2);
123 quad[3] = FloatPoint(x2 - max(-adjacentWidth2, 0), y1);
124 break;
125 case BSBottom:
126 quad[0] = FloatPoint(x1 + max(adjacentWidth1, 0), y1);
127 quad[1] = FloatPoint(x1 + max(-adjacentWidth1, 0), y2);
128 quad[2] = FloatPoint(x2 - max(-adjacentWidth2, 0), y2);
129 quad[3] = FloatPoint(x2 - max(adjacentWidth2, 0), y1);
130 break;
131 case BSLeft:
132 quad[0] = FloatPoint(x1, y1 + max(-adjacentWidth1, 0));
133 quad[1] = FloatPoint(x1, y2 - max(-adjacentWidth2, 0));
134 quad[2] = FloatPoint(x2, y2 - max(adjacentWidth2, 0));
135 quad[3] = FloatPoint(x2, y1 + max(adjacentWidth1, 0));
136 break;
137 case BSRight:
138 quad[0] = FloatPoint(x1, y1 + max(adjacentWidth1, 0));
139 quad[1] = FloatPoint(x1, y2 - max(adjacentWidth2, 0));
140 quad[2] = FloatPoint(x2, y2 - max(-adjacentWidth2, 0));
141 quad[3] = FloatPoint(x2, y1 + max(-adjacentWidth1, 0));
142 break;
143 }
144
145 graphicsContext->drawConvexPolygon(4, quad, antialias);
146 }
147
theme()148 RenderTheme* theme()
149 {
150 DEFINE_STATIC_LOCAL(RenderThemeAndroid, androidTheme, ());
151 return &androidTheme;
152 }
153
themeForPage(Page * page)154 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
155 {
156 static RenderTheme* rt = RenderThemeAndroid::create().releaseRef();
157 return rt;
158 }
159
create()160 PassRefPtr<RenderTheme> RenderThemeAndroid::create()
161 {
162 return adoptRef(new RenderThemeAndroid());
163 }
164
RenderThemeAndroid()165 RenderThemeAndroid::RenderThemeAndroid()
166 {
167 }
168
~RenderThemeAndroid()169 RenderThemeAndroid::~RenderThemeAndroid()
170 {
171 }
172
close()173 void RenderThemeAndroid::close()
174 {
175 }
176
stateChanged(RenderObject * obj,ControlState state) const177 bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const
178 {
179 if (CheckedState == state) {
180 obj->repaint();
181 return true;
182 }
183 return false;
184 }
185
platformActiveSelectionBackgroundColor() const186 Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const
187 {
188 return Color(selectionColor);
189 }
190
platformInactiveSelectionBackgroundColor() const191 Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const
192 {
193 return Color(Color::transparent);
194 }
195
platformActiveSelectionForegroundColor() const196 Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const
197 {
198 return Color::black;
199 }
200
platformInactiveSelectionForegroundColor() const201 Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const
202 {
203 return Color::black;
204 }
205
platformTextSearchHighlightColor() const206 Color RenderThemeAndroid::platformTextSearchHighlightColor() const
207 {
208 return Color(Color::transparent);
209 }
210
platformActiveListBoxSelectionBackgroundColor() const211 Color RenderThemeAndroid::platformActiveListBoxSelectionBackgroundColor() const
212 {
213 return Color(Color::transparent);
214 }
215
platformInactiveListBoxSelectionBackgroundColor() const216 Color RenderThemeAndroid::platformInactiveListBoxSelectionBackgroundColor() const
217 {
218 return Color(Color::transparent);
219 }
220
platformActiveListBoxSelectionForegroundColor() const221 Color RenderThemeAndroid::platformActiveListBoxSelectionForegroundColor() const
222 {
223 return Color(Color::transparent);
224 }
225
platformInactiveListBoxSelectionForegroundColor() const226 Color RenderThemeAndroid::platformInactiveListBoxSelectionForegroundColor() const
227 {
228 return Color(Color::transparent);
229 }
230
platformActiveTextSearchHighlightColor() const231 Color RenderThemeAndroid::platformActiveTextSearchHighlightColor() const
232 {
233 return Color(0x00, 0x99, 0xcc, 0x99); // HOLO_DARK
234 }
235
platformInactiveTextSearchHighlightColor() const236 Color RenderThemeAndroid::platformInactiveTextSearchHighlightColor() const
237 {
238 return Color(0x33, 0xb5, 0xe5, 0x66); // HOLO_LIGHT
239 }
240
baselinePosition(const RenderObject * obj) const241 int RenderThemeAndroid::baselinePosition(const RenderObject* obj) const
242 {
243 // From the description of this function in RenderTheme.h:
244 // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline
245 // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
246 // controls that need to do this.
247 //
248 // Our checkboxes and radio buttons need to be offset to line up properly.
249 return RenderTheme::baselinePosition(obj) - 6;
250 }
251
addIntrinsicMargins(RenderStyle * style) const252 void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const
253 {
254 // Cut out the intrinsic margins completely if we end up using a small font size
255 if (style->fontSize() < 11)
256 return;
257
258 // Intrinsic margin value.
259 const int m = 2;
260
261 // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
262 if (style->width().isIntrinsicOrAuto()) {
263 if (style->marginLeft().quirk())
264 style->setMarginLeft(Length(m, Fixed));
265 if (style->marginRight().quirk())
266 style->setMarginRight(Length(m, Fixed));
267 }
268
269 if (style->height().isAuto()) {
270 if (style->marginTop().quirk())
271 style->setMarginTop(Length(m, Fixed));
272 if (style->marginBottom().quirk())
273 style->setMarginBottom(Length(m, Fixed));
274 }
275 }
276
supportsFocus(ControlPart appearance)277 bool RenderThemeAndroid::supportsFocus(ControlPart appearance)
278 {
279 switch (appearance) {
280 case PushButtonPart:
281 case ButtonPart:
282 case TextFieldPart:
283 return true;
284 default:
285 return false;
286 }
287
288 return false;
289 }
290
adjustButtonStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const291 void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
292 {
293 }
294
paintCheckbox(RenderObject * obj,const PaintInfo & info,const IntRect & rect)295 bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
296 {
297 paintRadio(obj, info, rect);
298 return false;
299 }
300
paintButton(RenderObject * obj,const PaintInfo & info,const IntRect & rect)301 bool RenderThemeAndroid::paintButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
302 {
303 // If it is a disabled button, simply paint it to the master picture.
304 Node* node = obj->node();
305 Element* formControlElement = static_cast<Element*>(node);
306 if (formControlElement) {
307 android::WebFrame* webFrame = getWebFrame(node);
308 if (webFrame) {
309 GraphicsContext *context = info.context;
310 IntRect innerrect = IntRect(rect.x() + paddingButton, rect.y() + paddingButton,
311 rect.width() - 2 * paddingButton, rect.height() - 2 * paddingButton);
312 IntSize cornerrect = IntSize(cornerButton, cornerButton);
313 Color bg, bright, dark, medium;
314 if (formControlElement->isEnabledFormControl()) {
315 bg = Color(defaultBgColor);
316 bright = Color(defaultBgBright);
317 dark = Color(defaultBgDark);
318 medium = Color(defaultBgMedium);
319 } else {
320 bg = Color(disabledBgColor);
321 bright = Color(disabledBgBright);
322 dark = Color(disabledBgDark);
323 medium = Color(disabledBgMedium);
324 }
325 context->save();
326 RoundedIntRect border(rect, cornerrect, cornerrect, cornerrect, cornerrect);
327 context->addRoundedRectClip(border);
328 context->setStrokeStyle(NoStroke);
329 drawLineForBoxSide(context, rect.x(), rect.y(), rect.maxX(), innerrect.y(),
330 BSTop, bright, paddingButton, paddingButton);
331 drawLineForBoxSide(context, rect.x(), rect.y(), innerrect.x(), rect.maxY(),
332 BSLeft, medium, paddingButton, paddingButton);
333 drawLineForBoxSide(context, innerrect.maxX(), rect.y(), rect.maxX(), rect.maxY(),
334 BSRight, medium, paddingButton, paddingButton);
335 drawLineForBoxSide(context, rect.x(), innerrect.maxY(), rect.maxX(), rect.maxY(),
336 BSBottom, dark, paddingButton, paddingButton);
337 context->fillRect(innerrect, bg, context->fillColorSpace());
338 context->restore();
339 }
340 }
341
342
343 // We always return false so we do not request to be redrawn.
344 return false;
345 }
346
347 #if ENABLE(VIDEO)
348
extraMediaControlsStyleSheet()349 String RenderThemeAndroid::extraMediaControlsStyleSheet()
350 {
351 return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet));
352 }
353
shouldRenderMediaControlPart(ControlPart part,Element * e)354 bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e)
355 {
356 HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
357 switch (part) {
358 case MediaMuteButtonPart:
359 return false;
360 case MediaSeekBackButtonPart:
361 case MediaSeekForwardButtonPart:
362 return false;
363 case MediaRewindButtonPart:
364 return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
365 case MediaReturnToRealtimeButtonPart:
366 return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
367 case MediaFullscreenButtonPart:
368 return mediaElement->supportsFullscreen();
369 case MediaToggleClosedCaptionsButtonPart:
370 return mediaElement->hasClosedCaptions();
371 default:
372 return true;
373 }
374 }
375
paintMediaFullscreenButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)376 bool RenderThemeAndroid::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
377 {
378 bool translucent = false;
379 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
380 translucent = true;
381 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::FULLSCREEN, translucent);
382 return false;
383 }
384
paintMediaMuteButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)385 bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
386 {
387 bool translucent = false;
388 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
389 translucent = true;
390 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::MUTE, translucent);
391 return false;
392 }
393
paintMediaPlayButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)394 bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
395 {
396 bool translucent = false;
397 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
398 translucent = true;
399 if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
400 if (btn->displayType() == MediaPlayButton)
401 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::PLAY, translucent);
402 else
403 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::PAUSE, translucent);
404 return false;
405 }
406 return true;
407 }
408
paintMediaSeekBackButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)409 bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
410 {
411 bool translucent = false;
412 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
413 translucent = true;
414 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::REWIND, translucent);
415 return false;
416 }
417
paintMediaSeekForwardButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)418 bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
419 {
420 bool translucent = false;
421 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
422 translucent = true;
423 paintInfo.context->platformContext()->drawMediaButton(rect, RenderSkinMediaButton::FORWARD, translucent);
424 return false;
425 }
426
paintMediaControlsBackground(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)427 bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
428 {
429 bool translucent = false;
430 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
431 translucent = true;
432 paintInfo.context->platformContext()->drawMediaButton(rect,
433 RenderSkinMediaButton::BACKGROUND_SLIDER,
434 translucent, false);
435 return false;
436 }
437
paintMediaSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)438 bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
439 {
440 bool translucent = false;
441 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
442 translucent = true;
443 IntRect thumb;
444 if (o && o->isSlider())
445 thumb = toRenderSlider(o)->thumbRect();
446 paintInfo.context->platformContext()->drawMediaButton(rect,
447 RenderSkinMediaButton::SLIDER_TRACK, translucent, true, thumb);
448 return false;
449 }
450
paintMediaSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & rect)451 bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
452 {
453 bool translucent = false;
454 if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
455 translucent = true;
456 paintInfo.context->platformContext()->drawMediaButton(rect,
457 RenderSkinMediaButton::SLIDER_THUMB,
458 translucent, false);
459 return false;
460 }
461
adjustSliderThumbSize(RenderObject * o) const462 void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const
463 {
464 static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth();
465 static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight();
466 o->style()->setWidth(Length(sliderThumbWidth, Fixed));
467 o->style()->setHeight(Length(sliderThumbHeight, Fixed));
468 }
469
470 #endif
471
paintRadio(RenderObject * obj,const PaintInfo & info,const IntRect & rect)472 bool RenderThemeAndroid::paintRadio(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
473 {
474 Node* node = obj->node();
475 Element* element = static_cast<Element*>(node);
476 if (element) {
477 InputElement* input = element->toInputElement();
478 GraphicsContext* context = info.context;
479 context->save();
480 Color borderColor = defaultFgColor;
481 Color checkColor = defaultCheckColor;
482 Color checkColorShadow = defaultCheckColorShadow;
483 if (!element->isEnabledFormControl()) {
484 borderColor = disabledFgColor;
485 checkColor = disabledCheckColor;
486 checkColorShadow = disabledCheckColorShadow;
487 }
488 IntRect borderRect = rect;
489 borderRect.inflate(-3);
490 const float cx = borderRect.center().x();
491 const float cy = borderRect.center().y() - 1;
492 context->setStrokeStyle(SolidStroke);
493 context->setStrokeColor(borderColor, context->strokeColorSpace());
494 context->setStrokeThickness(1);
495 context->setFillColor(Color::transparent, context->fillColorSpace());
496 context->setShadow(FloatSize(), 1.0f, borderColor, context->fillColorSpace());
497 if (input->isCheckbox()) {
498 if (input->isChecked()) {
499 Path clip;
500 clip.moveTo(FloatPoint(cx, cy - 1));
501 clip.addLineTo(FloatPoint(rect.maxX() - 3, rect.y() + 1));
502 clip.addLineTo(FloatPoint(rect.maxX(), rect.y() + 4));
503 clip.addLineTo(FloatPoint(cx, cy + 5));
504 clip.closeSubpath();
505 context->save();
506 context->clipOut(clip);
507 }
508 context->drawRect(borderRect);
509 if (input->isChecked())
510 context->restore();
511 } else
512 context->drawEllipse(borderRect);
513 if (input->isChecked()) {
514 context->setFillColor(checkColor, context->fillColorSpace());
515 context->setStrokeColor(Color::transparent, context->strokeColorSpace());
516 context->setShadow(FloatSize(), 2, checkColorShadow, context->fillColorSpace());
517 if (input->isCheckbox()) {
518 Path checkmark;
519 checkmark.moveTo(FloatPoint(cx, cy));
520 checkmark.addLineTo(FloatPoint(rect.maxX() - 2, rect.y() + 1));
521 checkmark.addLineTo(FloatPoint(rect.maxX(), rect.y() + 3));
522 checkmark.addLineTo(FloatPoint(cx, cy + 4));
523 checkmark.addLineTo(FloatPoint(cx - 4, cy));
524 checkmark.addLineTo(FloatPoint(cx - 2, cy - 2));
525 checkmark.closeSubpath();
526 context->fillPath(checkmark);
527 } else {
528 borderRect.inflate(-3);
529 context->drawEllipse(borderRect);
530 }
531 }
532 context->restore();
533 }
534 return false;
535 }
536
setCheckboxSize(RenderStyle * style) const537 void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const
538 {
539 style->setWidth(Length(19, Fixed));
540 style->setHeight(Length(19, Fixed));
541 }
542
setRadioSize(RenderStyle * style) const543 void RenderThemeAndroid::setRadioSize(RenderStyle* style) const
544 {
545 // This is the same as checkboxes.
546 setCheckboxSize(style);
547 }
548
adjustTextFieldStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const549 void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
550 {
551 addIntrinsicMargins(style);
552 }
553
paintTextField(RenderObject *,const PaintInfo &,const IntRect &)554 bool RenderThemeAndroid::paintTextField(RenderObject*, const PaintInfo&, const IntRect&)
555 {
556 return true;
557 }
558
adjustTextAreaStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const559 void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
560 {
561 addIntrinsicMargins(style);
562 }
563
paintTextArea(RenderObject * obj,const PaintInfo & info,const IntRect & rect)564 bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
565 {
566 if (obj->isMenuList())
567 paintCombo(obj, info, rect);
568 return true;
569 }
570
adjustSearchFieldStyle(CSSStyleSelector *,RenderStyle * style,Element *) const571 void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
572 {
573 addIntrinsicMargins(style);
574 }
575
paintSearchField(RenderObject *,const PaintInfo &,const IntRect &)576 bool RenderThemeAndroid::paintSearchField(RenderObject*, const PaintInfo&, const IntRect&)
577 {
578 return true;
579 }
580
adjustMenuListStyleCommon(RenderStyle * style)581 static void adjustMenuListStyleCommon(RenderStyle* style)
582 {
583 // Added to make room for our arrow and make the touch target less cramped.
584 const int padding = (int)(scaleFactor[RenderSkinAndroid::DrawableResolution()] + 0.5f);
585 style->setPaddingLeft(Length(padding,Fixed));
586 style->setPaddingTop(Length(padding, Fixed));
587 style->setPaddingBottom(Length(padding, Fixed));
588 // allocate height as arrow size
589 int arrow = std::max(18, style->fontMetrics().height() + 2 * padding);
590 style->setPaddingRight(Length(arrow, Fixed));
591 style->setMinHeight(Length(arrow, Fixed));
592 style->setHeight(Length(arrow, Fixed));
593 }
594
adjustListboxStyle(CSSStyleSelector *,RenderStyle * style,Element * e) const595 void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
596 {
597 adjustMenuListButtonStyle(0, style, 0);
598 }
599
adjustMenuListStyle(CSSStyleSelector *,RenderStyle * style,Element *) const600 void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
601 {
602 adjustMenuListStyleCommon(style);
603 addIntrinsicMargins(style);
604 }
605
paintCombo(RenderObject * obj,const PaintInfo & info,const IntRect & rect)606 bool RenderThemeAndroid::paintCombo(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
607 {
608 if (obj->style() && !obj->style()->visitedDependentColor(CSSPropertyBackgroundColor).alpha())
609 return true;
610 Node* node = obj->node();
611 Element* element = static_cast<Element*>(node);
612 if (element) {
613 InputElement* input = element->toInputElement();
614 GraphicsContext* context = info.context;
615 context->save();
616 if (!element->isEnabledFormControl())
617 context->setAlpha(0.5f);
618 IntRect bounds = IntRect(rect.x(), rect.y(), rect.width(), rect.height());
619 // paint bg color
620 RenderStyle* style = obj->style();
621 context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor),
622 context->fillColorSpace());
623 context->fillRect(FloatRect(bounds));
624 // copied form the original RenderSkinCombo:
625 // If this is an appearance where RenderTheme::paint returns true
626 // without doing anything, this means that
627 // RenderBox::PaintBoxDecorationWithSize will end up painting the
628 // border, so we shouldn't paint a border here.
629 if (style->appearance() != MenulistButtonPart &&
630 style->appearance() != ListboxPart &&
631 style->appearance() != TextFieldPart &&
632 style->appearance() != TextAreaPart) {
633 const int arrowSize = bounds.height();
634 // dropdown button bg
635 context->setFillColor(Color(defaultBgColor), context->fillColorSpace());
636 context->fillRect(FloatRect(bounds.maxX() - arrowSize + 0.5f, bounds.y() + .5f,
637 arrowSize - 1, bounds.height() - 1));
638 // outline
639 context->setStrokeStyle(SolidStroke);
640 context->setStrokeThickness(1.0f);
641 context->setStrokeColor(Color(defaultBgDark), context->strokeColorSpace());
642 context->strokeRect(bounds, 1.0f);
643 // arrow
644 context->setFillColor(Color(defaultFgColor), context->fillColorSpace());
645 Path tri = Path();
646 tri.clear();
647 const float aw = arrowSize - 10;
648 FloatPoint br = FloatPoint(bounds.maxX() - 4, bounds.maxY() - 4);
649 tri.moveTo(br);
650 tri.addLineTo(FloatPoint(br.x() - aw, br.y()));
651 tri.addLineTo(FloatPoint(br.x(), br.y() - aw));
652 context->fillPath(tri);
653 }
654 context->restore();
655 }
656 return false;
657 }
658
paintMenuList(RenderObject * obj,const PaintInfo & info,const IntRect & rect)659 bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
660 {
661 return paintCombo(obj, info, rect);
662 }
663
adjustMenuListButtonStyle(CSSStyleSelector *,RenderStyle * style,Element *) const664 void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*,
665 RenderStyle* style, Element*) const
666 {
667 // Copied from RenderThemeSafari.
668 const float baseFontSize = 11.0f;
669 const int baseBorderRadius = 5;
670 float fontScale = style->fontSize() / baseFontSize;
671
672 style->resetPadding();
673 style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
674
675 const int minHeight = 15;
676 style->setMinHeight(Length(minHeight, Fixed));
677
678 style->setLineHeight(RenderStyle::initialLineHeight());
679 // Found these padding numbers by trial and error.
680 const int padding = 4;
681 style->setPaddingTop(Length(padding, Fixed));
682 style->setPaddingLeft(Length(padding, Fixed));
683 adjustMenuListStyleCommon(style);
684 }
685
paintMenuListButton(RenderObject * obj,const PaintInfo & info,const IntRect & rect)686 bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
687 {
688 return paintCombo(obj, info, rect);
689 }
690
paintSliderTrack(RenderObject * o,const PaintInfo & i,const IntRect & r)691 bool RenderThemeAndroid::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
692 {
693 static const bool translucent = true;
694 i.context->platformContext()->drawMediaButton(r,
695 RenderSkinMediaButton::SLIDER_TRACK,
696 translucent, false);
697 return false;
698 }
699
paintSliderThumb(RenderObject * o,const PaintInfo & i,const IntRect & r)700 bool RenderThemeAndroid::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
701 {
702 static const bool translucent = true;
703 i.context->platformContext()->drawMediaButton(r,
704 RenderSkinMediaButton::SLIDER_THUMB,
705 translucent, false);
706 return false;
707 }
708
platformFocusRingColor() const709 Color RenderThemeAndroid::platformFocusRingColor() const
710 {
711 static Color focusRingColor(0x33, 0xB5, 0xE5, 0x66);
712 return focusRingColor;
713 }
714
supportsFocusRing(const RenderStyle * style) const715 bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const
716 {
717 // Draw the focus ring ourselves unless it is a text area (webkit does borders better)
718 if (!style || !style->hasAppearance())
719 return true;
720 return style->appearance() != TextFieldPart && style->appearance() != TextAreaPart;
721 }
722
723 } // namespace WebCore
724