// 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