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 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 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 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 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 168 void DownloadStartedAnimationWin::Observe(NotificationType type, 169 const NotificationSource& source, 170 const NotificationDetails& details) { 171 Close(); 172 } 173 174 } // namespace 175 176 // static 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