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.
19 static const int kMoveTimeMs = 600;
20
21 // The animation framerate.
22 static 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.
27 static const double kMoveFraction = 1.0 / 3.0;
28
29 namespace {
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.
36 class 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
DownloadStartedAnimationWin(TabContents * tab_contents)76 DownloadStartedAnimationWin::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
Reposition()118 void 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
Close()135 void 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
AnimateToState(double state)151 void 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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)168 void DownloadStartedAnimationWin::Observe(NotificationType type,
169 const NotificationSource& source,
170 const NotificationDetails& details) {
171 Close();
172 }
173
174 } // namespace
175
176 // static
Show(TabContents * tab_contents)177 void 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