[Android WebViewShell] Make WebViewLayoutTest runnable with test_runner.py
[chromium-blink-merge.git] / ui / native_theme / native_theme_base.cc
blob878882ef54acf0365af6169b410a2de175672b72
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/native_theme/native_theme_base.h"
7 #include <limits>
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "third_party/skia/include/effects/SkGradientShader.h"
13 #include "ui/base/layout.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/base/ui_base_switches.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/color_utils.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/gfx/geometry/size.h"
20 #include "ui/gfx/image/image_skia.h"
21 #include "ui/gfx/skia_util.h"
22 #include "ui/native_theme/common_theme.h"
23 #include "ui/resources/grit/ui_resources.h"
25 namespace {
27 // These are the default dimensions of radio buttons and checkboxes.
28 const int kCheckboxAndRadioWidth = 13;
29 const int kCheckboxAndRadioHeight = 13;
31 // These sizes match the sizes in Chromium Win.
32 const int kSliderThumbWidth = 11;
33 const int kSliderThumbHeight = 21;
35 const SkColor kSliderTrackBackgroundColor =
36 SkColorSetRGB(0xe3, 0xdd, 0xd8);
37 const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
38 const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0);
39 const SkColor kSliderThumbBorderDarkGrey =
40 SkColorSetRGB(0x9d, 0x96, 0x8e);
42 const SkColor kTextBorderColor = SkColorSetRGB(0xa9, 0xa9, 0xa9);
44 const SkColor kMenuPopupBackgroundColor = SkColorSetRGB(210, 225, 246);
46 const unsigned int kDefaultScrollbarWidth = 15;
47 const unsigned int kDefaultScrollbarButtonLength = 14;
49 const SkColor kCheckboxTinyColor = SK_ColorGRAY;
50 const SkColor kCheckboxShadowColor = SkColorSetARGB(0x15, 0, 0, 0);
51 const SkColor kCheckboxShadowHoveredColor = SkColorSetARGB(0x1F, 0, 0, 0);
52 const SkColor kCheckboxShadowDisabledColor = SkColorSetARGB(0, 0, 0, 0);
53 const SkColor kCheckboxGradientColors[] = {
54 SkColorSetRGB(0xed, 0xed, 0xed),
55 SkColorSetRGB(0xde, 0xde, 0xde) };
56 const SkColor kCheckboxGradientPressedColors[] = {
57 SkColorSetRGB(0xe7, 0xe7, 0xe7),
58 SkColorSetRGB(0xd7, 0xd7, 0xd7) };
59 const SkColor kCheckboxGradientHoveredColors[] = {
60 SkColorSetRGB(0xf0, 0xf0, 0xf0),
61 SkColorSetRGB(0xe0, 0xe0, 0xe0) };
62 const SkColor kCheckboxGradientDisabledColors[] = {
63 SkColorSetARGB(0x80, 0xed, 0xed, 0xed),
64 SkColorSetARGB(0x80, 0xde, 0xde, 0xde) };
65 const SkColor kCheckboxBorderColor = SkColorSetARGB(0x40, 0, 0, 0);
66 const SkColor kCheckboxBorderHoveredColor = SkColorSetARGB(0x4D, 0, 0, 0);
67 const SkColor kCheckboxBorderDisabledColor = SkColorSetARGB(0x20, 0, 0, 0);
68 const SkColor kCheckboxStrokeColor = SkColorSetARGB(0xB3, 0, 0, 0);
69 const SkColor kCheckboxStrokeDisabledColor = SkColorSetARGB(0x59, 0, 0, 0);
70 const SkColor kRadioDotColor = SkColorSetRGB(0x66, 0x66, 0x66);
71 const SkColor kRadioDotDisabledColor = SkColorSetARGB(0x80, 0x66, 0x66, 0x66);
73 // Get lightness adjusted color.
74 SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
75 double lightness_amount) {
76 color_utils::HSL adjusted = hsl;
77 adjusted.l += lightness_amount;
78 if (adjusted.l > 1.0)
79 adjusted.l = 1.0;
80 if (adjusted.l < 0.0)
81 adjusted.l = 0.0;
83 return color_utils::HSLToSkColor(adjusted, alpha);
86 } // namespace
88 namespace ui {
90 gfx::Size NativeThemeBase::GetPartSize(Part part,
91 State state,
92 const ExtraParams& extra) const {
93 gfx::Size size = CommonThemeGetPartSize(part, state, extra);
94 if (!size.IsEmpty())
95 return size;
97 switch (part) {
98 // Please keep these in the order of NativeTheme::Part.
99 case kCheckbox:
100 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
101 case kInnerSpinButton:
102 return gfx::Size(scrollbar_width_, 0);
103 case kMenuList:
104 return gfx::Size(); // No default size.
105 case kMenuCheck:
106 case kMenuCheckBackground:
107 case kMenuPopupArrow:
108 NOTIMPLEMENTED();
109 break;
110 case kMenuPopupBackground:
111 return gfx::Size(); // No default size.
112 case kMenuPopupGutter:
113 case kMenuPopupSeparator:
114 NOTIMPLEMENTED();
115 break;
116 case kMenuItemBackground:
117 case kProgressBar:
118 case kPushButton:
119 return gfx::Size(); // No default size.
120 case kRadio:
121 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
122 case kScrollbarDownArrow:
123 case kScrollbarUpArrow:
124 return gfx::Size(scrollbar_width_, scrollbar_button_length_);
125 case kScrollbarLeftArrow:
126 case kScrollbarRightArrow:
127 return gfx::Size(scrollbar_button_length_, scrollbar_width_);
128 case kScrollbarHorizontalThumb:
129 // This matches Firefox on Linux.
130 return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
131 case kScrollbarVerticalThumb:
132 // This matches Firefox on Linux.
133 return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
134 case kScrollbarHorizontalTrack:
135 return gfx::Size(0, scrollbar_width_);
136 case kScrollbarVerticalTrack:
137 return gfx::Size(scrollbar_width_, 0);
138 case kScrollbarHorizontalGripper:
139 case kScrollbarVerticalGripper:
140 NOTIMPLEMENTED();
141 break;
142 case kSliderTrack:
143 return gfx::Size(); // No default size.
144 case kSliderThumb:
145 // These sizes match the sizes in Chromium Win.
146 return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
147 case kTabPanelBackground:
148 NOTIMPLEMENTED();
149 break;
150 case kTextField:
151 return gfx::Size(); // No default size.
152 case kTrackbarThumb:
153 case kTrackbarTrack:
154 case kWindowResizeGripper:
155 NOTIMPLEMENTED();
156 break;
157 default:
158 NOTREACHED() << "Unknown theme part: " << part;
159 break;
161 return gfx::Size();
164 void NativeThemeBase::PaintStateTransition(SkCanvas* canvas,
165 Part part,
166 State startState,
167 State endState,
168 double progress,
169 const gfx::Rect& rect) const {
170 if (rect.IsEmpty())
171 return;
173 // Currently state transition is animation only working for overlay scrollbars
174 // on Aura platforms.
175 switch (part) {
176 case kScrollbarHorizontalThumb:
177 case kScrollbarVerticalThumb:
178 PaintScrollbarThumbStateTransition(
179 canvas, startState, endState, progress, rect);
180 break;
181 default:
182 NOTREACHED() << "Does not support state transition for this part:"
183 << part;
184 break;
186 return;
189 void NativeThemeBase::Paint(SkCanvas* canvas,
190 Part part,
191 State state,
192 const gfx::Rect& rect,
193 const ExtraParams& extra) const {
194 if (rect.IsEmpty())
195 return;
197 switch (part) {
198 // Please keep these in the order of NativeTheme::Part.
199 case kComboboxArrow:
200 CommonThemePaintComboboxArrow(canvas, rect);
201 break;
202 case kCheckbox:
203 PaintCheckbox(canvas, state, rect, extra.button);
204 break;
205 case kInnerSpinButton:
206 PaintInnerSpinButton(canvas, state, rect, extra.inner_spin);
207 break;
208 case kMenuList:
209 PaintMenuList(canvas, state, rect, extra.menu_list);
210 break;
211 case kMenuCheck:
212 case kMenuCheckBackground:
213 case kMenuPopupArrow:
214 NOTIMPLEMENTED();
215 break;
216 case kMenuPopupBackground:
217 PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background);
218 break;
219 case kMenuPopupGutter:
220 case kMenuPopupSeparator:
221 NOTIMPLEMENTED();
222 break;
223 case kMenuItemBackground:
224 PaintMenuItemBackground(canvas, state, rect, extra.menu_list);
225 break;
226 case kProgressBar:
227 PaintProgressBar(canvas, state, rect, extra.progress_bar);
228 break;
229 case kPushButton:
230 PaintButton(canvas, state, rect, extra.button);
231 break;
232 case kRadio:
233 PaintRadio(canvas, state, rect, extra.button);
234 break;
235 case kScrollbarDownArrow:
236 case kScrollbarUpArrow:
237 case kScrollbarLeftArrow:
238 case kScrollbarRightArrow:
239 if (scrollbar_button_length_ > 0)
240 PaintArrowButton(canvas, rect, part, state);
241 break;
242 case kScrollbarHorizontalThumb:
243 case kScrollbarVerticalThumb:
244 PaintScrollbarThumb(canvas, part, state, rect);
245 break;
246 case kScrollbarHorizontalTrack:
247 case kScrollbarVerticalTrack:
248 PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect);
249 break;
250 case kScrollbarHorizontalGripper:
251 case kScrollbarVerticalGripper:
252 // Invoked by views scrollbar code, don't care about for non-win
253 // implementations, so no NOTIMPLEMENTED.
254 break;
255 case kScrollbarCorner:
256 PaintScrollbarCorner(canvas, state, rect);
257 break;
258 case kSliderTrack:
259 PaintSliderTrack(canvas, state, rect, extra.slider);
260 break;
261 case kSliderThumb:
262 PaintSliderThumb(canvas, state, rect, extra.slider);
263 break;
264 case kTabPanelBackground:
265 NOTIMPLEMENTED();
266 break;
267 case kTextField:
268 PaintTextField(canvas, state, rect, extra.text_field);
269 break;
270 case kTrackbarThumb:
271 case kTrackbarTrack:
272 case kWindowResizeGripper:
273 NOTIMPLEMENTED();
274 break;
275 default:
276 NOTREACHED() << "Unknown theme part: " << part;
277 break;
281 NativeThemeBase::NativeThemeBase()
282 : scrollbar_width_(kDefaultScrollbarWidth),
283 scrollbar_button_length_(kDefaultScrollbarButtonLength) {
286 NativeThemeBase::~NativeThemeBase() {
289 void NativeThemeBase::PaintArrowButton(
290 SkCanvas* canvas,
291 const gfx::Rect& rect, Part direction, State state) const {
292 SkPaint paint;
294 // Calculate button color.
295 SkScalar trackHSV[3];
296 SkColorToHSV(track_color_, trackHSV);
297 SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f);
298 SkColor backgroundColor = buttonColor;
299 if (state == kPressed) {
300 SkScalar buttonHSV[3];
301 SkColorToHSV(buttonColor, buttonHSV);
302 buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1f);
303 } else if (state == kHovered) {
304 SkScalar buttonHSV[3];
305 SkColorToHSV(buttonColor, buttonHSV);
306 buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05f);
309 SkIRect skrect;
310 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y()
311 + rect.height());
312 // Paint the background (the area visible behind the rounded corners).
313 paint.setColor(backgroundColor);
314 canvas->drawIRect(skrect, paint);
316 // Paint the button's outline and fill the middle
317 SkPath outline;
318 switch (direction) {
319 case kScrollbarUpArrow:
320 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
321 outline.rLineTo(0, -(rect.height() - 2));
322 outline.rLineTo(2, -2);
323 outline.rLineTo(rect.width() - 5, 0);
324 outline.rLineTo(2, 2);
325 outline.rLineTo(0, rect.height() - 2);
326 break;
327 case kScrollbarDownArrow:
328 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
329 outline.rLineTo(0, rect.height() - 2);
330 outline.rLineTo(2, 2);
331 outline.rLineTo(rect.width() - 5, 0);
332 outline.rLineTo(2, -2);
333 outline.rLineTo(0, -(rect.height() - 2));
334 break;
335 case kScrollbarRightArrow:
336 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
337 outline.rLineTo(rect.width() - 2, 0);
338 outline.rLineTo(2, 2);
339 outline.rLineTo(0, rect.height() - 5);
340 outline.rLineTo(-2, 2);
341 outline.rLineTo(-(rect.width() - 2), 0);
342 break;
343 case kScrollbarLeftArrow:
344 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
345 outline.rLineTo(-(rect.width() - 2), 0);
346 outline.rLineTo(-2, 2);
347 outline.rLineTo(0, rect.height() - 5);
348 outline.rLineTo(2, 2);
349 outline.rLineTo(rect.width() - 2, 0);
350 break;
351 default:
352 break;
354 outline.close();
356 paint.setStyle(SkPaint::kFill_Style);
357 paint.setColor(buttonColor);
358 canvas->drawPath(outline, paint);
360 paint.setAntiAlias(true);
361 paint.setStyle(SkPaint::kStroke_Style);
362 SkScalar thumbHSV[3];
363 SkColorToHSV(thumb_inactive_color_, thumbHSV);
364 paint.setColor(OutlineColor(trackHSV, thumbHSV));
365 canvas->drawPath(outline, paint);
367 PaintArrow(canvas, rect, direction, GetArrowColor(state));
370 void NativeThemeBase::PaintArrow(SkCanvas* gc,
371 const gfx::Rect& rect,
372 Part direction,
373 SkColor color) const {
374 int width_middle, length_middle;
375 if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
376 width_middle = rect.width() / 2 + 1;
377 length_middle = rect.height() / 2 + 1;
378 } else {
379 length_middle = rect.width() / 2 + 1;
380 width_middle = rect.height() / 2 + 1;
383 SkPaint paint;
384 paint.setColor(color);
385 paint.setAntiAlias(false);
386 paint.setStyle(SkPaint::kFill_Style);
388 SkPath path;
389 // The constants in this block of code are hand-tailored to produce good
390 // looking arrows without anti-aliasing.
391 switch (direction) {
392 case kScrollbarUpArrow:
393 path.moveTo(rect.x() + width_middle - 4, rect.y() + length_middle + 2);
394 path.rLineTo(7, 0);
395 path.rLineTo(-4, -4);
396 break;
397 case kScrollbarDownArrow:
398 path.moveTo(rect.x() + width_middle - 4, rect.y() + length_middle - 3);
399 path.rLineTo(7, 0);
400 path.rLineTo(-4, 4);
401 break;
402 case kScrollbarRightArrow:
403 path.moveTo(rect.x() + length_middle - 3, rect.y() + width_middle - 4);
404 path.rLineTo(0, 7);
405 path.rLineTo(4, -4);
406 break;
407 case kScrollbarLeftArrow:
408 path.moveTo(rect.x() + length_middle + 1, rect.y() + width_middle - 5);
409 path.rLineTo(0, 9);
410 path.rLineTo(-4, -4);
411 break;
412 default:
413 break;
415 path.close();
417 gc->drawPath(path, paint);
420 void NativeThemeBase::PaintScrollbarTrack(SkCanvas* canvas,
421 Part part,
422 State state,
423 const ScrollbarTrackExtraParams& extra_params,
424 const gfx::Rect& rect) const {
425 SkPaint paint;
426 SkIRect skrect;
428 skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
429 SkScalar track_hsv[3];
430 SkColorToHSV(track_color_, track_hsv);
431 paint.setColor(SaturateAndBrighten(track_hsv, 0, 0));
432 canvas->drawIRect(skrect, paint);
434 SkScalar thumb_hsv[3];
435 SkColorToHSV(thumb_inactive_color_, thumb_hsv);
437 paint.setColor(OutlineColor(track_hsv, thumb_hsv));
438 DrawBox(canvas, rect, paint);
441 void NativeThemeBase::PaintScrollbarThumb(SkCanvas* canvas,
442 Part part,
443 State state,
444 const gfx::Rect& rect) const {
445 const bool hovered = state == kHovered;
446 const int midx = rect.x() + rect.width() / 2;
447 const int midy = rect.y() + rect.height() / 2;
448 const bool vertical = part == kScrollbarVerticalThumb;
450 SkScalar thumb[3];
451 SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
453 SkPaint paint;
454 paint.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
456 SkIRect skrect;
457 if (vertical)
458 skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
459 else
460 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
462 canvas->drawIRect(skrect, paint);
464 paint.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
466 if (vertical) {
467 skrect.set(
468 midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
469 } else {
470 skrect.set(
471 rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height());
474 canvas->drawIRect(skrect, paint);
476 SkScalar track[3];
477 SkColorToHSV(track_color_, track);
478 paint.setColor(OutlineColor(track, thumb));
479 DrawBox(canvas, rect, paint);
481 if (rect.height() > 10 && rect.width() > 10) {
482 const int grippy_half_width = 2;
483 const int inter_grippy_offset = 3;
484 if (vertical) {
485 DrawHorizLine(canvas,
486 midx - grippy_half_width,
487 midx + grippy_half_width,
488 midy - inter_grippy_offset,
489 paint);
490 DrawHorizLine(canvas,
491 midx - grippy_half_width,
492 midx + grippy_half_width,
493 midy,
494 paint);
495 DrawHorizLine(canvas,
496 midx - grippy_half_width,
497 midx + grippy_half_width,
498 midy + inter_grippy_offset,
499 paint);
500 } else {
501 DrawVertLine(canvas,
502 midx - inter_grippy_offset,
503 midy - grippy_half_width,
504 midy + grippy_half_width,
505 paint);
506 DrawVertLine(canvas,
507 midx,
508 midy - grippy_half_width,
509 midy + grippy_half_width,
510 paint);
511 DrawVertLine(canvas,
512 midx + inter_grippy_offset,
513 midy - grippy_half_width,
514 midy + grippy_half_width,
515 paint);
520 void NativeThemeBase::PaintScrollbarCorner(SkCanvas* canvas,
521 State state,
522 const gfx::Rect& rect) const {
525 void NativeThemeBase::PaintCheckbox(SkCanvas* canvas,
526 State state,
527 const gfx::Rect& rect,
528 const ButtonExtraParams& button) const {
529 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect,
530 SkIntToScalar(2));
531 if (!skrect.isEmpty()) {
532 // Draw the checkmark / dash.
533 SkPaint paint;
534 paint.setAntiAlias(true);
535 paint.setStyle(SkPaint::kStroke_Style);
536 if (state == kDisabled)
537 paint.setColor(kCheckboxStrokeDisabledColor);
538 else
539 paint.setColor(kCheckboxStrokeColor);
540 if (button.indeterminate) {
541 SkPath dash;
542 dash.moveTo(skrect.x() + skrect.width() * 0.16,
543 (skrect.y() + skrect.bottom()) / 2);
544 dash.rLineTo(skrect.width() * 0.68, 0);
545 paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
546 canvas->drawPath(dash, paint);
547 } else if (button.checked) {
548 SkPath check;
549 check.moveTo(skrect.x() + skrect.width() * 0.2,
550 skrect.y() + skrect.height() * 0.5);
551 check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
552 paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
553 check.lineTo(skrect.right() - skrect.width() * 0.2,
554 skrect.y() + skrect.height() * 0.2);
555 canvas->drawPath(check, paint);
560 // Draws the common elements of checkboxes and radio buttons.
561 // Returns the rectangle within which any additional decorations should be
562 // drawn, or empty if none.
563 SkRect NativeThemeBase::PaintCheckboxRadioCommon(
564 SkCanvas* canvas,
565 State state,
566 const gfx::Rect& rect,
567 const SkScalar borderRadius) const {
569 SkRect skrect = gfx::RectToSkRect(rect);
571 // Use the largest square that fits inside the provided rectangle.
572 // No other browser seems to support non-square widget, so accidentally
573 // having non-square sizes is common (eg. amazon and webkit dev tools).
574 if (skrect.width() != skrect.height()) {
575 SkScalar size = SkMinScalar(skrect.width(), skrect.height());
576 skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
579 // If the rectangle is too small then paint only a rectangle. We don't want
580 // to have to worry about '- 1' and '+ 1' calculations below having overflow
581 // or underflow.
582 if (skrect.width() <= 2) {
583 SkPaint paint;
584 paint.setColor(kCheckboxTinyColor);
585 paint.setStyle(SkPaint::kFill_Style);
586 canvas->drawRect(skrect, paint);
587 // Too small to draw anything more.
588 return SkRect::MakeEmpty();
591 // Make room for the drop shadow.
592 skrect.iset(skrect.x(), skrect.y(), skrect.right() - 1, skrect.bottom() - 1);
594 // Draw the drop shadow below the widget.
595 if (state != kPressed) {
596 SkPaint paint;
597 paint.setAntiAlias(true);
598 SkRect shadowRect = skrect;
599 shadowRect.offset(0, 1);
600 if (state == kDisabled)
601 paint.setColor(kCheckboxShadowDisabledColor);
602 else if (state == kHovered)
603 paint.setColor(kCheckboxShadowHoveredColor);
604 else
605 paint.setColor(kCheckboxShadowColor);
606 paint.setStyle(SkPaint::kFill_Style);
607 canvas->drawRoundRect(shadowRect, borderRadius, borderRadius, paint);
610 // Draw the gradient-filled rectangle
611 SkPoint gradient_bounds[3];
612 gradient_bounds[0].set(skrect.x(), skrect.y());
613 gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
614 gradient_bounds[2].set(skrect.x(), skrect.bottom());
615 const SkColor* startEndColors;
616 if (state == kPressed)
617 startEndColors = kCheckboxGradientPressedColors;
618 else if (state == kHovered)
619 startEndColors = kCheckboxGradientHoveredColors;
620 else if (state == kDisabled)
621 startEndColors = kCheckboxGradientDisabledColors;
622 else /* kNormal */
623 startEndColors = kCheckboxGradientColors;
624 SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]};
625 skia::RefPtr<SkShader> shader = skia::AdoptRef(
626 SkGradientShader::CreateLinear(
627 gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode));
628 SkPaint paint;
629 paint.setAntiAlias(true);
630 paint.setShader(shader.get());
631 paint.setStyle(SkPaint::kFill_Style);
632 canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
633 paint.setShader(NULL);
635 // Draw the border.
636 if (state == kHovered)
637 paint.setColor(kCheckboxBorderHoveredColor);
638 else if (state == kDisabled)
639 paint.setColor(kCheckboxBorderDisabledColor);
640 else
641 paint.setColor(kCheckboxBorderColor);
642 paint.setStyle(SkPaint::kStroke_Style);
643 paint.setStrokeWidth(SkIntToScalar(1));
644 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
645 canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
647 // Return the rectangle excluding the drop shadow for drawing any additional
648 // decorations.
649 return skrect;
652 void NativeThemeBase::PaintRadio(SkCanvas* canvas,
653 State state,
654 const gfx::Rect& rect,
655 const ButtonExtraParams& button) const {
657 // Most of a radio button is the same as a checkbox, except the the rounded
658 // square is a circle (i.e. border radius >= 100%).
659 const SkScalar radius = SkFloatToScalar(
660 static_cast<float>(std::max(rect.width(), rect.height())) / 2);
661 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, radius);
662 if (!skrect.isEmpty() && button.checked) {
663 // Draw the dot.
664 SkPaint paint;
665 paint.setAntiAlias(true);
666 paint.setStyle(SkPaint::kFill_Style);
667 if (state == kDisabled)
668 paint.setColor(kRadioDotDisabledColor);
669 else
670 paint.setColor(kRadioDotColor);
671 skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
672 // Use drawRoundedRect instead of drawOval to be completely consistent
673 // with the border in PaintCheckboxRadioNewCommon.
674 canvas->drawRoundRect(skrect, radius, radius, paint);
678 void NativeThemeBase::PaintButton(SkCanvas* canvas,
679 State state,
680 const gfx::Rect& rect,
681 const ButtonExtraParams& button) const {
682 SkPaint paint;
683 const int kRight = rect.right();
684 const int kBottom = rect.bottom();
685 SkRect skrect = SkRect::MakeLTRB(rect.x(), rect.y(), kRight, kBottom);
686 SkColor base_color = button.background_color;
688 color_utils::HSL base_hsl;
689 color_utils::SkColorToHSL(base_color, &base_hsl);
691 // Our standard gradient is from 0xdd to 0xf8. This is the amount of
692 // increased luminance between those values.
693 SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
695 // If the button is too small, fallback to drawing a single, solid color
696 if (rect.width() < 5 || rect.height() < 5) {
697 paint.setColor(base_color);
698 canvas->drawRect(skrect, paint);
699 return;
702 paint.setColor(SK_ColorBLACK);
703 const int kLightEnd = state == kPressed ? 1 : 0;
704 const int kDarkEnd = !kLightEnd;
705 SkPoint gradient_bounds[2];
706 gradient_bounds[kLightEnd].iset(rect.x(), rect.y());
707 gradient_bounds[kDarkEnd].iset(rect.x(), kBottom - 1);
708 SkColor colors[2];
709 colors[0] = light_color;
710 colors[1] = base_color;
712 skia::RefPtr<SkShader> shader = skia::AdoptRef(
713 SkGradientShader::CreateLinear(
714 gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode));
715 paint.setStyle(SkPaint::kFill_Style);
716 paint.setAntiAlias(true);
717 paint.setShader(shader.get());
719 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
720 paint.setShader(NULL);
722 if (button.has_border) {
723 int border_alpha = state == kHovered ? 0x80 : 0x55;
724 if (button.is_focused) {
725 border_alpha = 0xff;
726 paint.setColor(GetSystemColor(kColorId_FocusedBorderColor));
728 paint.setStyle(SkPaint::kStroke_Style);
729 paint.setStrokeWidth(SkIntToScalar(1));
730 paint.setAlpha(border_alpha);
731 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
732 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
736 void NativeThemeBase::PaintTextField(SkCanvas* canvas,
737 State state,
738 const gfx::Rect& rect,
739 const TextFieldExtraParams& text) const {
740 SkRect bounds;
741 bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
743 SkPaint fill_paint;
744 fill_paint.setStyle(SkPaint::kFill_Style);
745 fill_paint.setColor(text.background_color);
746 canvas->drawRect(bounds, fill_paint);
748 // Text INPUT, listbox SELECT, and TEXTAREA have consistent borders.
749 // border: 1px solid #a9a9a9
750 SkPaint stroke_paint;
751 stroke_paint.setStyle(SkPaint::kStroke_Style);
752 stroke_paint.setColor(kTextBorderColor);
753 canvas->drawRect(bounds, stroke_paint);
756 void NativeThemeBase::PaintMenuList(
757 SkCanvas* canvas,
758 State state,
759 const gfx::Rect& rect,
760 const MenuListExtraParams& menu_list) const {
761 // If a border radius is specified, we let the WebCore paint the background
762 // and the border of the control.
763 if (!menu_list.has_border_radius) {
764 ButtonExtraParams button = { 0 };
765 button.background_color = menu_list.background_color;
766 button.has_border = menu_list.has_border;
767 PaintButton(canvas, state, rect, button);
770 SkPaint paint;
771 paint.setColor(SK_ColorBLACK);
772 paint.setAntiAlias(true);
773 paint.setStyle(SkPaint::kFill_Style);
775 static const int kArrowWidth = 6;
776 static const int kArrowHeight = 6;
778 gfx::Rect arrow(
779 menu_list.arrow_x,
780 menu_list.arrow_y - (kArrowHeight / 2),
781 kArrowWidth,
782 kArrowHeight);
784 // Constrain to the paint rect.
785 arrow.Intersect(rect);
787 SkPath path;
788 path.moveTo(arrow.x(), arrow.y());
789 path.lineTo(arrow.right(), arrow.y());
790 path.lineTo(arrow.x() + arrow.width() / 2, arrow.bottom());
791 path.close();
792 canvas->drawPath(path, paint);
795 void NativeThemeBase::PaintMenuPopupBackground(
796 SkCanvas* canvas,
797 const gfx::Size& size,
798 const MenuBackgroundExtraParams& menu_background) const {
799 canvas->drawColor(kMenuPopupBackgroundColor, SkXfermode::kSrc_Mode);
802 void NativeThemeBase::PaintMenuItemBackground(
803 SkCanvas* canvas,
804 State state,
805 const gfx::Rect& rect,
806 const MenuListExtraParams& menu_list) const {
807 // By default don't draw anything over the normal background.
810 void NativeThemeBase::PaintSliderTrack(SkCanvas* canvas,
811 State state,
812 const gfx::Rect& rect,
813 const SliderExtraParams& slider) const {
814 const int kMidX = rect.x() + rect.width() / 2;
815 const int kMidY = rect.y() + rect.height() / 2;
817 SkPaint paint;
818 paint.setColor(kSliderTrackBackgroundColor);
820 SkRect skrect;
821 if (slider.vertical) {
822 skrect.set(std::max(rect.x(), kMidX - 2),
823 rect.y(),
824 std::min(rect.right(), kMidX + 2),
825 rect.bottom());
826 } else {
827 skrect.set(rect.x(),
828 std::max(rect.y(), kMidY - 2),
829 rect.right(),
830 std::min(rect.bottom(), kMidY + 2));
832 canvas->drawRect(skrect, paint);
835 void NativeThemeBase::PaintSliderThumb(SkCanvas* canvas,
836 State state,
837 const gfx::Rect& rect,
838 const SliderExtraParams& slider) const {
839 const bool hovered = (state == kHovered) || slider.in_drag;
840 const int kMidX = rect.x() + rect.width() / 2;
841 const int kMidY = rect.y() + rect.height() / 2;
843 SkPaint paint;
844 paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
846 SkIRect skrect;
847 if (slider.vertical)
848 skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
849 else
850 skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
852 canvas->drawIRect(skrect, paint);
854 paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
856 if (slider.vertical)
857 skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
858 else
859 skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
861 canvas->drawIRect(skrect, paint);
863 paint.setColor(kSliderThumbBorderDarkGrey);
864 DrawBox(canvas, rect, paint);
866 if (rect.height() > 10 && rect.width() > 10) {
867 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
868 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
869 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
873 void NativeThemeBase::PaintInnerSpinButton(SkCanvas* canvas,
874 State state,
875 const gfx::Rect& rect,
876 const InnerSpinButtonExtraParams& spin_button) const {
877 if (spin_button.read_only)
878 state = kDisabled;
880 State north_state = state;
881 State south_state = state;
882 if (spin_button.spin_up)
883 south_state = south_state != kDisabled ? kNormal : kDisabled;
884 else
885 north_state = north_state != kDisabled ? kNormal : kDisabled;
887 gfx::Rect half = rect;
888 half.set_height(rect.height() / 2);
889 PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
891 half.set_y(rect.y() + rect.height() / 2);
892 PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
895 void NativeThemeBase::PaintProgressBar(SkCanvas* canvas,
896 State state,
897 const gfx::Rect& rect,
898 const ProgressBarExtraParams& progress_bar) const {
899 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
900 gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR);
901 gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed(
902 IDR_PROGRESS_BORDER_LEFT);
903 gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed(
904 IDR_PROGRESS_BORDER_RIGHT);
906 DCHECK(bar_image->width() > 0);
907 DCHECK(rect.width() > 0);
909 float tile_scale_y = static_cast<float>(rect.height()) / bar_image->height();
911 int dest_left_border_width = left_border_image->width();
912 int dest_right_border_width = right_border_image->width();
914 // Since an implicit float -> int conversion will truncate, we want to make
915 // sure that if a border is desired, it gets at least one pixel.
916 if (dest_left_border_width > 0) {
917 dest_left_border_width = dest_left_border_width * tile_scale_y;
918 dest_left_border_width = std::max(dest_left_border_width, 1);
920 if (dest_right_border_width > 0) {
921 dest_right_border_width = dest_right_border_width * tile_scale_y;
922 dest_right_border_width = std::max(dest_right_border_width, 1);
925 // Since the width of the progress bar may not be evenly divisible by the
926 // tile size, in order to make it look right we may need to draw some of the
927 // with a width of 1 pixel smaller than the rest of the tiles.
928 int new_tile_width = static_cast<int>(bar_image->width() * tile_scale_y);
929 new_tile_width = std::max(new_tile_width, 1);
931 float tile_scale_x = static_cast<float>(new_tile_width) / bar_image->width();
932 if (rect.width() % new_tile_width == 0) {
933 DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale_y,
934 rect.x(), rect.y(),
935 rect.width(), rect.height());
936 } else {
937 int num_tiles = 1 + rect.width() / new_tile_width;
938 int overshoot = num_tiles * new_tile_width - rect.width();
939 // Since |overshoot| represents the number of tiles that were too big, draw
940 // |overshoot| tiles with their width reduced by 1.
941 int num_big_tiles = num_tiles - overshoot;
942 int num_small_tiles = overshoot;
943 int small_width = new_tile_width - 1;
944 float small_scale_x = static_cast<float>(small_width) / bar_image->width();
945 float big_scale_x = tile_scale_x;
947 gfx::Rect big_rect = rect;
948 gfx::Rect small_rect = rect;
949 big_rect.Inset(0, 0, num_small_tiles*small_width, 0);
950 small_rect.Inset(num_big_tiles*new_tile_width, 0, 0, 0);
952 DrawTiledImage(canvas, *bar_image, 0, 0, big_scale_x, tile_scale_y,
953 big_rect.x(), big_rect.y(), big_rect.width(), big_rect.height());
954 DrawTiledImage(canvas, *bar_image, 0, 0, small_scale_x, tile_scale_y,
955 small_rect.x(), small_rect.y(), small_rect.width(), small_rect.height());
957 if (progress_bar.value_rect_width) {
958 gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE);
960 new_tile_width = static_cast<int>(value_image->width() * tile_scale_y);
961 tile_scale_x = static_cast<float>(new_tile_width) /
962 value_image->width();
964 DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale_y,
965 progress_bar.value_rect_x,
966 progress_bar.value_rect_y,
967 progress_bar.value_rect_width,
968 progress_bar.value_rect_height);
971 DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(),
972 left_border_image->height(), rect.x(), rect.y(), dest_left_border_width,
973 rect.height());
975 int dest_x = rect.right() - dest_right_border_width;
976 DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(),
977 right_border_image->height(), dest_x, rect.y(),
978 dest_right_border_width, rect.height());
981 bool NativeThemeBase::IntersectsClipRectInt(SkCanvas* canvas,
982 int x, int y, int w, int h) const {
983 SkRect clip;
984 return canvas->getClipBounds(&clip) &&
985 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
986 SkIntToScalar(y + h));
989 void NativeThemeBase::DrawImageInt(
990 SkCanvas* sk_canvas, const gfx::ImageSkia& image,
991 int src_x, int src_y, int src_w, int src_h,
992 int dest_x, int dest_y, int dest_w, int dest_h) const {
993 scoped_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
994 canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
995 dest_x, dest_y, dest_w, dest_h, true);
998 void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
999 const gfx::ImageSkia& image,
1000 int src_x, int src_y, float tile_scale_x, float tile_scale_y,
1001 int dest_x, int dest_y, int w, int h) const {
1002 scoped_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
1003 canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
1004 tile_scale_y, dest_x, dest_y, w, h);
1007 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
1008 SkScalar saturate_amount,
1009 SkScalar brighten_amount) const {
1010 SkScalar color[3];
1011 color[0] = hsv[0];
1012 color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
1013 color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
1014 return SkHSVToColor(color);
1017 SkColor NativeThemeBase::GetArrowColor(State state) const {
1018 if (state != kDisabled)
1019 return SK_ColorBLACK;
1021 SkScalar track_hsv[3];
1022 SkColorToHSV(track_color_, track_hsv);
1023 SkScalar thumb_hsv[3];
1024 SkColorToHSV(thumb_inactive_color_, thumb_hsv);
1025 return OutlineColor(track_hsv, thumb_hsv);
1028 void NativeThemeBase::DrawVertLine(SkCanvas* canvas,
1029 int x,
1030 int y1,
1031 int y2,
1032 const SkPaint& paint) const {
1033 SkIRect skrect;
1034 skrect.set(x, y1, x + 1, y2 + 1);
1035 canvas->drawIRect(skrect, paint);
1038 void NativeThemeBase::DrawHorizLine(SkCanvas* canvas,
1039 int x1,
1040 int x2,
1041 int y,
1042 const SkPaint& paint) const {
1043 SkIRect skrect;
1044 skrect.set(x1, y, x2 + 1, y + 1);
1045 canvas->drawIRect(skrect, paint);
1048 void NativeThemeBase::DrawBox(SkCanvas* canvas,
1049 const gfx::Rect& rect,
1050 const SkPaint& paint) const {
1051 const int right = rect.x() + rect.width() - 1;
1052 const int bottom = rect.y() + rect.height() - 1;
1053 DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
1054 DrawVertLine(canvas, right, rect.y(), bottom, paint);
1055 DrawHorizLine(canvas, rect.x(), right, bottom, paint);
1056 DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
1059 SkScalar NativeThemeBase::Clamp(SkScalar value,
1060 SkScalar min,
1061 SkScalar max) const {
1062 return std::min(std::max(value, min), max);
1065 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
1066 // GTK Theme engines have way too much control over the layout of
1067 // the scrollbar. We might be able to more closely approximate its
1068 // look-and-feel, if we sent whole images instead of just colors
1069 // from the browser to the renderer. But even then, some themes
1070 // would just break.
1072 // So, instead, we don't even try to 100% replicate the look of
1073 // the native scrollbar. We render our own version, but we make
1074 // sure to pick colors that blend in nicely with the system GTK
1075 // theme. In most cases, we can just sample a couple of pixels
1076 // from the system scrollbar and use those colors to draw our
1077 // scrollbar.
1079 // This works fine for the track color and the overall thumb
1080 // color. But it fails spectacularly for the outline color used
1081 // around the thumb piece. Not all themes have a clearly defined
1082 // outline. For some of them it is partially transparent, and for
1083 // others the thickness is very unpredictable.
1085 // So, instead of trying to approximate the system theme, we
1086 // instead try to compute a reasonable looking choice based on the
1087 // known color of the track and the thumb piece. This is difficult
1088 // when trying to deal both with high- and low-contrast themes,
1089 // and both with positive and inverted themes.
1091 // The following code has been tested to look OK with all of the
1092 // default GTK themes.
1093 SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
1094 SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
1096 if (hsv1[2] + hsv2[2] > 1.0)
1097 diff = -diff;
1099 return SaturateAndBrighten(hsv2, -0.2f, diff);
1102 } // namespace ui