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