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