1// Copyright (c) 2011 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/download/download_started_animation.h"
6
7#include "content/browser/tab_contents/tab_contents.h"
8#include "content/common/notification_details.h"
9#include "content/common/notification_registrar.h"
10#include "content/common/notification_source.h"
11#include "grit/theme_resources.h"
12#include "ui/base/animation/linear_animation.h"
13#include "ui/base/resource/resource_bundle.h"
14#include "ui/gfx/rect.h"
15#include "views/controls/image_view.h"
16#include "views/widget/widget.h"
17
18// How long to spend moving downwards and fading out after waiting.
19static const int kMoveTimeMs = 600;
20
21// The animation framerate.
22static const int kFrameRateHz = 60;
23
24// What fraction of the frame height to move downward from the frame center.
25// Note that setting this greater than 0.5 will mean moving past the bottom of
26// the frame.
27static const double kMoveFraction = 1.0 / 3.0;
28
29namespace {
30
31// DownloadStartAnimation creates an animation (which begins running
32// immediately) that animates an image downward from the center of the frame
33// provided on the constructor, while simultaneously fading it out.  To use,
34// simply call "new DownloadStartAnimation"; the class cleans itself up when it
35// finishes animating.
36class DownloadStartedAnimationWin : public ui::LinearAnimation,
37                                    public NotificationObserver,
38                                    public views::ImageView {
39 public:
40  explicit DownloadStartedAnimationWin(TabContents* tab_contents);
41
42 private:
43  // Move the animation to wherever it should currently be.
44  void Reposition();
45
46  // Shut down the animation cleanly.
47  void Close();
48
49  // Animation
50  virtual void AnimateToState(double state);
51
52  // NotificationObserver
53  virtual void Observe(NotificationType type,
54                       const NotificationSource& source,
55                       const NotificationDetails& details);
56
57  // We use a HWND for the popup so that it may float above any HWNDs in our UI.
58  views::Widget* popup_;
59
60  // The content area holding us.
61  TabContents* tab_contents_;
62
63  // The content area at the start of the animation. We store this so that the
64  // download shelf's resizing of the content area doesn't cause the animation
65  // to move around. This means that once started, the animation won't move
66  // with the parent window, but it's so fast that this shouldn't cause too
67  // much heartbreak.
68  gfx::Rect tab_contents_bounds_;
69
70  // A scoped container for notification registries.
71  NotificationRegistrar registrar_;
72
73  DISALLOW_COPY_AND_ASSIGN(DownloadStartedAnimationWin);
74};
75
76DownloadStartedAnimationWin::DownloadStartedAnimationWin(
77    TabContents* tab_contents)
78    : ui::LinearAnimation(kMoveTimeMs, kFrameRateHz, NULL),
79      popup_(NULL),
80      tab_contents_(tab_contents) {
81  static SkBitmap* kDownloadImage = NULL;
82  if (!kDownloadImage) {
83    kDownloadImage = ResourceBundle::GetSharedInstance().GetBitmapNamed(
84        IDR_DOWNLOAD_ANIMATION_BEGIN);
85  }
86
87  // If we're too small to show the download image, then don't bother -
88  // the shelf will be enough.
89  tab_contents_->GetContainerBounds(&tab_contents_bounds_);
90  if (tab_contents_bounds_.height() < kDownloadImage->height())
91    return;
92
93  registrar_.Add(
94      this,
95      NotificationType::TAB_CONTENTS_HIDDEN,
96      Source<TabContents>(tab_contents_));
97  registrar_.Add(
98      this,
99      NotificationType::TAB_CONTENTS_DESTROYED,
100      Source<TabContents>(tab_contents_));
101
102  SetImage(kDownloadImage);
103
104  gfx::Rect rc(0, 0, 0, 0);
105  views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
106  params.transparent = true;
107  params.accept_events = false;
108  popup_ = views::Widget::CreateWidget(params);
109  popup_->SetOpacity(0x00);
110  popup_->Init(tab_contents_->GetNativeView(), rc);
111  popup_->SetContentsView(this);
112  Reposition();
113  popup_->Show();
114
115  Start();
116}
117
118void DownloadStartedAnimationWin::Reposition() {
119  if (!tab_contents_)
120    return;
121
122  // Align the image with the bottom left of the web contents (so that it
123  // points to the newly created download).
124  gfx::Size size = GetPreferredSize();
125  int x = base::i18n::IsRTL() ?
126      tab_contents_bounds_.right() - size.width() : tab_contents_bounds_.x();
127  popup_->SetBounds(gfx::Rect(
128      x,
129      static_cast<int>(tab_contents_bounds_.bottom() -
130          size.height() - size.height() * (1 - GetCurrentValue())),
131      size.width(),
132      size.height()));
133}
134
135void DownloadStartedAnimationWin::Close() {
136  if (!tab_contents_)
137    return;
138
139  registrar_.Remove(
140      this,
141      NotificationType::TAB_CONTENTS_HIDDEN,
142      Source<TabContents>(tab_contents_));
143  registrar_.Remove(
144      this,
145      NotificationType::TAB_CONTENTS_DESTROYED,
146      Source<TabContents>(tab_contents_));
147  tab_contents_ = NULL;
148  popup_->Close();
149}
150
151void DownloadStartedAnimationWin::AnimateToState(double state) {
152  if (state >= 1.0) {
153    Close();
154  } else {
155    Reposition();
156
157    // Start at zero, peak halfway and end at zero.
158    double opacity = std::min(1.0 - pow(GetCurrentValue() - 0.5, 2) * 4.0,
159                              static_cast<double>(1.0));
160
161    popup_->SetOpacity(
162        static_cast<SkColor>(opacity * 255.0));
163    SchedulePaint();  // Reposition() calls MoveWindow() which never picks up
164                      // alpha changes, so we need to force a paint.
165  }
166}
167
168void DownloadStartedAnimationWin::Observe(NotificationType type,
169                                          const NotificationSource& source,
170                                          const NotificationDetails& details) {
171  Close();
172}
173
174}  // namespace
175
176// static
177void DownloadStartedAnimation::Show(TabContents* tab_contents) {
178  // The animation will delete itself when it's finished or when the tab
179  // contents is hidden or destroyed.
180  new DownloadStartedAnimationWin(tab_contents);
181}
182