root/chrome/browser/ui/gtk/download/download_started_animation_gtk.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. popup_
  2. Reposition
  3. Close
  4. AnimateToState
  5. Show

// 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 <math.h>

#include <gtk/gtk.h>

#include "base/message_loop/message_loop.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "grit/theme_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/rect.h"

namespace {

// How long to spend moving downwards and fading out after waiting.
const int kMoveTimeMs = 600;

// The animation framerate.
const int kFrameRateHz = 60;

class DownloadStartedAnimationGtk : public gfx::LinearAnimation {
 public:
  explicit DownloadStartedAnimationGtk(content::WebContents* web_contents);

  // DownloadStartedAnimation will delete itself, but this is public so
  // that we can use DeleteSoon().
  virtual ~DownloadStartedAnimationGtk();

 private:
  // Move the arrow to wherever it should currently be.
  void Reposition();

  // Shut down cleanly.
  void Close();

  // Animation implementation.
  virtual void AnimateToState(double state) OVERRIDE;

  // The top level window that floats over the browser and displays the
  // image.
  GtkWidget* popup_;

  // Dimensions of the image.
  int width_;
  int height_;

  // 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(DownloadStartedAnimationGtk);
};

DownloadStartedAnimationGtk::DownloadStartedAnimationGtk(
    content::WebContents* web_contents)
    : gfx::LinearAnimation(kMoveTimeMs, kFrameRateHz, NULL),
      popup_(NULL) {
  static GdkPixbuf* kDownloadImage = NULL;
  if (!kDownloadImage) {
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    kDownloadImage = rb.GetNativeImageNamed(
        IDR_DOWNLOAD_ANIMATION_BEGIN).ToGdkPixbuf();
  }

  width_ = gdk_pixbuf_get_width(kDownloadImage);
  height_ = gdk_pixbuf_get_height(kDownloadImage);

  // If we're too small to show the download image, then don't bother -
  // the shelf will be enough.
  web_contents->GetView()->GetContainerBounds(&web_contents_bounds_);
  if (web_contents_bounds_.height() < height_)
    return;

  // TODO(estade): don't show up on the wrong virtual desktop.

  popup_ = gtk_window_new(GTK_WINDOW_POPUP);
  GtkWidget* image = gtk_image_new_from_pixbuf(kDownloadImage);
  gtk_container_add(GTK_CONTAINER(popup_), image);

  // Set the shape of the window to that of the arrow. Areas with
  // opacity less than 0xff (i.e. <100% opacity) will be transparent.
  GdkBitmap* mask = gdk_pixmap_new(NULL, width_, height_, 1);
  gdk_pixbuf_render_threshold_alpha(kDownloadImage, mask,
                                    0, 0,
                                    0, 0, -1, -1,
                                    0xff);
  gtk_widget_shape_combine_mask(popup_, mask, 0, 0);
  g_object_unref(mask);

  Reposition();
  gtk_widget_show_all(popup_);
  // Make sure our window has focus, is brought to the top, etc.
  gtk_window_present(GTK_WINDOW(popup_));

  Start();
}

DownloadStartedAnimationGtk::~DownloadStartedAnimationGtk() {
}

void DownloadStartedAnimationGtk::Reposition() {
  DCHECK(popup_);

  // Align the image with the bottom left of the web contents (so that it
  // points to the newly created download).
  gtk_window_move(GTK_WINDOW(popup_),
      web_contents_bounds_.x(),
      static_cast<int>(web_contents_bounds_.bottom() -
          height_ - height_ * (1 - GetCurrentValue())));
}

void DownloadStartedAnimationGtk::Close() {
  DCHECK(popup_);

  gtk_widget_destroy(popup_);
  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}

void DownloadStartedAnimationGtk::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<double>(1.0));

    // This only works when there's a compositing manager running. Oh well.
    gtk_window_set_opacity(GTK_WINDOW(popup_), opacity);
  }
}

}  // namespace

// static
void DownloadStartedAnimation::Show(content::WebContents* web_contents) {
  // The animation will delete itself.
  new DownloadStartedAnimationGtk(web_contents);
}

/* [<][>][^][v][top][bottom][index][help] */