Home | History | Annotate | Download | only in native_theme
      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.
      4 
      5 #include "ui/native_theme/native_theme_base.h"
      6 
      7 #include <limits>
      8 
      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/image/image_skia.h"
     19 #include "ui/gfx/rect.h"
     20 #include "ui/gfx/size.h"
     21 #include "ui/gfx/skia_util.h"
     22 #include "ui/native_theme/common_theme.h"
     23 #include "ui/resources/grit/ui_resources.h"
     24 
     25 namespace {
     26 
     27 // These are the default dimensions of radio buttons and checkboxes.
     28 const int kCheckboxAndRadioWidth = 13;
     29 const int kCheckboxAndRadioHeight = 13;
     30 
     31 // These sizes match the sizes in Chromium Win.
     32 const int kSliderThumbWidth = 11;
     33 const int kSliderThumbHeight = 21;
     34 
     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);
     41 
     42 const SkColor kTextBorderColor = SkColorSetRGB(0xa9, 0xa9, 0xa9);
     43 
     44 const SkColor kMenuPopupBackgroundColor = SkColorSetRGB(210, 225, 246);
     45 
     46 const unsigned int kDefaultScrollbarWidth = 15;
     47 const unsigned int kDefaultScrollbarButtonLength = 14;
     48 
     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);
     72 
     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;
     82 
     83   return color_utils::HSLToSkColor(adjusted, alpha);
     84 }
     85 
     86 }  // namespace
     87 
     88 namespace ui {
     89 
     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;
     96 
     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;
    160   }
    161   return gfx::Size();
    162 }
    163 
    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;
    172 
    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;
    185   }
    186   return;
    187 }
    188 
    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;
    196 
    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;
    278   }
    279 }
    280 
    281 NativeThemeBase::NativeThemeBase()
    282     : scrollbar_width_(kDefaultScrollbarWidth),
    283       scrollbar_button_length_(kDefaultScrollbarButtonLength) {
    284 }
    285 
    286 NativeThemeBase::~NativeThemeBase() {
    287 }
    288 
    289 void NativeThemeBase::PaintArrowButton(
    290     SkCanvas* canvas,
    291     const gfx::Rect& rect, Part direction, State state) const {
    292   SkPaint paint;
    293 
    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);
    307   }
    308 
    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);
    315 
    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;
    353   }
    354   outline.close();
    355 
    356   paint.setStyle(SkPaint::kFill_Style);
    357   paint.setColor(buttonColor);
    358   canvas->drawPath(outline, paint);
    359 
    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);
    366 
    367   PaintArrow(canvas, rect, direction, GetArrowColor(state));
    368 }
    369 
    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;
    381   }
    382 
    383   SkPaint paint;
    384   paint.setColor(color);
    385   paint.setAntiAlias(false);
    386   paint.setStyle(SkPaint::kFill_Style);
    387 
    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;
    414   }
    415   path.close();
    416 
    417   gc->drawPath(path, paint);
    418 }
    419 
    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;
    427 
    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);
    433 
    434   SkScalar thumb_hsv[3];
    435   SkColorToHSV(thumb_inactive_color_, thumb_hsv);
    436 
    437   paint.setColor(OutlineColor(track_hsv, thumb_hsv));
    438   DrawBox(canvas, rect, paint);
    439 }
    440 
    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;
    449 
    450   SkScalar thumb[3];
    451   SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
    452 
    453   SkPaint paint;
    454   paint.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
    455 
    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);
    461 
    462   canvas->drawIRect(skrect, paint);
    463 
    464   paint.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
    465 
    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());
    472   }
    473 
    474   canvas->drawIRect(skrect, paint);
    475 
    476   SkScalar track[3];
    477   SkColorToHSV(track_color_, track);
    478   paint.setColor(OutlineColor(track, thumb));
    479   DrawBox(canvas, rect, paint);
    480 
    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);
    516     }
    517   }
    518 }
    519 
    520 void NativeThemeBase::PaintScrollbarCorner(SkCanvas* canvas,
    521                                            State state,
    522                                            const gfx::Rect& rect) const {
    523 }
    524 
    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);
    556     }
    557   }
    558 }
    559 
    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 {
    568 
    569   SkRect skrect = gfx::RectToSkRect(rect);
    570 
    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);
    577   }
    578 
    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();
    589   }
    590 
    591   // Make room for the drop shadow.
    592   skrect.iset(skrect.x(), skrect.y(), skrect.right() - 1, skrect.bottom() - 1);
    593 
    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);
    608   }
    609 
    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);
    634 
    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);
    646 
    647   // Return the rectangle excluding the drop shadow for drawing any additional
    648   // decorations.
    649   return skrect;
    650 }
    651 
    652 void NativeThemeBase::PaintRadio(SkCanvas* canvas,
    653                                   State state,
    654                                   const gfx::Rect& rect,
    655                                   const ButtonExtraParams& button) const {
    656 
    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);
    675   }
    676 }
    677 
    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;
    687 
    688   color_utils::HSL base_hsl;
    689   color_utils::SkColorToHSL(base_color, &base_hsl);
    690 
    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));
    694 
    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;
    700   }
    701 
    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;
    711 
    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());
    718 
    719   canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
    720   paint.setShader(NULL);
    721 
    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));
    727     }
    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);
    733   }
    734 }
    735 
    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);
    742 
    743   SkPaint fill_paint;
    744   fill_paint.setStyle(SkPaint::kFill_Style);
    745   fill_paint.setColor(text.background_color);
    746   canvas->drawRect(bounds, fill_paint);
    747 
    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);
    754 }
    755 
    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);
    768   }
    769 
    770   SkPaint paint;
    771   paint.setColor(SK_ColorBLACK);
    772   paint.setAntiAlias(true);
    773   paint.setStyle(SkPaint::kFill_Style);
    774 
    775   SkPath path;
    776   path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3);
    777   path.rLineTo(6, 0);
    778   path.rLineTo(-3, 6);
    779   path.close();
    780   canvas->drawPath(path, paint);
    781 }
    782 
    783 void NativeThemeBase::PaintMenuPopupBackground(
    784     SkCanvas* canvas,
    785     const gfx::Size& size,
    786     const MenuBackgroundExtraParams& menu_background) const {
    787   canvas->drawColor(kMenuPopupBackgroundColor, SkXfermode::kSrc_Mode);
    788 }
    789 
    790 void NativeThemeBase::PaintMenuItemBackground(
    791     SkCanvas* canvas,
    792     State state,
    793     const gfx::Rect& rect,
    794     const MenuListExtraParams& menu_list) const {
    795   // By default don't draw anything over the normal background.
    796 }
    797 
    798 void NativeThemeBase::PaintSliderTrack(SkCanvas* canvas,
    799                                        State state,
    800                                        const gfx::Rect& rect,
    801                                        const SliderExtraParams& slider) const {
    802   const int kMidX = rect.x() + rect.width() / 2;
    803   const int kMidY = rect.y() + rect.height() / 2;
    804 
    805   SkPaint paint;
    806   paint.setColor(kSliderTrackBackgroundColor);
    807 
    808   SkRect skrect;
    809   if (slider.vertical) {
    810     skrect.set(std::max(rect.x(), kMidX - 2),
    811                rect.y(),
    812                std::min(rect.right(), kMidX + 2),
    813                rect.bottom());
    814   } else {
    815     skrect.set(rect.x(),
    816                std::max(rect.y(), kMidY - 2),
    817                rect.right(),
    818                std::min(rect.bottom(), kMidY + 2));
    819   }
    820   canvas->drawRect(skrect, paint);
    821 }
    822 
    823 void NativeThemeBase::PaintSliderThumb(SkCanvas* canvas,
    824                                        State state,
    825                                        const gfx::Rect& rect,
    826                                        const SliderExtraParams& slider) const {
    827   const bool hovered = (state == kHovered) || slider.in_drag;
    828   const int kMidX = rect.x() + rect.width() / 2;
    829   const int kMidY = rect.y() + rect.height() / 2;
    830 
    831   SkPaint paint;
    832   paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
    833 
    834   SkIRect skrect;
    835   if (slider.vertical)
    836     skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
    837   else
    838     skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
    839 
    840   canvas->drawIRect(skrect, paint);
    841 
    842   paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
    843 
    844   if (slider.vertical)
    845     skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
    846   else
    847     skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
    848 
    849   canvas->drawIRect(skrect, paint);
    850 
    851   paint.setColor(kSliderThumbBorderDarkGrey);
    852   DrawBox(canvas, rect, paint);
    853 
    854   if (rect.height() > 10 && rect.width() > 10) {
    855     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
    856     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
    857     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
    858   }
    859 }
    860 
    861 void NativeThemeBase::PaintInnerSpinButton(SkCanvas* canvas,
    862     State state,
    863     const gfx::Rect& rect,
    864     const InnerSpinButtonExtraParams& spin_button) const {
    865   if (spin_button.read_only)
    866     state = kDisabled;
    867 
    868   State north_state = state;
    869   State south_state = state;
    870   if (spin_button.spin_up)
    871     south_state = south_state != kDisabled ? kNormal : kDisabled;
    872   else
    873     north_state = north_state != kDisabled ? kNormal : kDisabled;
    874 
    875   gfx::Rect half = rect;
    876   half.set_height(rect.height() / 2);
    877   PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
    878 
    879   half.set_y(rect.y() + rect.height() / 2);
    880   PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
    881 }
    882 
    883 void NativeThemeBase::PaintProgressBar(SkCanvas* canvas,
    884     State state,
    885     const gfx::Rect& rect,
    886     const ProgressBarExtraParams& progress_bar) const {
    887   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    888   gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR);
    889   gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed(
    890       IDR_PROGRESS_BORDER_LEFT);
    891   gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed(
    892       IDR_PROGRESS_BORDER_RIGHT);
    893 
    894   DCHECK(bar_image->width() > 0);
    895   DCHECK(rect.width() > 0);
    896 
    897   float tile_scale_y = static_cast<float>(rect.height()) / bar_image->height();
    898 
    899   int dest_left_border_width = left_border_image->width();
    900   int dest_right_border_width = right_border_image->width();
    901 
    902   // Since an implicit float -> int conversion will truncate, we want to make
    903   // sure that if a border is desired, it gets at least one pixel.
    904   if (dest_left_border_width > 0) {
    905     dest_left_border_width = dest_left_border_width * tile_scale_y;
    906     dest_left_border_width = std::max(dest_left_border_width, 1);
    907   }
    908   if (dest_right_border_width > 0) {
    909     dest_right_border_width = dest_right_border_width * tile_scale_y;
    910     dest_right_border_width = std::max(dest_right_border_width, 1);
    911   }
    912 
    913   // Since the width of the progress bar may not be evenly divisible by the
    914   // tile size, in order to make it look right we may need to draw some of the
    915   // with a width of 1 pixel smaller than the rest of the tiles.
    916   int new_tile_width = static_cast<int>(bar_image->width() * tile_scale_y);
    917   new_tile_width = std::max(new_tile_width, 1);
    918 
    919   float tile_scale_x = static_cast<float>(new_tile_width) / bar_image->width();
    920   if (rect.width() % new_tile_width == 0) {
    921     DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale_y,
    922         rect.x(), rect.y(),
    923         rect.width(), rect.height());
    924   } else {
    925     int num_tiles = 1 + rect.width() / new_tile_width;
    926     int overshoot = num_tiles * new_tile_width - rect.width();
    927     // Since |overshoot| represents the number of tiles that were too big, draw
    928     // |overshoot| tiles with their width reduced by 1.
    929     int num_big_tiles = num_tiles - overshoot;
    930     int num_small_tiles = overshoot;
    931     int small_width = new_tile_width - 1;
    932     float small_scale_x = static_cast<float>(small_width) / bar_image->width();
    933     float big_scale_x = tile_scale_x;
    934 
    935     gfx::Rect big_rect = rect;
    936     gfx::Rect small_rect = rect;
    937     big_rect.Inset(0, 0, num_small_tiles*small_width, 0);
    938     small_rect.Inset(num_big_tiles*new_tile_width, 0, 0, 0);
    939 
    940     DrawTiledImage(canvas, *bar_image, 0, 0, big_scale_x, tile_scale_y,
    941       big_rect.x(), big_rect.y(), big_rect.width(), big_rect.height());
    942     DrawTiledImage(canvas, *bar_image, 0, 0, small_scale_x, tile_scale_y,
    943       small_rect.x(), small_rect.y(), small_rect.width(), small_rect.height());
    944   }
    945   if (progress_bar.value_rect_width) {
    946     gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE);
    947 
    948     new_tile_width = static_cast<int>(value_image->width() * tile_scale_y);
    949     tile_scale_x = static_cast<float>(new_tile_width) /
    950         value_image->width();
    951 
    952     DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale_y,
    953         progress_bar.value_rect_x,
    954         progress_bar.value_rect_y,
    955         progress_bar.value_rect_width,
    956         progress_bar.value_rect_height);
    957   }
    958 
    959   DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(),
    960       left_border_image->height(), rect.x(), rect.y(), dest_left_border_width,
    961       rect.height());
    962 
    963   int dest_x = rect.right() - dest_right_border_width;
    964   DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(),
    965                right_border_image->height(), dest_x, rect.y(),
    966                dest_right_border_width, rect.height());
    967 }
    968 
    969 bool NativeThemeBase::IntersectsClipRectInt(SkCanvas* canvas,
    970                                             int x, int y, int w, int h) const {
    971   SkRect clip;
    972   return canvas->getClipBounds(&clip) &&
    973       clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
    974                      SkIntToScalar(y + h));
    975 }
    976 
    977 void NativeThemeBase::DrawImageInt(
    978     SkCanvas* sk_canvas, const gfx::ImageSkia& image,
    979     int src_x, int src_y, int src_w, int src_h,
    980     int dest_x, int dest_y, int dest_w, int dest_h) const {
    981   scoped_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
    982   canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
    983       dest_x, dest_y, dest_w, dest_h, true);
    984 }
    985 
    986 void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
    987     const gfx::ImageSkia& image,
    988     int src_x, int src_y, float tile_scale_x, float tile_scale_y,
    989     int dest_x, int dest_y, int w, int h) const {
    990   scoped_ptr<gfx::Canvas> canvas(CommonThemeCreateCanvas(sk_canvas));
    991   canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
    992       tile_scale_y, dest_x, dest_y, w, h);
    993 }
    994 
    995 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
    996                                              SkScalar saturate_amount,
    997                                              SkScalar brighten_amount) const {
    998   SkScalar color[3];
    999   color[0] = hsv[0];
   1000   color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
   1001   color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
   1002   return SkHSVToColor(color);
   1003 }
   1004 
   1005 SkColor NativeThemeBase::GetArrowColor(State state) const {
   1006   if (state != kDisabled)
   1007     return SK_ColorBLACK;
   1008 
   1009   SkScalar track_hsv[3];
   1010   SkColorToHSV(track_color_, track_hsv);
   1011   SkScalar thumb_hsv[3];
   1012   SkColorToHSV(thumb_inactive_color_, thumb_hsv);
   1013   return OutlineColor(track_hsv, thumb_hsv);
   1014 }
   1015 
   1016 void NativeThemeBase::DrawVertLine(SkCanvas* canvas,
   1017                                    int x,
   1018                                    int y1,
   1019                                    int y2,
   1020                                    const SkPaint& paint) const {
   1021   SkIRect skrect;
   1022   skrect.set(x, y1, x + 1, y2 + 1);
   1023   canvas->drawIRect(skrect, paint);
   1024 }
   1025 
   1026 void NativeThemeBase::DrawHorizLine(SkCanvas* canvas,
   1027                                     int x1,
   1028                                     int x2,
   1029                                     int y,
   1030                                     const SkPaint& paint) const {
   1031   SkIRect skrect;
   1032   skrect.set(x1, y, x2 + 1, y + 1);
   1033   canvas->drawIRect(skrect, paint);
   1034 }
   1035 
   1036 void NativeThemeBase::DrawBox(SkCanvas* canvas,
   1037                               const gfx::Rect& rect,
   1038                               const SkPaint& paint) const {
   1039   const int right = rect.x() + rect.width() - 1;
   1040   const int bottom = rect.y() + rect.height() - 1;
   1041   DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
   1042   DrawVertLine(canvas, right, rect.y(), bottom, paint);
   1043   DrawHorizLine(canvas, rect.x(), right, bottom, paint);
   1044   DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
   1045 }
   1046 
   1047 SkScalar NativeThemeBase::Clamp(SkScalar value,
   1048                                 SkScalar min,
   1049                                 SkScalar max) const {
   1050   return std::min(std::max(value, min), max);
   1051 }
   1052 
   1053 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
   1054   // GTK Theme engines have way too much control over the layout of
   1055   // the scrollbar. We might be able to more closely approximate its
   1056   // look-and-feel, if we sent whole images instead of just colors
   1057   // from the browser to the renderer. But even then, some themes
   1058   // would just break.
   1059   //
   1060   // So, instead, we don't even try to 100% replicate the look of
   1061   // the native scrollbar. We render our own version, but we make
   1062   // sure to pick colors that blend in nicely with the system GTK
   1063   // theme. In most cases, we can just sample a couple of pixels
   1064   // from the system scrollbar and use those colors to draw our
   1065   // scrollbar.
   1066   //
   1067   // This works fine for the track color and the overall thumb
   1068   // color. But it fails spectacularly for the outline color used
   1069   // around the thumb piece.  Not all themes have a clearly defined
   1070   // outline. For some of them it is partially transparent, and for
   1071   // others the thickness is very unpredictable.
   1072   //
   1073   // So, instead of trying to approximate the system theme, we
   1074   // instead try to compute a reasonable looking choice based on the
   1075   // known color of the track and the thumb piece. This is difficult
   1076   // when trying to deal both with high- and low-contrast themes,
   1077   // and both with positive and inverted themes.
   1078   //
   1079   // The following code has been tested to look OK with all of the
   1080   // default GTK themes.
   1081   SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
   1082   SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
   1083 
   1084   if (hsv1[2] + hsv2[2] > 1.0)
   1085     diff = -diff;
   1086 
   1087   return SaturateAndBrighten(hsv2, -0.2f, diff);
   1088 }
   1089 
   1090 }  // namespace ui
   1091