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