普通文本  |  103行  |  3.06 KB

// 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/ui/gtk/throbber_gtk.h"

#include "base/logging.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "content/public/browser/notification_source.h"
#include "grit/ui_resources.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/image/cairo_cached_surface.h"

namespace {

// The length of a single cycle of the animation in milliseconds.
const int kThrobberDurationMs = 750;

}  // namespace

ThrobberGtk::ThrobberGtk(GtkThemeService* theme_service)
    : theme_service_(theme_service),
      animation_(this),
      num_frames_(0) {
  DCHECK(theme_service_);
  Init();
}

ThrobberGtk::~ThrobberGtk() {
  widget_.Destroy();
}

void ThrobberGtk::Start() {
  animation_.Show();
}

void ThrobberGtk::Stop() {
  animation_.Reset();
}

void ThrobberGtk::AnimationEnded(const gfx::Animation* animation) {
  animation_.Reset();
  animation_.Show();
}

void ThrobberGtk::AnimationProgressed(const gfx::Animation* animation) {
  gtk_widget_queue_draw(widget_.get());
}

void ThrobberGtk::Observe(int type,
                          const content::NotificationSource& source,
                          const content::NotificationDetails& details) {
  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
  LoadFrames();
  gtk_widget_queue_draw(widget_.get());
}

gboolean ThrobberGtk::OnExpose(GtkWidget* widget, GdkEventExpose* expose) {
  cairo_t* cairo_context =
      gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget)));
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  cairo_translate(cairo_context, allocation.x, allocation.y);

  gfx::CairoCachedSurface* cairo_frames = frames_.ToCairo();
  const int frame =
      static_cast<int>(animation_.GetCurrentValue() * (num_frames_ - 1));
  const int image_size = cairo_frames->Height();
  const int image_offset = frame * image_size;

  cairo_frames->SetSource(cairo_context, widget, -image_offset, 0);
  cairo_rectangle(cairo_context, 0, 0, image_size, image_size);
  cairo_fill(cairo_context);
  cairo_destroy(cairo_context);

  return TRUE;
}

void ThrobberGtk::Init() {
  animation_.SetSlideDuration(kThrobberDurationMs);
  animation_.SetTweenType(gfx::Tween::LINEAR);
  widget_.Own(gtk_image_new());
  gtk_widget_set_can_focus(widget_.get(), FALSE);
  g_signal_connect(widget_.get(), "expose-event", G_CALLBACK(OnExposeThunk),
                   this);

  theme_service_->InitThemesFor(this);
  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
                 content::Source<ThemeService>(theme_service_));
}

void ThrobberGtk::LoadFrames() {
  frames_ = theme_service_->GetImageNamed(IDR_THROBBER);
  DCHECK(!frames_.IsEmpty());
  const int width = frames_.ToCairo()->Width();
  const int height = frames_.ToCairo()->Height();
  DCHECK_EQ(0, width % height);
  num_frames_ = width / height;

  gtk_widget_set_size_request(widget_.get(), height, height);
}