• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/accessibility/ax_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 
AddRoundRectPathWithPadding(int x,int y,int w,int h,int corner_radius,SkScalar padding,SkPath * path)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 
AddRoundRectPath(int x,int y,int w,int h,int corner_radius,SkPath * path)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 
FillRoundRect(gfx::Canvas * canvas,int x,int y,int w,int h,int corner_radius,const SkColor colors[],const SkScalar points[],int count,bool gradient_horizontal)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));
85   paint.setShader(s.get());
86 
87   canvas->DrawPath(path, paint);
88 }
89 
FillRoundRect(gfx::Canvas * canvas,int x,int y,int w,int h,int corner_radius,SkColor gradient_start_color,SkColor gradient_end_color,bool gradient_horizontal)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 
StrokeRoundRect(gfx::Canvas * canvas,int x,int y,int w,int h,int corner_radius,SkColor stroke_color,int stroke_width)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 
ProgressBar()136 ProgressBar::ProgressBar()
137     : min_display_value_(0.0),
138       max_display_value_(1.0),
139       current_value_(0.0) {
140 }
141 
~ProgressBar()142 ProgressBar::~ProgressBar() {
143 }
144 
GetNormalizedValue() const145 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 
SetDisplayRange(double min_display_value,double max_display_value)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 
SetValue(double value)163 void ProgressBar::SetValue(double value) {
164   if (value != current_value_) {
165     current_value_ = value;
166     SchedulePaint();
167   }
168 }
169 
SetTooltipText(const base::string16 & tooltip_text)170 void ProgressBar::SetTooltipText(const base::string16& tooltip_text) {
171   tooltip_text_ = tooltip_text;
172 }
173 
GetTooltipText(const gfx::Point & p,base::string16 * tooltip) const174 bool ProgressBar::GetTooltipText(const gfx::Point& p,
175                                  base::string16* tooltip) const {
176   DCHECK(tooltip);
177   *tooltip = tooltip_text_;
178   return !tooltip_text_.empty();
179 }
180 
GetAccessibleState(ui::AXViewState * state)181 void ProgressBar::GetAccessibleState(ui::AXViewState* state) {
182   state->role = ui::AX_ROLE_PROGRESS_INDICATOR;
183   state->AddStateFlag(ui::AX_STATE_READ_ONLY);
184 }
185 
GetPreferredSize() const186 gfx::Size ProgressBar::GetPreferredSize() const {
187   gfx::Size pref_size(100, 11);
188   gfx::Insets insets = GetInsets();
189   pref_size.Enlarge(insets.width(), insets.height());
190   return pref_size;
191 }
192 
GetClassName() const193 const char* ProgressBar::GetClassName() const {
194   return kViewClassName;
195 }
196 
OnPaint(gfx::Canvas * canvas)197 void ProgressBar::OnPaint(gfx::Canvas* canvas) {
198   gfx::Rect content_bounds = GetContentsBounds();
199   int bar_left = content_bounds.x();
200   int bar_top = content_bounds.y();
201   int bar_width = content_bounds.width();
202   int bar_height = content_bounds.height();
203 
204   const int progress_width =
205       static_cast<int>(bar_width * GetNormalizedValue() + 0.5);
206 
207   // Draw background.
208   FillRoundRect(canvas,
209                 bar_left, bar_top, bar_width, bar_height,
210                 kCornerRadius,
211                 kBackgroundColor, kBackgroundColor,
212                 false);
213   StrokeRoundRect(canvas,
214                   bar_left, bar_top,
215                   bar_width, bar_height,
216                   kCornerRadius,
217                   kBackgroundBorderColor,
218                   kBorderWidth);
219 
220   if (progress_width > 1) {
221     // Draw inner if wide enough.
222     if (progress_width > kBorderWidth * 2) {
223       canvas->Save();
224 
225       SkPath inner_path;
226       AddRoundRectPathWithPadding(
227           bar_left, bar_top, progress_width, bar_height,
228           kCornerRadius,
229           0,
230           &inner_path);
231       canvas->ClipPath(inner_path, false);
232 
233       const SkColor bar_colors[] = {
234         kBarTopColor,
235         kBarTopColor,
236         kBarColorStart,
237         kBarColorEnd,
238         kBarColorEnd,
239       };
240       // We want a thin 1-pixel line for kBarTopColor.
241       SkScalar scalar_height = SkIntToScalar(bar_height);
242       SkScalar highlight_width = SkScalarDiv(SK_Scalar1, scalar_height);
243       SkScalar border_width = SkScalarDiv(SkIntToScalar(kBorderWidth),
244                                           scalar_height);
245       const SkScalar bar_points[] = {
246         0,
247         border_width,
248         border_width + highlight_width,
249         SK_Scalar1 - border_width,
250         SK_Scalar1,
251       };
252 
253       const SkColor disabled_bar_colors[] = {
254         kDisabledBarColorStart,
255         kDisabledBarColorStart,
256         kDisabledBarColorEnd,
257         kDisabledBarColorEnd,
258       };
259 
260       const SkScalar disabled_bar_points[] = {
261         0,
262         border_width,
263         SK_Scalar1 - border_width,
264         SK_Scalar1
265       };
266 
267       // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps
268       // between the inner and the border.
269       FillRoundRect(canvas,
270                     bar_left, bar_top,
271                     progress_width, bar_height,
272                     kCornerRadius,
273                     enabled() ? bar_colors : disabled_bar_colors,
274                     enabled() ? bar_points : disabled_bar_points,
275                     enabled() ? arraysize(bar_colors) :
276                         arraysize(disabled_bar_colors),
277                     false);
278 
279       if (enabled()) {
280         // Draw the highlight to the right.
281         const SkColor highlight_colors[] = {
282           SkColorSetA(kBarHighlightEnd, 0),
283           kBarHighlightEnd,
284           kBarHighlightEnd,
285         };
286         const SkScalar highlight_points[] = {
287           0,
288           SK_Scalar1 - SkScalarDiv(SkIntToScalar(kBorderWidth), scalar_height),
289           SK_Scalar1,
290         };
291         SkPaint paint;
292         paint.setStyle(SkPaint::kFill_Style);
293         paint.setFlags(SkPaint::kAntiAlias_Flag);
294 
295         SkPoint p[2];
296         int highlight_left =
297             std::max(0, progress_width - kHighlightWidth - kBorderWidth);
298         p[0].iset(highlight_left, 0);
299         p[1].iset(progress_width, 0);
300         skia::RefPtr<SkShader> s =
301             skia::AdoptRef(SkGradientShader::CreateLinear(
302                 p, highlight_colors, highlight_points,
303                 arraysize(highlight_colors), SkShader::kClamp_TileMode));
304         paint.setShader(s.get());
305         paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
306         canvas->DrawRect(gfx::Rect(highlight_left, 0,
307                                    kHighlightWidth + kBorderWidth, bar_height),
308                          paint);
309       }
310 
311       canvas->Restore();
312     }
313 
314     // Draw bar stroke
315     StrokeRoundRect(canvas,
316                     bar_left, bar_top, progress_width, bar_height,
317                     kCornerRadius,
318                     enabled() ? kBarBorderColor : kDisabledBarBorderColor,
319                     kBorderWidth);
320   }
321 }
322 
323 }  // namespace views
324