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