Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Side by Side Diff: ui/views/controls/progress_bar.cc

Issue 2329633003: Implement progress bar spec (determinate and indeterminate). (Closed)
Patch Set: attempt to unbreak mac and unit test Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/controls/progress_bar.h ('k') | ui/views/controls/progress_bar_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/progress_bar.h" 5 #include "ui/views/controls/progress_bar.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "third_party/skia/include/core/SkPaint.h" 12 #include "third_party/skia/include/core/SkPaint.h"
13 #include "third_party/skia/include/core/SkPath.h" 13 #include "third_party/skia/include/core/SkPath.h"
14 #include "third_party/skia/include/core/SkXfermode.h" 14 #include "third_party/skia/include/core/SkXfermode.h"
15 #include "third_party/skia/include/effects/SkGradientShader.h" 15 #include "third_party/skia/include/effects/SkGradientShader.h"
16 #include "ui/accessibility/ax_view_state.h" 16 #include "ui/accessibility/ax_view_state.h"
17 #include "ui/gfx/animation/linear_animation.h"
17 #include "ui/gfx/canvas.h" 18 #include "ui/gfx/canvas.h"
19 #include "ui/gfx/color_utils.h"
20 #include "ui/native_theme/native_theme.h"
21
22 namespace views {
18 23
19 namespace { 24 namespace {
20 25
21 // Progress bar's border width. 26 // In DP, the amount to round the corners of the progress bar (both bg and
22 const int kBorderWidth = 1; 27 // fg, aka slice).
28 const int kCornerRadius = 3;
23 29
24 // Corner radius for the progress bar's border. 30 // Adds a rectangle to the path. The corners will be rounded if there is room.
25 const int kCornerRadius = 2; 31 void AddPossiblyRoundRectToPath(const gfx::Rect& rectangle, SkPath* path) {
26 32 if (rectangle.height() < kCornerRadius) {
27 // The width of the highlight at the right of the progress bar. 33 path->addRect(gfx::RectToSkRect(rectangle));
28 const int kHighlightWidth = 18;
29
30 const SkColor kBackgroundColor = SkColorSetRGB(230, 230, 230);
31 const SkColor kBackgroundBorderColor = SkColorSetRGB(208, 208, 208);
32 const SkColor kBarBorderColor = SkColorSetRGB(65, 137, 237);
33 const SkColor kBarTopColor = SkColorSetRGB(110, 188, 249);
34 const SkColor kBarColorStart = SkColorSetRGB(86, 167, 247);
35 const SkColor kBarColorEnd = SkColorSetRGB(76, 148, 245);
36 const SkColor kBarHighlightEnd = SkColorSetRGB(114, 206, 251);
37 const SkColor kDisabledBarBorderColor = SkColorSetRGB(191, 191, 191);
38 const SkColor kDisabledBarColorStart = SkColorSetRGB(224, 224, 224);
39 const SkColor kDisabledBarColorEnd = SkColorSetRGB(212, 212, 212);
40
41 void AddRoundRectPathWithPadding(int x, int y,
42 int w, int h,
43 int corner_radius,
44 SkScalar padding,
45 SkPath* path) {
46 DCHECK(path);
47 SkRect rect;
48 rect.set(
49 SkIntToScalar(x) + padding, SkIntToScalar(y) + padding,
50 SkIntToScalar(x + w) - padding, SkIntToScalar(y + h) - padding);
51 path->addRoundRect(
52 rect,
53 SkIntToScalar(corner_radius) - padding,
54 SkIntToScalar(corner_radius) - padding);
55 }
56
57 void AddRoundRectPath(int x, int y,
58 int w, int h,
59 int corner_radius,
60 SkPath* path) {
61 AddRoundRectPathWithPadding(x, y, w, h, corner_radius, SK_ScalarHalf, path);
62 }
63
64 void FillRoundRect(gfx::Canvas* canvas,
65 int x, int y,
66 int w, int h,
67 int corner_radius,
68 const SkColor colors[],
69 const SkScalar points[],
70 int count,
71 bool gradient_horizontal) {
72 SkPath path;
73 AddRoundRectPath(x, y, w, h, corner_radius, &path);
74 SkPaint paint;
75 paint.setStyle(SkPaint::kFill_Style);
76 paint.setFlags(SkPaint::kAntiAlias_Flag);
77
78 SkPoint p[2];
79 p[0].iset(x, y);
80 if (gradient_horizontal) {
81 p[1].iset(x + w, y);
82 } else { 34 } else {
83 p[1].iset(x, y + h); 35 path->addRoundRect(gfx::RectToSkRect(rectangle), kCornerRadius,
36 kCornerRadius);
84 } 37 }
85 paint.setShader(SkGradientShader::MakeLinear(p, colors, points, count,
86 SkShader::kClamp_TileMode));
87
88 canvas->DrawPath(path, paint);
89 }
90
91 void FillRoundRect(gfx::Canvas* canvas,
92 int x, int y,
93 int w, int h,
94 int corner_radius,
95 SkColor gradient_start_color,
96 SkColor gradient_end_color,
97 bool gradient_horizontal) {
98 if (gradient_start_color != gradient_end_color) {
99 SkColor colors[2] = { gradient_start_color, gradient_end_color };
100 FillRoundRect(canvas, x, y, w, h, corner_radius,
101 colors, NULL, 2, gradient_horizontal);
102 } else {
103 SkPath path;
104 AddRoundRectPath(x, y, w, h, corner_radius, &path);
105 SkPaint paint;
106 paint.setStyle(SkPaint::kFill_Style);
107 paint.setFlags(SkPaint::kAntiAlias_Flag);
108 paint.setColor(gradient_start_color);
109 canvas->DrawPath(path, paint);
110 }
111 }
112
113 void StrokeRoundRect(gfx::Canvas* canvas,
114 int x, int y,
115 int w, int h,
116 int corner_radius,
117 SkColor stroke_color,
118 int stroke_width) {
119 SkPath path;
120 AddRoundRectPath(x, y, w, h, corner_radius, &path);
121 SkPaint paint;
122 paint.setShader(NULL);
123 paint.setColor(stroke_color);
124 paint.setStyle(SkPaint::kStroke_Style);
125 paint.setFlags(SkPaint::kAntiAlias_Flag);
126 paint.setStrokeWidth(SkIntToScalar(stroke_width));
127 canvas->DrawPath(path, paint);
128 } 38 }
129 39
130 } // namespace 40 } // namespace
131 41
132 namespace views {
133
134 // static 42 // static
135 const char ProgressBar::kViewClassName[] = "ProgressBar"; 43 const char ProgressBar::kViewClassName[] = "ProgressBar";
136 44
137 ProgressBar::ProgressBar() 45 ProgressBar::ProgressBar(int preferred_height)
138 : min_display_value_(0.0), 46 : preferred_height_(preferred_height) {}
139 max_display_value_(1.0),
140 current_value_(0.0) {
141 }
142 47
143 ProgressBar::~ProgressBar() { 48 ProgressBar::~ProgressBar() {
144 } 49 }
145 50
146 double ProgressBar::GetNormalizedValue() const {
147 const double capped_value = std::min(
148 std::max(current_value_, min_display_value_), max_display_value_);
149 return (capped_value - min_display_value_) /
150 (max_display_value_ - min_display_value_);
151 }
152
153 void ProgressBar::SetDisplayRange(double min_display_value,
154 double max_display_value) {
155 if (min_display_value != min_display_value_ ||
156 max_display_value != max_display_value_) {
157 DCHECK(min_display_value < max_display_value);
158 min_display_value_ = min_display_value;
159 max_display_value_ = max_display_value;
160 SchedulePaint();
161 }
162 }
163
164 void ProgressBar::SetValue(double value) {
165 if (value != current_value_) {
166 current_value_ = value;
167 SchedulePaint();
168 }
169 }
170
171 void ProgressBar::SetTooltipText(const base::string16& tooltip_text) {
172 tooltip_text_ = tooltip_text;
173 }
174
175 bool ProgressBar::GetTooltipText(const gfx::Point& p,
176 base::string16* tooltip) const {
177 DCHECK(tooltip);
178 *tooltip = tooltip_text_;
179 return !tooltip_text_.empty();
180 }
181
182 void ProgressBar::GetAccessibleState(ui::AXViewState* state) { 51 void ProgressBar::GetAccessibleState(ui::AXViewState* state) {
183 state->role = ui::AX_ROLE_PROGRESS_INDICATOR; 52 state->role = ui::AX_ROLE_PROGRESS_INDICATOR;
184 state->AddStateFlag(ui::AX_STATE_READ_ONLY); 53 state->AddStateFlag(ui::AX_STATE_READ_ONLY);
185 } 54 }
186 55
187 gfx::Size ProgressBar::GetPreferredSize() const { 56 gfx::Size ProgressBar::GetPreferredSize() const {
188 gfx::Size pref_size(100, 11); 57 // The width will typically be ignored.
58 gfx::Size pref_size(1, preferred_height_);
189 gfx::Insets insets = GetInsets(); 59 gfx::Insets insets = GetInsets();
190 pref_size.Enlarge(insets.width(), insets.height()); 60 pref_size.Enlarge(insets.width(), insets.height());
191 return pref_size; 61 return pref_size;
192 } 62 }
193 63
194 const char* ProgressBar::GetClassName() const { 64 const char* ProgressBar::GetClassName() const {
195 return kViewClassName; 65 return kViewClassName;
196 } 66 }
197 67
198 void ProgressBar::OnPaint(gfx::Canvas* canvas) { 68 void ProgressBar::OnPaint(gfx::Canvas* canvas) {
69 if (IsIndeterminate())
70 return OnPaintIndeterminate(canvas);
71
199 gfx::Rect content_bounds = GetContentsBounds(); 72 gfx::Rect content_bounds = GetContentsBounds();
200 int bar_left = content_bounds.x();
201 int bar_top = content_bounds.y();
202 int bar_width = content_bounds.width();
203 int bar_height = content_bounds.height();
204
205 const int progress_width =
206 static_cast<int>(bar_width * GetNormalizedValue() + 0.5);
207 73
208 // Draw background. 74 // Draw background.
209 FillRoundRect(canvas, 75 SkPath background_path;
210 bar_left, bar_top, bar_width, bar_height, 76 AddPossiblyRoundRectToPath(content_bounds, &background_path);
211 kCornerRadius, 77 SkPaint background_paint;
212 kBackgroundColor, kBackgroundColor, 78 background_paint.setStyle(SkPaint::kFill_Style);
213 false); 79 background_paint.setFlags(SkPaint::kAntiAlias_Flag);
214 StrokeRoundRect(canvas, 80 background_paint.setColor(GetBackgroundColor());
215 bar_left, bar_top, 81 canvas->DrawPath(background_path, background_paint);
216 bar_width, bar_height,
217 kCornerRadius,
218 kBackgroundBorderColor,
219 kBorderWidth);
220 82
221 if (progress_width > 1) { 83 // Draw slice.
222 // Draw inner if wide enough. 84 SkPath slice_path;
223 if (progress_width > kBorderWidth * 2) { 85 const int slice_width = static_cast<int>(
224 canvas->Save(); 86 content_bounds.width() * std::min(current_value_, 1.0) + 0.5);
87 if (slice_width < 1)
88 return;
225 89
226 SkPath inner_path; 90 gfx::Rect slice_bounds = content_bounds;
227 AddRoundRectPathWithPadding( 91 slice_bounds.set_width(slice_width);
228 bar_left, bar_top, progress_width, bar_height, 92 AddPossiblyRoundRectToPath(slice_bounds, &slice_path);
229 kCornerRadius,
230 0,
231 &inner_path);
232 canvas->ClipPath(inner_path, false);
233 93
234 const SkColor bar_colors[] = { 94 SkPaint slice_paint;
235 kBarTopColor, 95 slice_paint.setStyle(SkPaint::kFill_Style);
236 kBarTopColor, 96 slice_paint.setFlags(SkPaint::kAntiAlias_Flag);
237 kBarColorStart, 97 slice_paint.setColor(GetForegroundColor());
238 kBarColorEnd, 98 canvas->DrawPath(slice_path, slice_paint);
239 kBarColorEnd, 99 }
240 };
241 // We want a thin 1-pixel line for kBarTopColor.
242 SkScalar scalar_height = SkIntToScalar(bar_height);
243 SkScalar highlight_width = 1 / scalar_height;
244 SkScalar border_width = kBorderWidth / 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 100
253 const SkColor disabled_bar_colors[] = { 101 void ProgressBar::SetValue(double value) {
254 kDisabledBarColorStart, 102 double adjusted_value = (value < 0.0 || value > 1.0) ? -1.0 : value;
255 kDisabledBarColorStart,
256 kDisabledBarColorEnd,
257 kDisabledBarColorEnd,
258 };
259 103
260 const SkScalar disabled_bar_points[] = { 104 if (adjusted_value == current_value_)
261 0, 105 return;
262 border_width,
263 SK_Scalar1 - border_width,
264 SK_Scalar1
265 };
266 106
267 // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps 107 current_value_ = adjusted_value;
268 // between the inner and the border. 108 if (IsIndeterminate()) {
269 FillRoundRect(canvas, 109 indeterminate_bar_animation_.reset(new gfx::LinearAnimation(this));
270 bar_left, bar_top, 110 indeterminate_bar_animation_->SetDuration(2000); // In milliseconds.
271 progress_width, bar_height, 111 indeterminate_bar_animation_->Start();
272 kCornerRadius, 112 } else {
273 enabled() ? bar_colors : disabled_bar_colors, 113 indeterminate_bar_animation_.reset();
274 enabled() ? bar_points : disabled_bar_points, 114 SchedulePaint();
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, SK_Scalar1 - kBorderWidth / scalar_height, SK_Scalar1,
288 };
289 SkPaint paint;
290 paint.setStyle(SkPaint::kFill_Style);
291 paint.setFlags(SkPaint::kAntiAlias_Flag);
292
293 SkPoint p[2];
294 int highlight_left =
295 std::max(0, progress_width - kHighlightWidth - kBorderWidth);
296 p[0].iset(highlight_left, 0);
297 p[1].iset(progress_width, 0);
298 paint.setShader(SkGradientShader::MakeLinear(
299 p, highlight_colors, highlight_points, arraysize(highlight_colors),
300 SkShader::kClamp_TileMode));
301 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
302 canvas->DrawRect(gfx::Rect(highlight_left, 0,
303 kHighlightWidth + kBorderWidth, bar_height),
304 paint);
305 }
306
307 canvas->Restore();
308 }
309
310 // Draw bar stroke
311 StrokeRoundRect(canvas,
312 bar_left, bar_top, progress_width, bar_height,
313 kCornerRadius,
314 enabled() ? kBarBorderColor : kDisabledBarBorderColor,
315 kBorderWidth);
316 } 115 }
317 } 116 }
318 117
118 SkColor ProgressBar::GetForegroundColor() const {
119 return GetNativeTheme()->GetSystemColor(
120 ui::NativeTheme::kColorId_CallToActionColor);
121 }
122
123 SkColor ProgressBar::GetBackgroundColor() const {
124 // The default foreground is GoogleBlue500, and the default background is
125 // that color but 80% lighter.
126 return color_utils::BlendTowardOppositeLuma(GetForegroundColor(), 0xCC);
127 }
128
129 void ProgressBar::AnimationProgressed(const gfx::Animation* animation) {
130 DCHECK_EQ(animation, indeterminate_bar_animation_.get());
131 DCHECK(IsIndeterminate());
132 SchedulePaint();
133 }
134
135 void ProgressBar::AnimationEnded(const gfx::Animation* animation) {
136 DCHECK_EQ(animation, indeterminate_bar_animation_.get());
137 // Restarts animation.
138 if (IsIndeterminate())
139 indeterminate_bar_animation_->Start();
140 }
141
142 bool ProgressBar::IsIndeterminate() {
143 return current_value_ < 0.0;
144 }
145
146 void ProgressBar::OnPaintIndeterminate(gfx::Canvas* canvas) {
147 gfx::Rect content_bounds = GetContentsBounds();
148
149 // Draw background.
150 SkPath background_path;
151 AddPossiblyRoundRectToPath(content_bounds, &background_path);
152 SkPaint background_paint;
153 background_paint.setStyle(SkPaint::kFill_Style);
154 background_paint.setFlags(SkPaint::kAntiAlias_Flag);
155 background_paint.setColor(GetBackgroundColor());
156 canvas->DrawPath(background_path, background_paint);
157
158 // Draw slice.
159 SkPath slice_path;
160 double time = indeterminate_bar_animation_->GetCurrentValue();
161
162 // The animation spec corresponds to the material design lite's parameter.
163 // (cf. https://github.com/google/material-design-lite/)
164 double bar1_left;
165 double bar1_width;
166 double bar2_left;
167 double bar2_width;
168 if (time < 0.50) {
169 bar1_left = time / 2;
170 bar1_width = time * 1.5;
171 bar2_left = 0;
172 bar2_width = 0;
173 } else if (time < 0.75) {
174 bar1_left = time * 3 - 1.25;
175 bar1_width = 0.75 - (time - 0.5) * 3;
176 bar2_left = 0;
177 bar2_width = time - 0.5;
178 } else {
179 bar1_left = 1;
180 bar1_width = 0;
181 bar2_left = (time - 0.75) * 4;
182 bar2_width = 0.25 - (time - 0.75);
183 }
184
185 int bar1_x = static_cast<int>(content_bounds.width() * bar1_left);
186 int bar1_w =
187 std::min(static_cast<int>(content_bounds.width() * bar1_width + 0.5),
188 content_bounds.width() - bar1_x);
189 int bar2_x = static_cast<int>(content_bounds.width() * bar2_left);
190 int bar2_w =
191 std::min(static_cast<int>(content_bounds.width() * bar2_width + 0.5),
192 content_bounds.width() - bar2_x);
193
194 gfx::Rect slice_bounds = content_bounds;
195 slice_bounds.set_x(content_bounds.x() + bar1_x);
196 slice_bounds.set_width(bar1_w);
197 AddPossiblyRoundRectToPath(slice_bounds, &slice_path);
198 slice_bounds.set_x(content_bounds.x() + bar2_x);
199 slice_bounds.set_width(bar2_w);
200 AddPossiblyRoundRectToPath(slice_bounds, &slice_path);
201
202 SkPaint slice_paint;
203 slice_paint.setStyle(SkPaint::kFill_Style);
204 slice_paint.setFlags(SkPaint::kAntiAlias_Flag);
205 slice_paint.setColor(GetForegroundColor());
206 canvas->DrawPath(slice_path, slice_paint);
207 }
208
319 } // namespace views 209 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/progress_bar.h ('k') | ui/views/controls/progress_bar_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698