blob: 587e6dc435efdb15d459f841df380bb4f31823e9 [file] [log] [blame]
// Copyright (c) 2011 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 "views/controls/progress_bar.h"
#include <algorithm>
#include <string>
#include "base/logging.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "third_party/skia/include/effects/SkBlurMaskFilter.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/font.h"
#include "ui/gfx/insets.h"
#include "views/background.h"
#include "views/border.h"
#include "views/painter.h"
using std::max;
using std::min;
namespace {
// Corner radius for the progress bar's border.
const int kCornerRadius = 3;
// Progress bar's border width
const int kBorderWidth = 1;
void AddRoundRectPathWithPadding(int x, int y,
int w, int h,
int corner_radius,
SkScalar padding,
SkPath* path) {
DCHECK(path);
if (path == NULL)
return;
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) {
static const SkScalar half = SkIntToScalar(1) / 2;
AddRoundRectPathWithPadding(x, y, w, h, corner_radius, half, 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].set(SkIntToScalar(x), SkIntToScalar(y));
if (gradient_horizontal) {
p[1].set(SkIntToScalar(x + w), SkIntToScalar(y));
} else {
p[1].set(SkIntToScalar(x), SkIntToScalar(y + h));
}
SkShader* s = SkGradientShader::CreateLinear(
p, colors, points, count, SkShader::kClamp_TileMode, NULL);
paint.setShader(s);
// Need to unref shader, otherwise never deleted.
s->unref();
canvas->GetSkCanvas()->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->GetSkCanvas()->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->GetSkCanvas()->drawPath(path, paint);
}
} // namespace
namespace views {
// static
const char ProgressBar::kViewClassName[] = "views/ProgressBar";
ProgressBar::ProgressBar()
: min_display_value_(0.0),
max_display_value_(1.0),
current_value_(0.0) {
}
ProgressBar::~ProgressBar() {
}
gfx::Size ProgressBar::GetPreferredSize() {
return gfx::Size(100, 16);
}
std::string ProgressBar::GetClassName() const {
return kViewClassName;
}
void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR;
state->state = ui::AccessibilityTypes::STATE_READONLY;
}
void ProgressBar::OnPaint(gfx::Canvas* canvas) {
const double capped_value =
min(max(current_value_, min_display_value_), max_display_value_);
const double capped_fraction =
(capped_value - min_display_value_) /
(max_display_value_ - min_display_value_);
const int progress_width = static_cast<int>(width() * capped_fraction + 0.5);
#if defined(OS_CHROMEOS)
const SkColor background_colors[] = {
SkColorSetRGB(0xBB, 0xBB, 0xBB),
SkColorSetRGB(0xE7, 0xE7, 0xE7),
SkColorSetRGB(0xFE, 0xFE, 0xFE)
};
const SkScalar background_points[] = {
SkDoubleToScalar(0),
SkDoubleToScalar(0.1),
SkDoubleToScalar(1)
};
const SkColor background_border_color = SkColorSetRGB(0xA1, 0xA1, 0xA1);
// Draw background.
FillRoundRect(canvas,
0, 0, width(), height(),
kCornerRadius,
background_colors,
background_points,
arraysize(background_colors),
false);
StrokeRoundRect(canvas,
0, 0,
width(), height(),
kCornerRadius,
background_border_color,
kBorderWidth);
if (progress_width > 1) {
const bool enabled = IsEnabled();
const SkColor bar_color_start = enabled ?
SkColorSetRGB(100, 116, 147) :
SkColorSetRGB(229, 232, 237);
const SkColor bar_color_end = enabled ?
SkColorSetRGB(65, 73, 87) :
SkColorSetRGB(224, 225, 227);
const SkColor bar_outer_color = enabled ?
SkColorSetRGB(0x4A, 0x4A, 0x4A) :
SkColorSetARGB(0x80, 0x4A, 0x4A, 0x4A);
const SkColor bar_inner_border_color =
SkColorSetARGB(0x3F, 0xFF, 0xFF, 0xFF); // 0.25 white
const SkColor bar_inner_shadow_color =
SkColorSetARGB(0x54, 0xFF, 0xFF, 0xFF); // 0.33 white
// Draw bar background
FillRoundRect(canvas,
0, 0, progress_width, height(),
kCornerRadius,
bar_color_start,
bar_color_end,
false);
// Draw inner stroke and shadow if wide enough.
if (progress_width > 2 * kBorderWidth) {
canvas->GetSkCanvas()->save();
SkPath inner_path;
AddRoundRectPathWithPadding(
0, 0, progress_width, height(),
kCornerRadius,
SkIntToScalar(kBorderWidth),
&inner_path);
canvas->GetSkCanvas()->clipPath(inner_path);
// Draw bar inner stroke
StrokeRoundRect(canvas,
kBorderWidth, kBorderWidth,
progress_width - 2 * kBorderWidth,
height() - 2 * kBorderWidth,
kCornerRadius - kBorderWidth,
bar_inner_border_color,
kBorderWidth);
// Draw bar inner shadow
StrokeRoundRect(canvas,
0, kBorderWidth, progress_width, height(),
kCornerRadius,
bar_inner_shadow_color,
kBorderWidth);
canvas->GetSkCanvas()->restore();
}
// Draw bar stroke
StrokeRoundRect(canvas,
0, 0, progress_width, height(),
kCornerRadius,
bar_outer_color,
kBorderWidth);
}
#else
SkColor bar_color_start = SkColorSetRGB(81, 138, 223);
SkColor bar_color_end = SkColorSetRGB(51, 103, 205);
SkColor background_color_start = SkColorSetRGB(212, 212, 212);
SkColor background_color_end = SkColorSetRGB(252, 252, 252);
SkColor border_color = SkColorSetRGB(144, 144, 144);
FillRoundRect(canvas,
0, 0, width(), height(),
kCornerRadius,
background_color_start,
background_color_end,
false);
if (progress_width > 1) {
FillRoundRect(canvas,
0, 0,
progress_width, height(),
kCornerRadius,
bar_color_start,
bar_color_end,
false);
}
StrokeRoundRect(canvas,
0, 0,
width(), height(),
kCornerRadius,
border_color,
kBorderWidth);
#endif
}
bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
DCHECK(tooltip);
if (tooltip == NULL)
return false;
tooltip->assign(tooltip_text_);
return !tooltip_text_.empty();
}
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 string16& tooltip_text) {
tooltip_text_ = tooltip_text;
}
} // namespace views