// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/download_started_animation.h" #include "base/macros.h" #include "content/public/browser/web_contents.h" #include "grit/theme_resources.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/animation/linear_animation.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" #include "ui/views/controls/image_view.h" #include "ui/views/widget/widget.h" // How long to spend moving downwards and fading out after waiting. const int kMoveTimeMs = 600; // The animation framerate. const int kFrameRateHz = 60; namespace { // DownloadStartAnimation creates an animation (which begins running // immediately) that animates an image downward from the center of the frame // provided on the constructor, while simultaneously fading it out. To use, // simply call "new DownloadStartAnimation"; the class cleans itself up when it // finishes animating. class DownloadStartedAnimationViews : public gfx::LinearAnimation, public views::ImageView { public: explicit DownloadStartedAnimationViews(content::WebContents* web_contents); private: // Move the animation to wherever it should currently be. void Reposition(); // Shut down the animation cleanly. void Close(); // Animation void AnimateToState(double state) override; // We use a TYPE_POPUP for the popup so that it may float above any windows in // our UI. views::Widget* popup_; // The content area at the start of the animation. We store this so that the // download shelf's resizing of the content area doesn't cause the animation // to move around. This means that once started, the animation won't move // with the parent window, but it's so fast that this shouldn't cause too // much heartbreak. gfx::Rect web_contents_bounds_; DISALLOW_COPY_AND_ASSIGN(DownloadStartedAnimationViews); }; DownloadStartedAnimationViews::DownloadStartedAnimationViews( content::WebContents* web_contents) : gfx::LinearAnimation(kMoveTimeMs, kFrameRateHz, NULL), popup_(NULL) { gfx::ImageSkia download_image = gfx::CreateVectorIcon( gfx::VectorIconId::FILE_DOWNLOAD_SHELF, 72, gfx::kGoogleBlue500); // If we're too small to show the download image, then don't bother - // the shelf will be enough. web_contents_bounds_ = web_contents->GetContainerBounds(); if (web_contents_bounds_.height() < download_image.height()) return; SetImage(&download_image); popup_ = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.accept_events = false; params.parent = web_contents->GetNativeView(); popup_->Init(params); popup_->SetOpacity(0x00); popup_->SetContentsView(this); Reposition(); popup_->Show(); Start(); } void DownloadStartedAnimationViews::Reposition() { // Align the image with the bottom left of the web contents (so that it // points to the newly created download). gfx::Size size = GetPreferredSize(); int x = base::i18n::IsRTL() ? web_contents_bounds_.right() - size.width() : web_contents_bounds_.x(); popup_->SetBounds(gfx::Rect( x, static_cast(web_contents_bounds_.bottom() - size.height() - size.height() * (1 - GetCurrentValue())), size.width(), size.height())); } void DownloadStartedAnimationViews::Close() { popup_->Close(); } void DownloadStartedAnimationViews::AnimateToState(double state) { if (state >= 1.0) { Close(); } else { Reposition(); // Start at zero, peak halfway and end at zero. double opacity = std::min(1.0 - pow(GetCurrentValue() - 0.5, 2) * 4.0, static_cast(1.0)); popup_->SetOpacity(static_cast(opacity * 255.0)); } } } // namespace // static void DownloadStartedAnimation::Show(content::WebContents* web_contents) { // The animation will delete itself when it's finished. new DownloadStartedAnimationViews(web_contents); }