Home | History | Annotate | Download | only in download
      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