1// Copyright (c) 2013 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 "chrome/browser/ui/toolbar/wrench_icon_painter.h"
6
7#include <algorithm>
8
9#include "grit/theme_resources.h"
10#include "ui/base/theme_provider.h"
11#include "ui/gfx/animation/multi_animation.h"
12#include "ui/gfx/canvas.h"
13#include "ui/gfx/image/image_skia.h"
14#include "ui/gfx/rect.h"
15
16namespace {
17
18// The wrench icon is made up of this many bars stacked vertically.
19const int kBarCount = 3;
20
21// |value| is the animation progress from 0 to 1. |index| is the index of the
22// bar being drawn. This function returns a new progress value (from 0 to 1)
23// such that bars appear staggered.
24double GetStaggeredValue(double value, int index) {
25  // When animating the wrench icon's bars the bars are staggered by this
26  // factor.
27  const double kStaggerFactor = 0.15;
28  double maxStaggeredValue = 1.0 - (kBarCount - 1) * kStaggerFactor;
29  double staggeredValue = (value - kStaggerFactor * index) / maxStaggeredValue;
30  return std::min(1.0, std::max(0.0, staggeredValue));
31}
32
33}  // namespace
34
35WrenchIconPainter::WrenchIconPainter(Delegate* delegate)
36    : delegate_(delegate),
37      severity_(SEVERITY_NONE) {
38}
39
40WrenchIconPainter::~WrenchIconPainter() {}
41
42void WrenchIconPainter::SetSeverity(Severity severity, bool animate) {
43  if (severity_ == severity)
44    return;
45
46  severity_ = severity;
47  delegate_->ScheduleWrenchIconPaint();
48  animation_.reset();
49  if (severity_ == SEVERITY_NONE || !animate)
50    return;
51
52  gfx::MultiAnimation::Parts parts;
53  // Animate the bars left to right.
54  parts.push_back(gfx::MultiAnimation::Part(1300, gfx::Tween::LINEAR));
55  // Fade out animation.
56  parts.push_back(gfx::MultiAnimation::Part(1000, gfx::Tween::EASE_IN));
57  // Again, animate the bars left to right.
58  parts.push_back(gfx::MultiAnimation::Part(1300, gfx::Tween::LINEAR));
59
60  animation_.reset(
61      new gfx::MultiAnimation(parts, base::TimeDelta::FromMilliseconds(40)));
62  animation_->set_delegate(this);
63  animation_->set_continuous(false);
64  animation_->Start();
65}
66
67void WrenchIconPainter::Paint(gfx::Canvas* canvas,
68                              ui::ThemeProvider* theme_provider,
69                              const gfx::Rect& rect,
70                              BezelType bezel_type) {
71  gfx::Point center = rect.CenterPoint();
72
73  // Bezel.
74  if (bezel_type != BEZEL_NONE) {
75    gfx::ImageSkia* image = theme_provider->GetImageSkiaNamed(
76        bezel_type == BEZEL_HOVER ? IDR_TOOLBAR_BEZEL_HOVER
77                                  : IDR_TOOLBAR_BEZEL_PRESSED);
78    canvas->DrawImageInt(*image,
79                         center.x() - image->width() / 2,
80                         center.y() - image->height() / 2);
81  }
82
83  // The bars with no color.
84  {
85    gfx::ImageSkia* image = theme_provider->GetImageSkiaNamed(IDR_TOOLS_BAR);
86    int x = center.x() - image->width() / 2;
87    int y = center.y() - image->height() * kBarCount / 2;
88    for (int i = 0; i < kBarCount; ++i) {
89      canvas->DrawImageInt(*image, x, y);
90      y += image->height();
91    }
92  }
93
94  // The bars with color based on severity.
95  int severity_image_id = GetCurrentSeverityImageID();
96  if (severity_image_id) {
97    gfx::ImageSkia* image =
98        theme_provider->GetImageSkiaNamed(severity_image_id);
99    int x = center.x() - image->width() / 2;
100    int y = center.y() - image->height() * kBarCount / 2;
101    for (int i = 0; i < kBarCount; ++i) {
102      SkPaint paint;
103      int width = image->width();
104
105      if (animation_ && animation_->is_animating()) {
106        if (animation_->current_part_index() % 2 == 1) {
107          // Fade out.
108          int alpha = animation_->CurrentValueBetween(0xFF, 0);
109          if (alpha == 0)
110            continue;
111          paint.setAlpha(alpha);
112        } else {
113          // Stagger the widths.
114          width = image->width() *
115                  GetStaggeredValue(animation_->GetCurrentValue(), i);
116          if (width == 0)
117            continue;
118        }
119      }
120
121      canvas->DrawImageInt(*image, 0, 0, width, image->height(),
122                            x, y, width, image->height(), false, paint);
123      y += image->height();
124    }
125  }
126
127  if (!badge_.isNull())
128    canvas->DrawImageInt(badge_, 0, 0);
129}
130
131void WrenchIconPainter::AnimationProgressed(const gfx::Animation* animation) {
132  delegate_->ScheduleWrenchIconPaint();
133}
134
135int WrenchIconPainter::GetCurrentSeverityImageID() const {
136  switch (severity_) {
137    case SEVERITY_NONE:
138      return 0;
139    case SEVERITY_LOW:
140      return IDR_TOOLS_BAR_LOW;
141    case SEVERITY_MEDIUM:
142      return IDR_TOOLS_BAR_MEDIUM;
143    case SEVERITY_HIGH:
144      return IDR_TOOLS_BAR_HIGH;
145  }
146  NOTREACHED();
147  return 0;
148}
149