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 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 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 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 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 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 136 ProgressBar::ProgressBar() 137 : min_display_value_(0.0), 138 max_display_value_(1.0), 139 current_value_(0.0) { 140 } 141 142 ProgressBar::~ProgressBar() { 143 } 144 145 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 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 163 void ProgressBar::SetValue(double value) { 164 if (value != current_value_) { 165 current_value_ = value; 166 SchedulePaint(); 167 } 168 } 169 170 void ProgressBar::SetTooltipText(const string16& tooltip_text) { 171 tooltip_text_ = tooltip_text; 172 } 173 174 bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const { 175 DCHECK(tooltip); 176 *tooltip = tooltip_text_; 177 return !tooltip_text_.empty(); 178 } 179 180 void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) { 181 state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR; 182 state->state = ui::AccessibilityTypes::STATE_READONLY; 183 } 184 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 192 const char* ProgressBar::GetClassName() const { 193 return kViewClassName; 194 } 195 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