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/animation/linear_animation.h" 16 #include "ui/base/resource/resource_bundle.h" 17 #include "ui/gfx/rect.h" 18 #include "ui/views/controls/image_view.h" 19 #include "ui/views/widget/widget.h" 20 21 using content::WebContents; 22 23 // How long to spend moving downwards and fading out after waiting. 24 static const int kMoveTimeMs = 600; 25 26 // The animation framerate. 27 static 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. 32 static const double kMoveFraction = 1.0 / 3.0; 33 34 namespace { 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. 41 class DownloadStartedAnimationWin : public ui::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 81 DownloadStartedAnimationWin::DownloadStartedAnimationWin( 82 WebContents* web_contents) 83 : ui::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 124 void 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 141 void 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 157 void 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 171 void 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 186 void 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