| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/controls/progress_bar.h" |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "third_party/skia/include/core/SkPaint.h" |
| #include "third_party/skia/include/core/SkXfermode.h" |
| #include "third_party/skia/include/effects/SkGradientShader.h" |
| #include "ui/accessibility/ax_view_state.h" |
| #include "ui/gfx/canvas.h" |
| |
| namespace { |
| |
| // Progress bar's border width. |
| const int kBorderWidth = 1; |
| |
| // Corner radius for the progress bar's border. |
| const int kCornerRadius = 2; |
| |
| // The width of the highlight at the right of the progress bar. |
| const int kHighlightWidth = 18; |
| |
| const SkColor kBackgroundColor = SkColorSetRGB(230, 230, 230); |
| const SkColor kBackgroundBorderColor = SkColorSetRGB(208, 208, 208); |
| const SkColor kBarBorderColor = SkColorSetRGB(65, 137, 237); |
| const SkColor kBarTopColor = SkColorSetRGB(110, 188, 249); |
| const SkColor kBarColorStart = SkColorSetRGB(86, 167, 247); |
| const SkColor kBarColorEnd = SkColorSetRGB(76, 148, 245); |
| const SkColor kBarHighlightEnd = SkColorSetRGB(114, 206, 251); |
| const SkColor kDisabledBarBorderColor = SkColorSetRGB(191, 191, 191); |
| const SkColor kDisabledBarColorStart = SkColorSetRGB(224, 224, 224); |
| const SkColor kDisabledBarColorEnd = SkColorSetRGB(212, 212, 212); |
| |
| void AddRoundRectPathWithPadding(int x, int y, |
| int w, int h, |
| int corner_radius, |
| SkScalar padding, |
| SkPath* path) { |
| DCHECK(path); |
| SkRect rect; |
| rect.set( |
| SkIntToScalar(x) + padding, SkIntToScalar(y) + padding, |
| SkIntToScalar(x + w) - padding, SkIntToScalar(y + h) - padding); |
| path->addRoundRect( |
| rect, |
| SkIntToScalar(corner_radius) - padding, |
| SkIntToScalar(corner_radius) - padding); |
| } |
| |
| void AddRoundRectPath(int x, int y, |
| int w, int h, |
| int corner_radius, |
| SkPath* path) { |
| AddRoundRectPathWithPadding(x, y, w, h, corner_radius, SK_ScalarHalf, path); |
| } |
| |
| void 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) { |
| SkPath path; |
| AddRoundRectPath(x, y, w, h, corner_radius, &path); |
| SkPaint paint; |
| paint.setStyle(SkPaint::kFill_Style); |
| paint.setFlags(SkPaint::kAntiAlias_Flag); |
| |
| SkPoint p[2]; |
| p[0].iset(x, y); |
| if (gradient_horizontal) { |
| p[1].iset(x + w, y); |
| } else { |
| p[1].iset(x, y + h); |
| } |
| skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( |
| p, colors, points, count, SkShader::kClamp_TileMode)); |
| paint.setShader(s.get()); |
| |
| canvas->DrawPath(path, paint); |
| } |
| |
| void 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) { |
| if (gradient_start_color != gradient_end_color) { |
| SkColor colors[2] = { gradient_start_color, gradient_end_color }; |
| FillRoundRect(canvas, x, y, w, h, corner_radius, |
| colors, NULL, 2, gradient_horizontal); |
| } else { |
| SkPath path; |
| AddRoundRectPath(x, y, w, h, corner_radius, &path); |
| SkPaint paint; |
| paint.setStyle(SkPaint::kFill_Style); |
| paint.setFlags(SkPaint::kAntiAlias_Flag); |
| paint.setColor(gradient_start_color); |
| canvas->DrawPath(path, paint); |
| } |
| } |
| |
| void StrokeRoundRect(gfx::Canvas* canvas, |
| int x, int y, |
| int w, int h, |
| int corner_radius, |
| SkColor stroke_color, |
| int stroke_width) { |
| SkPath path; |
| AddRoundRectPath(x, y, w, h, corner_radius, &path); |
| SkPaint paint; |
| paint.setShader(NULL); |
| paint.setColor(stroke_color); |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setFlags(SkPaint::kAntiAlias_Flag); |
| paint.setStrokeWidth(SkIntToScalar(stroke_width)); |
| canvas->DrawPath(path, paint); |
| } |
| |
| } // namespace |
| |
| namespace views { |
| |
| // static |
| const char ProgressBar::kViewClassName[] = "ProgressBar"; |
| |
| ProgressBar::ProgressBar() |
| : min_display_value_(0.0), |
| max_display_value_(1.0), |
| current_value_(0.0) { |
| } |
| |
| ProgressBar::~ProgressBar() { |
| } |
| |
| double ProgressBar::GetNormalizedValue() const { |
| const double capped_value = std::min( |
| std::max(current_value_, min_display_value_), max_display_value_); |
| return (capped_value - min_display_value_) / |
| (max_display_value_ - min_display_value_); |
| } |
| |
| void ProgressBar::SetDisplayRange(double min_display_value, |
| double max_display_value) { |
| if (min_display_value != min_display_value_ || |
| max_display_value != max_display_value_) { |
| DCHECK(min_display_value < max_display_value); |
| min_display_value_ = min_display_value; |
| max_display_value_ = max_display_value; |
| SchedulePaint(); |
| } |
| } |
| |
| void ProgressBar::SetValue(double value) { |
| if (value != current_value_) { |
| current_value_ = value; |
| SchedulePaint(); |
| } |
| } |
| |
| void ProgressBar::SetTooltipText(const base::string16& tooltip_text) { |
| tooltip_text_ = tooltip_text; |
| } |
| |
| bool ProgressBar::GetTooltipText(const gfx::Point& p, |
| base::string16* tooltip) const { |
| DCHECK(tooltip); |
| *tooltip = tooltip_text_; |
| return !tooltip_text_.empty(); |
| } |
| |
| void ProgressBar::GetAccessibleState(ui::AXViewState* state) { |
| state->role = ui::AX_ROLE_PROGRESS_INDICATOR; |
| state->AddStateFlag(ui::AX_STATE_READ_ONLY); |
| } |
| |
| gfx::Size ProgressBar::GetPreferredSize() const { |
| gfx::Size pref_size(100, 11); |
| gfx::Insets insets = GetInsets(); |
| pref_size.Enlarge(insets.width(), insets.height()); |
| return pref_size; |
| } |
| |
| const char* ProgressBar::GetClassName() const { |
| return kViewClassName; |
| } |
| |
| void ProgressBar::OnPaint(gfx::Canvas* canvas) { |
| gfx::Rect content_bounds = GetContentsBounds(); |
| int bar_left = content_bounds.x(); |
| int bar_top = content_bounds.y(); |
| int bar_width = content_bounds.width(); |
| int bar_height = content_bounds.height(); |
| |
| const int progress_width = |
| static_cast<int>(bar_width * GetNormalizedValue() + 0.5); |
| |
| // Draw background. |
| FillRoundRect(canvas, |
| bar_left, bar_top, bar_width, bar_height, |
| kCornerRadius, |
| kBackgroundColor, kBackgroundColor, |
| false); |
| StrokeRoundRect(canvas, |
| bar_left, bar_top, |
| bar_width, bar_height, |
| kCornerRadius, |
| kBackgroundBorderColor, |
| kBorderWidth); |
| |
| if (progress_width > 1) { |
| // Draw inner if wide enough. |
| if (progress_width > kBorderWidth * 2) { |
| canvas->Save(); |
| |
| SkPath inner_path; |
| AddRoundRectPathWithPadding( |
| bar_left, bar_top, progress_width, bar_height, |
| kCornerRadius, |
| 0, |
| &inner_path); |
| canvas->ClipPath(inner_path, false); |
| |
| const SkColor bar_colors[] = { |
| kBarTopColor, |
| kBarTopColor, |
| kBarColorStart, |
| kBarColorEnd, |
| kBarColorEnd, |
| }; |
| // We want a thin 1-pixel line for kBarTopColor. |
| SkScalar scalar_height = SkIntToScalar(bar_height); |
| SkScalar highlight_width = SkScalarDiv(SK_Scalar1, scalar_height); |
| SkScalar border_width = SkScalarDiv(SkIntToScalar(kBorderWidth), |
| scalar_height); |
| const SkScalar bar_points[] = { |
| 0, |
| border_width, |
| border_width + highlight_width, |
| SK_Scalar1 - border_width, |
| SK_Scalar1, |
| }; |
| |
| const SkColor disabled_bar_colors[] = { |
| kDisabledBarColorStart, |
| kDisabledBarColorStart, |
| kDisabledBarColorEnd, |
| kDisabledBarColorEnd, |
| }; |
| |
| const SkScalar disabled_bar_points[] = { |
| 0, |
| border_width, |
| SK_Scalar1 - border_width, |
| SK_Scalar1 |
| }; |
| |
| // Do not start from (kBorderWidth, kBorderWidth) because it makes gaps |
| // between the inner and the border. |
| FillRoundRect(canvas, |
| bar_left, bar_top, |
| progress_width, bar_height, |
| kCornerRadius, |
| enabled() ? bar_colors : disabled_bar_colors, |
| enabled() ? bar_points : disabled_bar_points, |
| enabled() ? arraysize(bar_colors) : |
| arraysize(disabled_bar_colors), |
| false); |
| |
| if (enabled()) { |
| // Draw the highlight to the right. |
| const SkColor highlight_colors[] = { |
| SkColorSetA(kBarHighlightEnd, 0), |
| kBarHighlightEnd, |
| kBarHighlightEnd, |
| }; |
| const SkScalar highlight_points[] = { |
| 0, |
| SK_Scalar1 - SkScalarDiv(SkIntToScalar(kBorderWidth), scalar_height), |
| SK_Scalar1, |
| }; |
| SkPaint paint; |
| paint.setStyle(SkPaint::kFill_Style); |
| paint.setFlags(SkPaint::kAntiAlias_Flag); |
| |
| SkPoint p[2]; |
| int highlight_left = |
| std::max(0, progress_width - kHighlightWidth - kBorderWidth); |
| p[0].iset(highlight_left, 0); |
| p[1].iset(progress_width, 0); |
| skia::RefPtr<SkShader> s = |
| skia::AdoptRef(SkGradientShader::CreateLinear( |
| p, highlight_colors, highlight_points, |
| arraysize(highlight_colors), SkShader::kClamp_TileMode)); |
| paint.setShader(s.get()); |
| paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); |
| canvas->DrawRect(gfx::Rect(highlight_left, 0, |
| kHighlightWidth + kBorderWidth, bar_height), |
| paint); |
| } |
| |
| canvas->Restore(); |
| } |
| |
| // Draw bar stroke |
| StrokeRoundRect(canvas, |
| bar_left, bar_top, progress_width, bar_height, |
| kCornerRadius, |
| enabled() ? kBarBorderColor : kDisabledBarBorderColor, |
| kBorderWidth); |
| } |
| } |
| |
| } // namespace views |