• 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/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 
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, NULL));
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 string16 & tooltip_text)170 void ProgressBar::SetTooltipText(const string16& tooltip_text) {
171   tooltip_text_ = tooltip_text;
172 }
173 
GetTooltipText(const gfx::Point & p,string16 * tooltip) const174 bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
175   DCHECK(tooltip);
176   *tooltip = tooltip_text_;
177   return !tooltip_text_.empty();
178 }
179 
GetAccessibleState(ui::AccessibleViewState * state)180 void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) {
181   state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR;
182   state->state = ui::AccessibilityTypes::STATE_READONLY;
183 }
184 
GetPreferredSize()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 
GetClassName() const192 const char* ProgressBar::GetClassName() const {
193   return kViewClassName;
194 }
195 
OnPaint(gfx::Canvas * canvas)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