Chromium Code Reviews| OLD | NEW |
|---|---|
| 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() : current_value_(0.0), preferred_height_(5) {} |
| 138 : min_display_value_(0.0), | |
| 139 max_display_value_(1.0), | |
| 140 current_value_(0.0) { | |
| 141 } | |
| 142 | 46 |
| 143 ProgressBar::~ProgressBar() { | 47 ProgressBar::~ProgressBar() { |
| 144 } | 48 } |
| 145 | 49 |
| 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) { | 50 void ProgressBar::GetAccessibleState(ui::AXViewState* state) { |
| 183 state->role = ui::AX_ROLE_PROGRESS_INDICATOR; | 51 state->role = ui::AX_ROLE_PROGRESS_INDICATOR; |
| 184 state->AddStateFlag(ui::AX_STATE_READ_ONLY); | 52 state->AddStateFlag(ui::AX_STATE_READ_ONLY); |
| 185 } | 53 } |
| 186 | 54 |
| 187 gfx::Size ProgressBar::GetPreferredSize() const { | 55 gfx::Size ProgressBar::GetPreferredSize() const { |
| 188 gfx::Size pref_size(100, 11); | 56 // The width will typically be ignored. |
| 57 gfx::Size pref_size(1, preferred_height_); | |
| 189 gfx::Insets insets = GetInsets(); | 58 gfx::Insets insets = GetInsets(); |
| 190 pref_size.Enlarge(insets.width(), insets.height()); | 59 pref_size.Enlarge(insets.width(), insets.height()); |
| 191 return pref_size; | 60 return pref_size; |
| 192 } | 61 } |
| 193 | 62 |
| 194 const char* ProgressBar::GetClassName() const { | 63 const char* ProgressBar::GetClassName() const { |
| 195 return kViewClassName; | 64 return kViewClassName; |
| 196 } | 65 } |
| 197 | 66 |
| 67 SkColor ProgressBar::GetForegroundColor() const { | |
|
sky
2016/09/13 02:30:08
definition/declaration order is off.
Evan Stade
2016/09/13 15:45:39
Done.
| |
| 68 return GetNativeTheme()->GetSystemColor( | |
| 69 ui::NativeTheme::kColorId_CallToActionColor); | |
| 70 } | |
| 71 | |
| 72 SkColor ProgressBar::GetBackgroundColor() const { | |
| 73 // The default foreground is GoogleBlue500, and the default background is | |
| 74 // that color but 80% lighter. | |
| 75 return color_utils::BlendTowardOppositeLuma(GetForegroundColor(), 0xCC); | |
| 76 } | |
| 77 | |
| 198 void ProgressBar::OnPaint(gfx::Canvas* canvas) { | 78 void ProgressBar::OnPaint(gfx::Canvas* canvas) { |
| 79 if (IsIndeterminate()) | |
| 80 return OnPaintIndeterminate(canvas); | |
| 81 | |
| 199 gfx::Rect content_bounds = GetContentsBounds(); | 82 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 | 83 |
| 208 // Draw background. | 84 // Draw background. |
| 209 FillRoundRect(canvas, | 85 SkPath background_path; |
| 210 bar_left, bar_top, bar_width, bar_height, | 86 AddPossiblyRoundRectToPath(content_bounds, &background_path); |
| 211 kCornerRadius, | 87 SkPaint background_paint; |
| 212 kBackgroundColor, kBackgroundColor, | 88 background_paint.setStyle(SkPaint::kFill_Style); |
| 213 false); | 89 background_paint.setFlags(SkPaint::kAntiAlias_Flag); |
| 214 StrokeRoundRect(canvas, | 90 background_paint.setColor(GetBackgroundColor()); |
| 215 bar_left, bar_top, | 91 canvas->DrawPath(background_path, background_paint); |
| 216 bar_width, bar_height, | |
| 217 kCornerRadius, | |
| 218 kBackgroundBorderColor, | |
| 219 kBorderWidth); | |
| 220 | 92 |
| 221 if (progress_width > 1) { | 93 // Draw slice. |
| 222 // Draw inner if wide enough. | 94 SkPath slice_path; |
| 223 if (progress_width > kBorderWidth * 2) { | 95 const int slice_width = static_cast<int>( |
| 224 canvas->Save(); | 96 content_bounds.width() * std::min(current_value_, 1.0) + 0.5); |
| 97 if (slice_width < 1) | |
| 98 return; | |
| 225 | 99 |
| 226 SkPath inner_path; | 100 gfx::Rect slice_bounds = content_bounds; |
| 227 AddRoundRectPathWithPadding( | 101 slice_bounds.set_width(slice_width); |
| 228 bar_left, bar_top, progress_width, bar_height, | 102 AddPossiblyRoundRectToPath(slice_bounds, &slice_path); |
| 229 kCornerRadius, | |
| 230 0, | |
| 231 &inner_path); | |
| 232 canvas->ClipPath(inner_path, false); | |
| 233 | 103 |
| 234 const SkColor bar_colors[] = { | 104 SkPaint slice_paint; |
| 235 kBarTopColor, | 105 slice_paint.setStyle(SkPaint::kFill_Style); |
| 236 kBarTopColor, | 106 slice_paint.setFlags(SkPaint::kAntiAlias_Flag); |
| 237 kBarColorStart, | 107 slice_paint.setColor(GetForegroundColor()); |
| 238 kBarColorEnd, | 108 canvas->DrawPath(slice_path, slice_paint); |
| 239 kBarColorEnd, | 109 } |
| 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 | 110 |
| 253 const SkColor disabled_bar_colors[] = { | 111 void ProgressBar::SetValue(double value) { |
| 254 kDisabledBarColorStart, | 112 double adjusted_value = (value < 0.0 || value > 1.0) ? -1.0 : value; |
| 255 kDisabledBarColorStart, | |
| 256 kDisabledBarColorEnd, | |
| 257 kDisabledBarColorEnd, | |
| 258 }; | |
| 259 | 113 |
| 260 const SkScalar disabled_bar_points[] = { | 114 if (adjusted_value != current_value_) { |
|
sky
2016/09/13 02:30:08
optional: early return.
Evan Stade
2016/09/13 15:45:39
Done.
| |
| 261 0, | 115 current_value_ = value; |
| 262 border_width, | 116 if (IsIndeterminate()) { |
| 263 SK_Scalar1 - border_width, | 117 indeterminate_bar_animation_.reset(new gfx::LinearAnimation(this)); |
| 264 SK_Scalar1 | 118 indeterminate_bar_animation_->SetDuration(2000); // In milliseconds. |
| 265 }; | 119 indeterminate_bar_animation_->Start(); |
| 266 | 120 } else { |
| 267 // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps | 121 SchedulePaint(); |
|
sky
2016/09/13 02:30:08
Does this need to reset indeterminate_bar_animatio
Evan Stade
2016/09/13 15:45:39
Done.
| |
| 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, 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 } | 122 } |
| 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 } | 123 } |
| 317 } | 124 } |
| 318 | 125 |
| 126 void ProgressBar::AnimationProgressed(const gfx::Animation* animation) { | |
| 127 DCHECK_EQ(animation, indeterminate_bar_animation_.get()); | |
| 128 DCHECK(IsIndeterminate()); | |
| 129 SchedulePaint(); | |
| 130 } | |
| 131 | |
| 132 void ProgressBar::AnimationEnded(const gfx::Animation* animation) { | |
| 133 DCHECK_EQ(animation, indeterminate_bar_animation_.get()); | |
| 134 // Restarts animation. | |
| 135 if (IsIndeterminate()) | |
| 136 indeterminate_bar_animation_->Start(); | |
| 137 } | |
| 138 | |
| 139 bool ProgressBar::IsIndeterminate() { | |
| 140 return current_value_ < 0.0; | |
| 141 } | |
| 142 | |
| 143 void ProgressBar::OnPaintIndeterminate(gfx::Canvas* canvas) { | |
| 144 gfx::Rect content_bounds = GetContentsBounds(); | |
| 145 | |
| 146 // Draw background. | |
| 147 SkPath background_path; | |
| 148 AddPossiblyRoundRectToPath(content_bounds, &background_path); | |
| 149 SkPaint background_paint; | |
| 150 background_paint.setStyle(SkPaint::kFill_Style); | |
| 151 background_paint.setFlags(SkPaint::kAntiAlias_Flag); | |
| 152 background_paint.setColor(GetBackgroundColor()); | |
| 153 canvas->DrawPath(background_path, background_paint); | |
| 154 | |
| 155 // Draw slice. | |
| 156 SkPath slice_path; | |
| 157 double time = indeterminate_bar_animation_->GetCurrentValue(); | |
| 158 | |
| 159 // The animation spec corresponds to the material design lite's parameter. | |
| 160 // (cf. https://github.com/google/material-design-lite/) | |
| 161 double bar1_left; | |
| 162 double bar1_width; | |
| 163 double bar2_left; | |
| 164 double bar2_width; | |
| 165 if (time < 0.50) { | |
| 166 bar1_left = time / 2; | |
| 167 bar1_width = time * 1.5; | |
| 168 bar2_left = 0; | |
| 169 bar2_width = 0; | |
| 170 } else if (time < 0.75) { | |
| 171 bar1_left = time * 3 - 1.25; | |
| 172 bar1_width = 0.75 - (time - 0.5) * 3; | |
| 173 bar2_left = 0; | |
| 174 bar2_width = time - 0.5; | |
| 175 } else { | |
| 176 bar1_left = 1; | |
| 177 bar1_width = 0; | |
| 178 bar2_left = (time - 0.75) * 4; | |
| 179 bar2_width = 0.25 - (time - 0.75); | |
| 180 } | |
| 181 | |
| 182 int bar1_x = static_cast<int>(content_bounds.width() * bar1_left); | |
| 183 int bar1_w = | |
| 184 std::min(static_cast<int>(content_bounds.width() * bar1_width + 0.5), | |
| 185 content_bounds.width() - bar1_x); | |
| 186 int bar2_x = static_cast<int>(content_bounds.width() * bar2_left); | |
| 187 int bar2_w = | |
| 188 std::min(static_cast<int>(content_bounds.width() * bar2_width + 0.5), | |
| 189 content_bounds.width() - bar2_x); | |
| 190 | |
| 191 gfx::Rect slice_bounds = content_bounds; | |
| 192 slice_bounds.set_x(content_bounds.x() + bar1_x); | |
| 193 slice_bounds.set_width(bar1_w); | |
| 194 AddPossiblyRoundRectToPath(slice_bounds, &slice_path); | |
| 195 slice_bounds.set_x(content_bounds.x() + bar2_x); | |
| 196 slice_bounds.set_width(bar2_w); | |
| 197 AddPossiblyRoundRectToPath(slice_bounds, &slice_path); | |
| 198 | |
| 199 SkPaint slice_paint; | |
| 200 slice_paint.setStyle(SkPaint::kFill_Style); | |
| 201 slice_paint.setFlags(SkPaint::kAntiAlias_Flag); | |
| 202 slice_paint.setColor(GetForegroundColor()); | |
| 203 canvas->DrawPath(slice_path, slice_paint); | |
| 204 } | |
| 205 | |
| 319 } // namespace views | 206 } // namespace views |
| OLD | NEW |