Home | History | Annotate | Download | only in controls
      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/views/controls/progress_bar.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     10 #include "base/logging.h"
     11 #include "third_party/skia/include/core/SkPaint.h"
     12 #include "third_party/skia/include/core/SkXfermode.h"
     13 #include "third_party/skia/include/effects/SkGradientShader.h"
     14 #include "ui/base/accessibility/accessible_view_state.h"
     15 #include "ui/gfx/canvas.h"
     16 
     17 namespace {
     18 
     19 // Progress bar's border width.
     20 const int kBorderWidth = 1;
     21 
     22 // Corner radius for the progress bar's border.
     23 const int kCornerRadius = 2;
     24 
     25 // The width of the highlight at the right of the progress bar.
     26 const int kHighlightWidth = 18;
     27 
     28 const SkColor kBackgroundColor = SkColorSetRGB(230, 230, 230);
     29 const SkColor kBackgroundBorderColor = SkColorSetRGB(208, 208, 208);
     30 const SkColor kBarBorderColor = SkColorSetRGB(65, 137, 237);
     31 const SkColor kBarTopColor = SkColorSetRGB(110, 188, 249);
     32 const SkColor kBarColorStart = SkColorSetRGB(86, 167, 247);
     33 const SkColor kBarColorEnd = SkColorSetRGB(76, 148, 245);
     34 const SkColor kBarHighlightEnd = SkColorSetRGB(114, 206, 251);
     35 const SkColor kDisabledBarBorderColor = SkColorSetRGB(191, 191, 191);
     36 const SkColor kDisabledBarColorStart = SkColorSetRGB(224, 224, 224);
     37 const SkColor kDisabledBarColorEnd = SkColorSetRGB(212, 212, 212);
     38 
     39 void AddRoundRectPathWithPadding(int x, int y,
     40                                  int w, int h,
     41                                  int corner_radius,
     42                                  SkScalar padding,
     43                                  SkPath* path) {
     44   DCHECK(path);
     45   SkRect rect;
     46   rect.set(
     47       SkIntToScalar(x) + padding, SkIntToScalar(y) + padding,
     48       SkIntToScalar(x + w) - padding, SkIntToScalar(y + h) - padding);
     49   path->addRoundRect(
     50       rect,
     51       SkIntToScalar(corner_radius) - padding,
     52       SkIntToScalar(corner_radius) - padding);
     53 }
     54 
     55 void AddRoundRectPath(int x, int y,
     56                       int w, int h,
     57                       int corner_radius,
     58                       SkPath* path) {
     59   AddRoundRectPathWithPadding(x, y, w, h, corner_radius, SK_ScalarHalf, path);
     60 }
     61 
     62 void FillRoundRect(gfx::Canvas* canvas,
     63                    int x, int y,
     64                    int w, int h,
     65                    int corner_radius,
     66                    const SkColor colors[],
     67                    const SkScalar points[],
     68                    int count,
     69                    bool gradient_horizontal) {
     70   SkPath path;
     71   AddRoundRectPath(x, y, w, h, corner_radius, &path);
     72   SkPaint paint;
     73   paint.setStyle(SkPaint::kFill_Style);
     74   paint.setFlags(SkPaint::kAntiAlias_Flag);
     75 
     76   SkPoint p[2];
     77   p[0].iset(x, y);
     78   if (gradient_horizontal) {
     79     p[1].iset(x + w, y);
     80   } else {
     81     p[1].iset(x, y + h);
     82   }
     83   skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear(
     84       p, colors, points, count, SkShader::kClamp_TileMode, NULL));
     85   paint.setShader(s.get());
     86 
     87   canvas->DrawPath(path, paint);
     88 }
     89 
     90 void FillRoundRect(gfx::Canvas* canvas,
     91                    int x, int y,
     92                    int w, int h,
     93                    int corner_radius,
     94                    SkColor gradient_start_color,
     95                    SkColor gradient_end_color,
     96                    bool gradient_horizontal) {
     97   if (gradient_start_color != gradient_end_color) {
     98     SkColor colors[2] = { gradient_start_color, gradient_end_color };
     99     FillRoundRect(canvas, x, y, w, h, corner_radius,
    100                   colors, NULL, 2, gradient_horizontal);
    101   } else {
    102     SkPath path;
    103     AddRoundRectPath(x, y, w, h, corner_radius, &path);
    104     SkPaint paint;
    105     paint.setStyle(SkPaint::kFill_Style);
    106     paint.setFlags(SkPaint::kAntiAlias_Flag);
    107     paint.setColor(gradient_start_color);
    108     canvas->DrawPath(path, paint);
    109   }
    110 }
    111 
    112 void StrokeRoundRect(gfx::Canvas* canvas,
    113                      int x, int y,
    114                      int w, int h,
    115                      int corner_radius,
    116                      SkColor stroke_color,
    117                      int stroke_width) {
    118   SkPath path;
    119   AddRoundRectPath(x, y, w, h, corner_radius, &path);
    120   SkPaint paint;
    121   paint.setShader(NULL);
    122   paint.setColor(stroke_color);
    123   paint.setStyle(SkPaint::kStroke_Style);
    124   paint.setFlags(SkPaint::kAntiAlias_Flag);
    125   paint.setStrokeWidth(SkIntToScalar(stroke_width));
    126   canvas->DrawPath(path, paint);
    127 }
    128 
    129 }  // namespace
    130 
    131 namespace views {
    132 
    133 // static
    134 const char ProgressBar::kViewClassName[] = "ProgressBar";
    135 
    136 ProgressBar::ProgressBar()
    137     : min_display_value_(0.0),
    138       max_display_value_(1.0),
    139       current_value_(0.0) {
    140 }
    141 
    142 ProgressBar::~ProgressBar() {
    143 }
    144 
    145 double ProgressBar::GetNormalizedValue() const {
    146   const double capped_value = std::min(
    147       std::max(current_value_, min_display_value_), max_display_value_);
    148   return (capped_value - min_display_value_) /
    149       (max_display_value_ - min_display_value_);
    150 }
    151 
    152 void ProgressBar::SetDisplayRange(double min_display_value,
    153                                   double max_display_value) {
    154   if (min_display_value != min_display_value_ ||
    155       max_display_value != max_display_value_) {
    156     DCHECK(min_display_value < max_display_value);
    157     min_display_value_ = min_display_value;
    158     max_display_value_ = max_display_value;
    159     SchedulePaint();
    160   }
    161 }
    162 
    163 void ProgressBar::SetValue(double value) {
    164   if (value != current_value_) {
    165     current_value_ = value;
    166     SchedulePaint();
    167   }
    168 }
    169 
    170 void ProgressBar::SetTooltipText(const string16& tooltip_text) {
    171   tooltip_text_ = tooltip_text;
    172 }
    173 
    174 bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
    175   DCHECK(tooltip);
    176   *tooltip = tooltip_text_;
    177   return !tooltip_text_.empty();
    178 }
    179 
    180 void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) {
    181   state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR;
    182   state->state = ui::AccessibilityTypes::STATE_READONLY;
    183 }
    184 
    185 gfx::Size ProgressBar::GetPreferredSize() {
    186   gfx::Size pref_size(100, 11);
    187   gfx::Insets insets = GetInsets();
    188   pref_size.Enlarge(insets.width(), insets.height());
    189   return pref_size;
    190 }
    191 
    192 const char* ProgressBar::GetClassName() const {
    193   return kViewClassName;
    194 }
    195 
    196 void ProgressBar::OnPaint(gfx::Canvas* canvas) {
    197   gfx::Rect content_bounds = GetContentsBounds();
    198   int bar_left = content_bounds.x();
    199   int bar_top = content_bounds.y();
    200   int bar_width = content_bounds.width();
    201   int bar_height = content_bounds.height();
    202 
    203   const int progress_width =
    204       static_cast<int>(bar_width * GetNormalizedValue() + 0.5);
    205 
    206   // Draw background.
    207   FillRoundRect(canvas,
    208                 bar_left, bar_top, bar_width, bar_height,
    209                 kCornerRadius,
    210                 kBackgroundColor, kBackgroundColor,
    211                 false);
    212   StrokeRoundRect(canvas,
    213                   bar_left, bar_top,
    214                   bar_width, bar_height,
    215                   kCornerRadius,
    216                   kBackgroundBorderColor,
    217                   kBorderWidth);
    218 
    219   if (progress_width > 1) {
    220     // Draw inner if wide enough.
    221     if (progress_width > kBorderWidth * 2) {
    222       canvas->Save();
    223 
    224       SkPath inner_path;
    225       AddRoundRectPathWithPadding(
    226           bar_left, bar_top, progress_width, bar_height,
    227           kCornerRadius,
    228           0,
    229           &inner_path);
    230       canvas->ClipPath(inner_path);
    231 
    232       const SkColor bar_colors[] = {
    233         kBarTopColor,
    234         kBarTopColor,
    235         kBarColorStart,
    236         kBarColorEnd,
    237         kBarColorEnd,
    238       };
    239       // We want a thin 1-pixel line for kBarTopColor.
    240       SkScalar scalar_height = SkIntToScalar(bar_height);
    241       SkScalar highlight_width = SkScalarDiv(SK_Scalar1, scalar_height);
    242       SkScalar border_width = SkScalarDiv(SkIntToScalar(kBorderWidth),
    243                                           scalar_height);
    244       const SkScalar bar_points[] = {
    245         0,
    246         border_width,
    247         border_width + highlight_width,
    248         SK_Scalar1 - border_width,
    249         SK_Scalar1,
    250       };
    251 
    252       const SkColor disabled_bar_colors[] = {
    253         kDisabledBarColorStart,
    254         kDisabledBarColorStart,
    255         kDisabledBarColorEnd,
    256         kDisabledBarColorEnd,
    257       };
    258 
    259       const SkScalar disabled_bar_points[] = {
    260         0,
    261         border_width,
    262         SK_Scalar1 - border_width,
    263         SK_Scalar1
    264       };
    265 
    266       // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps
    267       // between the inner and the border.
    268       FillRoundRect(canvas,
    269                     bar_left, bar_top,
    270                     progress_width, bar_height,
    271                     kCornerRadius,
    272                     enabled() ? bar_colors : disabled_bar_colors,
    273                     enabled() ? bar_points : disabled_bar_points,
    274                     enabled() ? arraysize(bar_colors) :
    275                         arraysize(disabled_bar_colors),
    276                     false);
    277 
    278       if (enabled()) {
    279         // Draw the highlight to the right.
    280         const SkColor highlight_colors[] = {
    281           SkColorSetA(kBarHighlightEnd, 0),
    282           kBarHighlightEnd,
    283           kBarHighlightEnd,
    284         };
    285         const SkScalar highlight_points[] = {
    286           0,
    287           SK_Scalar1 - SkScalarDiv(SkIntToScalar(kBorderWidth), scalar_height),
    288           SK_Scalar1,
    289         };
    290         SkPaint paint;
    291         paint.setStyle(SkPaint::kFill_Style);
    292         paint.setFlags(SkPaint::kAntiAlias_Flag);
    293 
    294         SkPoint p[2];
    295         int highlight_left =
    296             std::max(0, progress_width - kHighlightWidth - kBorderWidth);
    297         p[0].iset(highlight_left, 0);
    298         p[1].iset(progress_width, 0);
    299         skia::RefPtr<SkShader> s =
    300             skia::AdoptRef(SkGradientShader::CreateLinear(
    301                 p, highlight_colors, highlight_points,
    302                 arraysize(highlight_colors), SkShader::kClamp_TileMode, NULL));
    303         paint.setShader(s.get());
    304         paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
    305         canvas->DrawRect(gfx::Rect(highlight_left, 0,
    306                                    kHighlightWidth + kBorderWidth, bar_height),
    307                          paint);
    308       }
    309 
    310       canvas->Restore();
    311     }
    312 
    313     // Draw bar stroke
    314     StrokeRoundRect(canvas,
    315                     bar_left, bar_top, progress_width, bar_height,
    316                     kCornerRadius,
    317                     enabled() ? kBarBorderColor : kDisabledBarBorderColor,
    318                     kBorderWidth);
    319   }
    320 }
    321 
    322 }  // namespace views
    323