[safe_browsing] Remove unused ContainsBrowseUrl() parameter.
[chromium-blink-merge.git] / ui / native_theme / native_theme_base.cc
blobfa7ab6e8a0afb01c715de1bef049434dc21ba2e2
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 "grit/ui_resources.h"
13 #include "third_party/skia/include/effects/SkGradientShader.h"
14 #include "ui/base/layout.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/base/ui_base_switches.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/color_utils.h"
19 #include "ui/gfx/image/image_skia.h"
20 #include "ui/gfx/rect.h"
21 #include "ui/gfx/size.h"
22 #include "ui/gfx/skia_util.h"
24 namespace {
26 // These are the default dimensions of radio buttons and checkboxes.
27 const int kCheckboxAndRadioWidth = 13;
28 const int kCheckboxAndRadioHeight = 13;
30 // These sizes match the sizes in Chromium Win.
31 const int kSliderThumbWidth = 11;
32 const int kSliderThumbHeight = 21;
34 const SkColor kSliderTrackBackgroundColor =
35 SkColorSetRGB(0xe3, 0xdd, 0xd8);
36 const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
37 const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0);
38 const SkColor kSliderThumbBorderDarkGrey =
39 SkColorSetRGB(0x9d, 0x96, 0x8e);
41 const SkColor kTextBorderColor = SkColorSetRGB(0xa9, 0xa9, 0xa9);
43 const SkColor kMenuPopupBackgroundColor = SkColorSetRGB(210, 225, 246);
45 const unsigned int kDefaultScrollbarWidth = 15;
46 const unsigned int kDefaultScrollbarButtonLength = 14;
48 const SkColor kCheckboxTinyColor = SK_ColorGRAY;
49 const SkColor kCheckboxShadowColor = SkColorSetARGB(0x15, 0, 0, 0);
50 const SkColor kCheckboxShadowHoveredColor = SkColorSetARGB(0x1F, 0, 0, 0);
51 const SkColor kCheckboxShadowDisabledColor = SkColorSetARGB(0, 0, 0, 0);
52 const SkColor kCheckboxGradientColors[] = {
53 SkColorSetRGB(0xed, 0xed, 0xed),
54 SkColorSetRGB(0xde, 0xde, 0xde) };
55 const SkColor kCheckboxGradientPressedColors[] = {
56 SkColorSetRGB(0xe7, 0xe7, 0xe7),
57 SkColorSetRGB(0xd7, 0xd7, 0xd7) };
58 const SkColor kCheckboxGradientHoveredColors[] = {
59 SkColorSetRGB(0xf0, 0xf0, 0xf0),
60 SkColorSetRGB(0xe0, 0xe0, 0xe0) };
61 const SkColor kCheckboxGradientDisabledColors[] = {
62 SkColorSetARGB(0x80, 0xed, 0xed, 0xed),
63 SkColorSetARGB(0x80, 0xde, 0xde, 0xde) };
64 const SkColor kCheckboxBorderColor = SkColorSetARGB(0x40, 0, 0, 0);
65 const SkColor kCheckboxBorderHoveredColor = SkColorSetARGB(0x4D, 0, 0, 0);
66 const SkColor kCheckboxBorderDisabledColor = SkColorSetARGB(0x20, 0, 0, 0);
67 const SkColor kCheckboxStrokeColor = SkColorSetARGB(0xB3, 0, 0, 0);
68 const SkColor kCheckboxStrokeDisabledColor = SkColorSetARGB(0x59, 0, 0, 0);
69 const SkColor kRadioDotColor = SkColorSetRGB(0x66, 0x66, 0x66);
70 const SkColor kRadioDotDisabledColor = SkColorSetARGB(0x80, 0x66, 0x66, 0x66);
72 // Get lightness adjusted color.
73 SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
74 double lightness_amount) {
75 color_utils::HSL adjusted = hsl;
76 adjusted.l += lightness_amount;
77 if (adjusted.l > 1.0)
78 adjusted.l = 1.0;
79 if (adjusted.l < 0.0)
80 adjusted.l = 0.0;
82 return color_utils::HSLToSkColor(adjusted, alpha);
85 } // namespace
87 namespace ui {
89 gfx::Size NativeThemeBase::GetPartSize(Part part,
90 State state,
91 const ExtraParams& extra) const {
92 switch (part) {
93 // Please keep these in the order of NativeTheme::Part.
94 case kCheckbox:
95 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
96 case kInnerSpinButton:
97 return gfx::Size(scrollbar_width_, 0);
98 case kMenuList:
99 return gfx::Size(); // No default size.
100 case kMenuCheck:
101 case kMenuCheckBackground:
102 case kMenuPopupArrow:
103 NOTIMPLEMENTED();
104 break;
105 case kMenuPopupBackground:
106 return gfx::Size(); // No default size.
107 case kMenuPopupGutter:
108 case kMenuPopupSeparator:
109 NOTIMPLEMENTED();
110 break;
111 case kMenuItemBackground:
112 case kProgressBar:
113 case kPushButton:
114 return gfx::Size(); // No default size.
115 case kRadio:
116 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
117 case kScrollbarDownArrow:
118 case kScrollbarUpArrow:
119 return gfx::Size(scrollbar_width_, scrollbar_button_length_);
120 case kScrollbarLeftArrow:
121 case kScrollbarRightArrow:
122 return gfx::Size(scrollbar_button_length_, scrollbar_width_);
123 case kScrollbarHorizontalThumb:
124 // This matches Firefox on Linux.
125 return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
126 case kScrollbarVerticalThumb:
127 // This matches Firefox on Linux.
128 return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
129 case kScrollbarHorizontalTrack:
130 return gfx::Size(0, scrollbar_width_);
131 case kScrollbarVerticalTrack:
132 return gfx::Size(scrollbar_width_, 0);
133 case kScrollbarHorizontalGripper:
134 case kScrollbarVerticalGripper:
135 NOTIMPLEMENTED();
136 break;
137 case kSliderTrack:
138 return gfx::Size(); // No default size.
139 case kSliderThumb:
140 // These sizes match the sizes in Chromium Win.
141 return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
142 case kTabPanelBackground:
143 NOTIMPLEMENTED();
144 break;
145 case kTextField:
146 return gfx::Size(); // No default size.
147 case kTrackbarThumb:
148 case kTrackbarTrack:
149 case kWindowResizeGripper:
150 NOTIMPLEMENTED();
151 break;
152 default:
153 NOTREACHED() << "Unknown theme part: " << part;
154 break;
156 return gfx::Size();
159 void NativeThemeBase::Paint(SkCanvas* canvas,
160 Part part,
161 State state,
162 const gfx::Rect& rect,
163 const ExtraParams& extra) const {
164 if (rect.IsEmpty())
165 return;
167 switch (part) {
168 // Please keep these in the order of NativeTheme::Part.
169 case kCheckbox:
170 PaintCheckbox(canvas, state, rect, extra.button);
171 break;
172 case kInnerSpinButton:
173 PaintInnerSpinButton(canvas, state, rect, extra.inner_spin);
174 break;
175 case kMenuList:
176 PaintMenuList(canvas, state, rect, extra.menu_list);
177 break;
178 case kMenuCheck:
179 case kMenuCheckBackground:
180 case kMenuPopupArrow:
181 NOTIMPLEMENTED();
182 break;
183 case kMenuPopupBackground:
184 PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background);
185 break;
186 case kMenuPopupGutter:
187 case kMenuPopupSeparator:
188 NOTIMPLEMENTED();
189 break;
190 case kMenuItemBackground:
191 PaintMenuItemBackground(canvas, state, rect, extra.menu_list);
192 break;
193 case kProgressBar:
194 PaintProgressBar(canvas, state, rect, extra.progress_bar);
195 break;
196 case kPushButton:
197 PaintButton(canvas, state, rect, extra.button);
198 break;
199 case kRadio:
200 PaintRadio(canvas, state, rect, extra.button);
201 break;
202 case kScrollbarDownArrow:
203 case kScrollbarUpArrow:
204 case kScrollbarLeftArrow:
205 case kScrollbarRightArrow:
206 PaintArrowButton(canvas, rect, part, state);
207 break;
208 case kScrollbarHorizontalThumb:
209 case kScrollbarVerticalThumb:
210 PaintScrollbarThumb(canvas, part, state, rect);
211 break;
212 case kScrollbarHorizontalTrack:
213 case kScrollbarVerticalTrack:
214 PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect);
215 break;
216 case kScrollbarHorizontalGripper:
217 case kScrollbarVerticalGripper:
218 // Invoked by views scrollbar code, don't care about for non-win
219 // implementations, so no NOTIMPLEMENTED.
220 break;
221 case kScrollbarCorner:
222 PaintScrollbarCorner(canvas, state, rect);
223 break;
224 case kSliderTrack:
225 PaintSliderTrack(canvas, state, rect, extra.slider);
226 break;
227 case kSliderThumb:
228 PaintSliderThumb(canvas, state, rect, extra.slider);
229 break;
230 case kTabPanelBackground:
231 NOTIMPLEMENTED();
232 break;
233 case kTextField:
234 PaintTextField(canvas, state, rect, extra.text_field);
235 break;
236 case kTrackbarThumb:
237 case kTrackbarTrack:
238 case kWindowResizeGripper:
239 NOTIMPLEMENTED();
240 break;
241 default:
242 NOTREACHED() << "Unknown theme part: " << part;
243 break;
247 NativeThemeBase::NativeThemeBase()
248 : scrollbar_width_(kDefaultScrollbarWidth),
249 scrollbar_button_length_(kDefaultScrollbarButtonLength) {
252 NativeThemeBase::~NativeThemeBase() {
255 // static
256 scoped_ptr<gfx::Canvas> NativeThemeBase::CreateCanvas(SkCanvas* sk_canvas) {
257 // TODO(pkotwicz): Do something better and don't infer device
258 // scale factor from canvas scale.
259 SkMatrix m = sk_canvas->getTotalMatrix();
260 float device_scale = static_cast<float>(SkScalarAbs(m.getScaleX()));
261 return scoped_ptr<gfx::Canvas>(
262 gfx::Canvas::CreateCanvasWithoutScaling(sk_canvas, device_scale));
265 void NativeThemeBase::PaintArrowButton(
266 SkCanvas* canvas,
267 const gfx::Rect& rect, Part direction, State state) const {
268 SkPaint paint;
270 // Calculate button color.
271 SkScalar trackHSV[3];
272 SkColorToHSV(track_color_, trackHSV);
273 SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f);
274 SkColor backgroundColor = buttonColor;
275 if (state == kPressed) {
276 SkScalar buttonHSV[3];
277 SkColorToHSV(buttonColor, buttonHSV);
278 buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1f);
279 } else if (state == kHovered) {
280 SkScalar buttonHSV[3];
281 SkColorToHSV(buttonColor, buttonHSV);
282 buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05f);
285 SkIRect skrect;
286 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y()
287 + rect.height());
288 // Paint the background (the area visible behind the rounded corners).
289 paint.setColor(backgroundColor);
290 canvas->drawIRect(skrect, paint);
292 // Paint the button's outline and fill the middle
293 SkPath outline;
294 switch (direction) {
295 case kScrollbarUpArrow:
296 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
297 outline.rLineTo(0, -(rect.height() - 2));
298 outline.rLineTo(2, -2);
299 outline.rLineTo(rect.width() - 5, 0);
300 outline.rLineTo(2, 2);
301 outline.rLineTo(0, rect.height() - 2);
302 break;
303 case kScrollbarDownArrow:
304 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
305 outline.rLineTo(0, rect.height() - 2);
306 outline.rLineTo(2, 2);
307 outline.rLineTo(rect.width() - 5, 0);
308 outline.rLineTo(2, -2);
309 outline.rLineTo(0, -(rect.height() - 2));
310 break;
311 case kScrollbarRightArrow:
312 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
313 outline.rLineTo(rect.width() - 2, 0);
314 outline.rLineTo(2, 2);
315 outline.rLineTo(0, rect.height() - 5);
316 outline.rLineTo(-2, 2);
317 outline.rLineTo(-(rect.width() - 2), 0);
318 break;
319 case kScrollbarLeftArrow:
320 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
321 outline.rLineTo(-(rect.width() - 2), 0);
322 outline.rLineTo(-2, 2);
323 outline.rLineTo(0, rect.height() - 5);
324 outline.rLineTo(2, 2);
325 outline.rLineTo(rect.width() - 2, 0);
326 break;
327 default:
328 break;
330 outline.close();
332 paint.setStyle(SkPaint::kFill_Style);
333 paint.setColor(buttonColor);
334 canvas->drawPath(outline, paint);
336 paint.setAntiAlias(true);
337 paint.setStyle(SkPaint::kStroke_Style);
338 SkScalar thumbHSV[3];
339 SkColorToHSV(thumb_inactive_color_, thumbHSV);
340 paint.setColor(OutlineColor(trackHSV, thumbHSV));
341 canvas->drawPath(outline, paint);
343 PaintArrow(canvas, rect, direction, GetArrowColor(state));
346 void NativeThemeBase::PaintArrow(SkCanvas* gc,
347 const gfx::Rect& rect,
348 Part direction,
349 SkColor color) const {
350 int width_middle, length_middle;
351 if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
352 width_middle = rect.width() / 2 + 1;
353 length_middle = rect.height() / 2 + 1;
354 } else {
355 length_middle = rect.width() / 2 + 1;
356 width_middle = rect.height() / 2 + 1;
359 SkPaint paint;
360 paint.setColor(color);
361 paint.setAntiAlias(false);
362 paint.setStyle(SkPaint::kFill_Style);
364 SkPath path;
365 // The constants in this block of code are hand-tailored to produce good
366 // looking arrows without anti-aliasing.
367 switch (direction) {
368 case kScrollbarUpArrow:
369 path.moveTo(rect.x() + width_middle - 4, rect.y() + length_middle + 2);
370 path.rLineTo(7, 0);
371 path.rLineTo(-4, -4);
372 break;
373 case kScrollbarDownArrow:
374 path.moveTo(rect.x() + width_middle - 4, rect.y() + length_middle - 3);
375 path.rLineTo(7, 0);
376 path.rLineTo(-4, 4);
377 break;
378 case kScrollbarRightArrow:
379 path.moveTo(rect.x() + length_middle - 3, rect.y() + width_middle - 4);
380 path.rLineTo(0, 7);
381 path.rLineTo(4, -4);
382 break;
383 case kScrollbarLeftArrow:
384 path.moveTo(rect.x() + length_middle + 1, rect.y() + width_middle - 5);
385 path.rLineTo(0, 9);
386 path.rLineTo(-4, -4);
387 break;
388 default:
389 break;
391 path.close();
393 gc->drawPath(path, paint);
396 void NativeThemeBase::PaintScrollbarTrack(SkCanvas* canvas,
397 Part part,
398 State state,
399 const ScrollbarTrackExtraParams& extra_params,
400 const gfx::Rect& rect) const {
401 SkPaint paint;
402 SkIRect skrect;
404 skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
405 SkScalar track_hsv[3];
406 SkColorToHSV(track_color_, track_hsv);
407 paint.setColor(SaturateAndBrighten(track_hsv, 0, 0));
408 canvas->drawIRect(skrect, paint);
410 SkScalar thumb_hsv[3];
411 SkColorToHSV(thumb_inactive_color_, thumb_hsv);
413 paint.setColor(OutlineColor(track_hsv, thumb_hsv));
414 DrawBox(canvas, rect, paint);
417 void NativeThemeBase::PaintScrollbarThumb(SkCanvas* canvas,
418 Part part,
419 State state,
420 const gfx::Rect& rect) const {
421 const bool hovered = state == kHovered;
422 const int midx = rect.x() + rect.width() / 2;
423 const int midy = rect.y() + rect.height() / 2;
424 const bool vertical = part == kScrollbarVerticalThumb;
426 SkScalar thumb[3];
427 SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
429 SkPaint paint;
430 paint.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
432 SkIRect skrect;
433 if (vertical)
434 skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
435 else
436 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
438 canvas->drawIRect(skrect, paint);
440 paint.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
442 if (vertical) {
443 skrect.set(
444 midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
445 } else {
446 skrect.set(
447 rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height());
450 canvas->drawIRect(skrect, paint);
452 SkScalar track[3];
453 SkColorToHSV(track_color_, track);
454 paint.setColor(OutlineColor(track, thumb));
455 DrawBox(canvas, rect, paint);
457 if (rect.height() > 10 && rect.width() > 10) {
458 const int grippy_half_width = 2;
459 const int inter_grippy_offset = 3;
460 if (vertical) {
461 DrawHorizLine(canvas,
462 midx - grippy_half_width,
463 midx + grippy_half_width,
464 midy - inter_grippy_offset,
465 paint);
466 DrawHorizLine(canvas,
467 midx - grippy_half_width,
468 midx + grippy_half_width,
469 midy,
470 paint);
471 DrawHorizLine(canvas,
472 midx - grippy_half_width,
473 midx + grippy_half_width,
474 midy + inter_grippy_offset,
475 paint);
476 } else {
477 DrawVertLine(canvas,
478 midx - inter_grippy_offset,
479 midy - grippy_half_width,
480 midy + grippy_half_width,
481 paint);
482 DrawVertLine(canvas,
483 midx,
484 midy - grippy_half_width,
485 midy + grippy_half_width,
486 paint);
487 DrawVertLine(canvas,
488 midx + inter_grippy_offset,
489 midy - grippy_half_width,
490 midy + grippy_half_width,
491 paint);
496 void NativeThemeBase::PaintScrollbarCorner(SkCanvas* canvas,
497 State state,
498 const gfx::Rect& rect) const {
499 SkPaint paint;
500 paint.setColor(SK_ColorWHITE);
501 paint.setStyle(SkPaint::kFill_Style);
502 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
503 canvas->drawIRect(RectToSkIRect(rect), paint);
506 void NativeThemeBase::PaintCheckbox(SkCanvas* canvas,
507 State state,
508 const gfx::Rect& rect,
509 const ButtonExtraParams& button) const {
510 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect,
511 SkIntToScalar(2));
512 if (!skrect.isEmpty()) {
513 // Draw the checkmark / dash.
514 SkPaint paint;
515 paint.setAntiAlias(true);
516 paint.setStyle(SkPaint::kStroke_Style);
517 if (state == kDisabled)
518 paint.setColor(kCheckboxStrokeDisabledColor);
519 else
520 paint.setColor(kCheckboxStrokeColor);
521 if (button.indeterminate) {
522 SkPath dash;
523 dash.moveTo(skrect.x() + skrect.width() * 0.16,
524 (skrect.y() + skrect.bottom()) / 2);
525 dash.rLineTo(skrect.width() * 0.68, 0);
526 paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
527 canvas->drawPath(dash, paint);
528 } else if (button.checked) {
529 SkPath check;
530 check.moveTo(skrect.x() + skrect.width() * 0.2,
531 skrect.y() + skrect.height() * 0.5);
532 check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
533 paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
534 check.lineTo(skrect.right() - skrect.width() * 0.2,
535 skrect.y() + skrect.height() * 0.2);
536 canvas->drawPath(check, paint);
541 // Draws the common elements of checkboxes and radio buttons.
542 // Returns the rectangle within which any additional decorations should be
543 // drawn, or empty if none.
544 SkRect NativeThemeBase::PaintCheckboxRadioCommon(
545 SkCanvas* canvas,
546 State state,
547 const gfx::Rect& rect,
548 const SkScalar borderRadius) const {
550 SkRect skrect = gfx::RectToSkRect(rect);
552 // Use the largest square that fits inside the provided rectangle.
553 // No other browser seems to support non-square widget, so accidentally
554 // having non-square sizes is common (eg. amazon and webkit dev tools).
555 if (skrect.width() != skrect.height()) {
556 SkScalar size = SkMinScalar(skrect.width(), skrect.height());
557 skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
560 // If the rectangle is too small then paint only a rectangle. We don't want
561 // to have to worry about '- 1' and '+ 1' calculations below having overflow
562 // or underflow.
563 if (skrect.width() <= 2) {
564 SkPaint paint;
565 paint.setColor(kCheckboxTinyColor);
566 paint.setStyle(SkPaint::kFill_Style);
567 canvas->drawRect(skrect, paint);
568 // Too small to draw anything more.
569 return SkRect::MakeEmpty();
572 // Make room for the drop shadow.
573 skrect.iset(skrect.x(), skrect.y(), skrect.right() - 1, skrect.bottom() - 1);
575 // Draw the drop shadow below the widget.
576 if (state != kPressed) {
577 SkPaint paint;
578 paint.setAntiAlias(true);
579 SkRect shadowRect = skrect;
580 shadowRect.offset(0, 1);
581 if (state == kDisabled)
582 paint.setColor(kCheckboxShadowDisabledColor);
583 else if (state == kHovered)
584 paint.setColor(kCheckboxShadowHoveredColor);
585 else
586 paint.setColor(kCheckboxShadowColor);
587 paint.setStyle(SkPaint::kFill_Style);
588 canvas->drawRoundRect(shadowRect, borderRadius, borderRadius, paint);
591 // Draw the gradient-filled rectangle
592 SkPoint gradient_bounds[3];
593 gradient_bounds[0].set(skrect.x(), skrect.y());
594 gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
595 gradient_bounds[2].set(skrect.x(), skrect.bottom());
596 const SkColor* startEndColors;
597 if (state == kPressed)
598 startEndColors = kCheckboxGradientPressedColors;
599 else if (state == kHovered)
600 startEndColors = kCheckboxGradientHoveredColors;
601 else if (state == kDisabled)
602 startEndColors = kCheckboxGradientDisabledColors;
603 else /* kNormal */
604 startEndColors = kCheckboxGradientColors;
605 SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]};
606 skia::RefPtr<SkShader> shader = skia::AdoptRef(
607 SkGradientShader::CreateLinear(
608 gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode, NULL));
609 SkPaint paint;
610 paint.setAntiAlias(true);
611 paint.setShader(shader.get());
612 paint.setStyle(SkPaint::kFill_Style);
613 canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
614 paint.setShader(NULL);
616 // Draw the border.
617 if (state == kHovered)
618 paint.setColor(kCheckboxBorderHoveredColor);
619 else if (state == kDisabled)
620 paint.setColor(kCheckboxBorderDisabledColor);
621 else
622 paint.setColor(kCheckboxBorderColor);
623 paint.setStyle(SkPaint::kStroke_Style);
624 paint.setStrokeWidth(SkIntToScalar(1));
625 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
626 canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
628 // Return the rectangle excluding the drop shadow for drawing any additional
629 // decorations.
630 return skrect;
633 void NativeThemeBase::PaintRadio(SkCanvas* canvas,
634 State state,
635 const gfx::Rect& rect,
636 const ButtonExtraParams& button) const {
638 // Most of a radio button is the same as a checkbox, except the the rounded
639 // square is a circle (i.e. border radius >= 100%).
640 const SkScalar radius = SkFloatToScalar(
641 static_cast<float>(std::max(rect.width(), rect.height())) / 2);
642 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, radius);
643 if (!skrect.isEmpty() && button.checked) {
644 // Draw the dot.
645 SkPaint paint;
646 paint.setAntiAlias(true);
647 paint.setStyle(SkPaint::kFill_Style);
648 if (state == kDisabled)
649 paint.setColor(kRadioDotDisabledColor);
650 else
651 paint.setColor(kRadioDotColor);
652 skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
653 // Use drawRoundedRect instead of drawOval to be completely consistent
654 // with the border in PaintCheckboxRadioNewCommon.
655 canvas->drawRoundRect(skrect, radius, radius, paint);
659 void NativeThemeBase::PaintButton(SkCanvas* canvas,
660 State state,
661 const gfx::Rect& rect,
662 const ButtonExtraParams& button) const {
663 SkPaint paint;
664 const int kRight = rect.right();
665 const int kBottom = rect.bottom();
666 SkRect skrect = SkRect::MakeLTRB(rect.x(), rect.y(), kRight, kBottom);
667 SkColor base_color = button.background_color;
669 color_utils::HSL base_hsl;
670 color_utils::SkColorToHSL(base_color, &base_hsl);
672 // Our standard gradient is from 0xdd to 0xf8. This is the amount of
673 // increased luminance between those values.
674 SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
676 // If the button is too small, fallback to drawing a single, solid color
677 if (rect.width() < 5 || rect.height() < 5) {
678 paint.setColor(base_color);
679 canvas->drawRect(skrect, paint);
680 return;
683 paint.setColor(SK_ColorBLACK);
684 const int kLightEnd = state == kPressed ? 1 : 0;
685 const int kDarkEnd = !kLightEnd;
686 SkPoint gradient_bounds[2];
687 gradient_bounds[kLightEnd].iset(rect.x(), rect.y());
688 gradient_bounds[kDarkEnd].iset(rect.x(), kBottom - 1);
689 SkColor colors[2];
690 colors[0] = light_color;
691 colors[1] = base_color;
693 skia::RefPtr<SkShader> shader = skia::AdoptRef(
694 SkGradientShader::CreateLinear(
695 gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL));
696 paint.setStyle(SkPaint::kFill_Style);
697 paint.setAntiAlias(true);
698 paint.setShader(shader.get());
700 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
701 paint.setShader(NULL);
703 if (button.has_border) {
704 int border_alpha = state == kHovered ? 0x80 : 0x55;
705 if (button.is_focused) {
706 border_alpha = 0xff;
707 paint.setColor(GetSystemColor(kColorId_FocusedBorderColor));
709 paint.setStyle(SkPaint::kStroke_Style);
710 paint.setStrokeWidth(SkIntToScalar(1));
711 paint.setAlpha(border_alpha);
712 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
713 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
717 void NativeThemeBase::PaintTextField(SkCanvas* canvas,
718 State state,
719 const gfx::Rect& rect,
720 const TextFieldExtraParams& text) const {
721 SkRect bounds;
722 bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
724 SkPaint fill_paint;
725 fill_paint.setStyle(SkPaint::kFill_Style);
726 fill_paint.setColor(text.background_color);
727 canvas->drawRect(bounds, fill_paint);
729 // Text INPUT, listbox SELECT, and TEXTAREA have consistent borders.
730 // border: 1px solid #a9a9a9
731 SkPaint stroke_paint;
732 stroke_paint.setStyle(SkPaint::kStroke_Style);
733 stroke_paint.setColor(kTextBorderColor);
734 canvas->drawRect(bounds, stroke_paint);
737 void NativeThemeBase::PaintMenuList(
738 SkCanvas* canvas,
739 State state,
740 const gfx::Rect& rect,
741 const MenuListExtraParams& menu_list) const {
742 // If a border radius is specified, we let the WebCore paint the background
743 // and the border of the control.
744 if (!menu_list.has_border_radius) {
745 ButtonExtraParams button = { 0 };
746 button.background_color = menu_list.background_color;
747 button.has_border = menu_list.has_border;
748 PaintButton(canvas, state, rect, button);
751 SkPaint paint;
752 paint.setColor(SK_ColorBLACK);
753 paint.setAntiAlias(true);
754 paint.setStyle(SkPaint::kFill_Style);
756 SkPath path;
757 path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3);
758 path.rLineTo(6, 0);
759 path.rLineTo(-3, 6);
760 path.close();
761 canvas->drawPath(path, paint);
764 void NativeThemeBase::PaintMenuPopupBackground(
765 SkCanvas* canvas,
766 const gfx::Size& size,
767 const MenuBackgroundExtraParams& menu_background) const {
768 canvas->drawColor(kMenuPopupBackgroundColor, SkXfermode::kSrc_Mode);
771 void NativeThemeBase::PaintMenuItemBackground(
772 SkCanvas* canvas,
773 State state,
774 const gfx::Rect& rect,
775 const MenuListExtraParams& menu_list) const {
776 // By default don't draw anything over the normal background.
779 void NativeThemeBase::PaintSliderTrack(SkCanvas* canvas,
780 State state,
781 const gfx::Rect& rect,
782 const SliderExtraParams& slider) const {
783 const int kMidX = rect.x() + rect.width() / 2;
784 const int kMidY = rect.y() + rect.height() / 2;
786 SkPaint paint;
787 paint.setColor(kSliderTrackBackgroundColor);
789 SkRect skrect;
790 if (slider.vertical) {
791 skrect.set(std::max(rect.x(), kMidX - 2),
792 rect.y(),
793 std::min(rect.right(), kMidX + 2),
794 rect.bottom());
795 } else {
796 skrect.set(rect.x(),
797 std::max(rect.y(), kMidY - 2),
798 rect.right(),
799 std::min(rect.bottom(), kMidY + 2));
801 canvas->drawRect(skrect, paint);
804 void NativeThemeBase::PaintSliderThumb(SkCanvas* canvas,
805 State state,
806 const gfx::Rect& rect,
807 const SliderExtraParams& slider) const {
808 const bool hovered = (state == kHovered) || slider.in_drag;
809 const int kMidX = rect.x() + rect.width() / 2;
810 const int kMidY = rect.y() + rect.height() / 2;
812 SkPaint paint;
813 paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
815 SkIRect skrect;
816 if (slider.vertical)
817 skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
818 else
819 skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
821 canvas->drawIRect(skrect, paint);
823 paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
825 if (slider.vertical)
826 skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
827 else
828 skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
830 canvas->drawIRect(skrect, paint);
832 paint.setColor(kSliderThumbBorderDarkGrey);
833 DrawBox(canvas, rect, paint);
835 if (rect.height() > 10 && rect.width() > 10) {
836 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
837 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
838 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
842 void NativeThemeBase::PaintInnerSpinButton(SkCanvas* canvas,
843 State state,
844 const gfx::Rect& rect,
845 const InnerSpinButtonExtraParams& spin_button) const {
846 if (spin_button.read_only)
847 state = kDisabled;
849 State north_state = state;
850 State south_state = state;
851 if (spin_button.spin_up)
852 south_state = south_state != kDisabled ? kNormal : kDisabled;
853 else
854 north_state = north_state != kDisabled ? kNormal : kDisabled;
856 gfx::Rect half = rect;
857 half.set_height(rect.height() / 2);
858 PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
860 half.set_y(rect.y() + rect.height() / 2);
861 PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
864 void NativeThemeBase::PaintProgressBar(SkCanvas* canvas,
865 State state,
866 const gfx::Rect& rect,
867 const ProgressBarExtraParams& progress_bar) const {
868 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
869 gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR);
870 gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed(
871 IDR_PROGRESS_BORDER_LEFT);
872 gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed(
873 IDR_PROGRESS_BORDER_RIGHT);
875 DCHECK(bar_image->width() > 0);
876 DCHECK(rect.width() > 0);
878 float tile_scale_y = static_cast<float>(rect.height()) / bar_image->height();
880 int dest_left_border_width = left_border_image->width();
881 int dest_right_border_width = right_border_image->width();
883 // Since an implicit float -> int conversion will truncate, we want to make
884 // sure that if a border is desired, it gets at least one pixel.
885 if (dest_left_border_width > 0) {
886 dest_left_border_width = dest_left_border_width * tile_scale_y;
887 dest_left_border_width = std::max(dest_left_border_width, 1);
889 if (dest_right_border_width > 0) {
890 dest_right_border_width = dest_right_border_width * tile_scale_y;
891 dest_right_border_width = std::max(dest_right_border_width, 1);
894 // Since the width of the progress bar may not be evenly divisible by the
895 // tile size, in order to make it look right we may need to draw some of the
896 // with a width of 1 pixel smaller than the rest of the tiles.
897 int new_tile_width = static_cast<int>(bar_image->width() * tile_scale_y);
898 new_tile_width = std::max(new_tile_width, 1);
900 float tile_scale_x = static_cast<float>(new_tile_width) / bar_image->width();
901 if (rect.width() % new_tile_width == 0) {
902 DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale_y,
903 rect.x(), rect.y(),
904 rect.width(), rect.height());
905 } else {
906 int num_tiles = 1 + rect.width() / new_tile_width;
907 int overshoot = num_tiles * new_tile_width - rect.width();
908 // Since |overshoot| represents the number of tiles that were too big, draw
909 // |overshoot| tiles with their width reduced by 1.
910 int num_big_tiles = num_tiles - overshoot;
911 int num_small_tiles = overshoot;
912 int small_width = new_tile_width - 1;
913 float small_scale_x = static_cast<float>(small_width) / bar_image->width();
914 float big_scale_x = tile_scale_x;
916 gfx::Rect big_rect = rect;
917 gfx::Rect small_rect = rect;
918 big_rect.Inset(0, 0, num_small_tiles*small_width, 0);
919 small_rect.Inset(num_big_tiles*new_tile_width, 0, 0, 0);
921 DrawTiledImage(canvas, *bar_image, 0, 0, big_scale_x, tile_scale_y,
922 big_rect.x(), big_rect.y(), big_rect.width(), big_rect.height());
923 DrawTiledImage(canvas, *bar_image, 0, 0, small_scale_x, tile_scale_y,
924 small_rect.x(), small_rect.y(), small_rect.width(), small_rect.height());
926 if (progress_bar.value_rect_width) {
927 gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE);
929 new_tile_width = static_cast<int>(value_image->width() * tile_scale_y);
930 tile_scale_x = static_cast<float>(new_tile_width) /
931 value_image->width();
933 DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale_y,
934 progress_bar.value_rect_x,
935 progress_bar.value_rect_y,
936 progress_bar.value_rect_width,
937 progress_bar.value_rect_height);
940 DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(),
941 left_border_image->height(), rect.x(), rect.y(), dest_left_border_width,
942 rect.height());
944 int dest_x = rect.right() - dest_right_border_width;
945 DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(),
946 right_border_image->height(), dest_x, rect.y(),
947 dest_right_border_width, rect.height());
950 bool NativeThemeBase::IntersectsClipRectInt(SkCanvas* canvas,
951 int x, int y, int w, int h) const {
952 SkRect clip;
953 return canvas->getClipBounds(&clip) &&
954 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
955 SkIntToScalar(y + h));
958 void NativeThemeBase::DrawImageInt(
959 SkCanvas* sk_canvas, const gfx::ImageSkia& image,
960 int src_x, int src_y, int src_w, int src_h,
961 int dest_x, int dest_y, int dest_w, int dest_h) const {
962 scoped_ptr<gfx::Canvas> canvas(CreateCanvas(sk_canvas));
963 canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
964 dest_x, dest_y, dest_w, dest_h, true);
967 void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
968 const gfx::ImageSkia& image,
969 int src_x, int src_y, float tile_scale_x, float tile_scale_y,
970 int dest_x, int dest_y, int w, int h) const {
971 scoped_ptr<gfx::Canvas> canvas(CreateCanvas(sk_canvas));
972 canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
973 tile_scale_y, dest_x, dest_y, w, h);
976 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
977 SkScalar saturate_amount,
978 SkScalar brighten_amount) const {
979 SkScalar color[3];
980 color[0] = hsv[0];
981 color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
982 color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
983 return SkHSVToColor(color);
986 SkColor NativeThemeBase::GetArrowColor(State state) const {
987 if (state != kDisabled)
988 return SK_ColorBLACK;
990 SkScalar track_hsv[3];
991 SkColorToHSV(track_color_, track_hsv);
992 SkScalar thumb_hsv[3];
993 SkColorToHSV(thumb_inactive_color_, thumb_hsv);
994 return OutlineColor(track_hsv, thumb_hsv);
997 void NativeThemeBase::DrawVertLine(SkCanvas* canvas,
998 int x,
999 int y1,
1000 int y2,
1001 const SkPaint& paint) const {
1002 SkIRect skrect;
1003 skrect.set(x, y1, x + 1, y2 + 1);
1004 canvas->drawIRect(skrect, paint);
1007 void NativeThemeBase::DrawHorizLine(SkCanvas* canvas,
1008 int x1,
1009 int x2,
1010 int y,
1011 const SkPaint& paint) const {
1012 SkIRect skrect;
1013 skrect.set(x1, y, x2 + 1, y + 1);
1014 canvas->drawIRect(skrect, paint);
1017 void NativeThemeBase::DrawBox(SkCanvas* canvas,
1018 const gfx::Rect& rect,
1019 const SkPaint& paint) const {
1020 const int right = rect.x() + rect.width() - 1;
1021 const int bottom = rect.y() + rect.height() - 1;
1022 DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
1023 DrawVertLine(canvas, right, rect.y(), bottom, paint);
1024 DrawHorizLine(canvas, rect.x(), right, bottom, paint);
1025 DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
1028 SkScalar NativeThemeBase::Clamp(SkScalar value,
1029 SkScalar min,
1030 SkScalar max) const {
1031 return std::min(std::max(value, min), max);
1034 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
1035 // GTK Theme engines have way too much control over the layout of
1036 // the scrollbar. We might be able to more closely approximate its
1037 // look-and-feel, if we sent whole images instead of just colors
1038 // from the browser to the renderer. But even then, some themes
1039 // would just break.
1041 // So, instead, we don't even try to 100% replicate the look of
1042 // the native scrollbar. We render our own version, but we make
1043 // sure to pick colors that blend in nicely with the system GTK
1044 // theme. In most cases, we can just sample a couple of pixels
1045 // from the system scrollbar and use those colors to draw our
1046 // scrollbar.
1048 // This works fine for the track color and the overall thumb
1049 // color. But it fails spectacularly for the outline color used
1050 // around the thumb piece. Not all themes have a clearly defined
1051 // outline. For some of them it is partially transparent, and for
1052 // others the thickness is very unpredictable.
1054 // So, instead of trying to approximate the system theme, we
1055 // instead try to compute a reasonable looking choice based on the
1056 // known color of the track and the thumb piece. This is difficult
1057 // when trying to deal both with high- and low-contrast themes,
1058 // and both with positive and inverted themes.
1060 // The following code has been tested to look OK with all of the
1061 // default GTK themes.
1062 SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
1063 SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
1065 if (hsv1[2] + hsv2[2] > 1.0)
1066 diff = -diff;
1068 return SaturateAndBrighten(hsv2, -0.2f, diff);
1071 } // namespace ui