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/ui/gtk/throbber_gtk.h" 6 7 #include "base/logging.h" 8 #include "chrome/browser/chrome_notification_types.h" 9 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 10 #include "content/public/browser/notification_source.h" 11 #include "grit/ui_resources.h" 12 #include "ui/gfx/animation/tween.h" 13 #include "ui/gfx/image/cairo_cached_surface.h" 14 15 namespace { 16 17 // The length of a single cycle of the animation in milliseconds. 18 const int kThrobberDurationMs = 750; 19 20 } // namespace 21 22 ThrobberGtk::ThrobberGtk(GtkThemeService* theme_service) 23 : theme_service_(theme_service), 24 animation_(this), 25 num_frames_(0) { 26 DCHECK(theme_service_); 27 Init(); 28 } 29 30 ThrobberGtk::~ThrobberGtk() { 31 widget_.Destroy(); 32 } 33 34 void ThrobberGtk::Start() { 35 animation_.Show(); 36 } 37 38 void ThrobberGtk::Stop() { 39 animation_.Reset(); 40 } 41 42 void ThrobberGtk::AnimationEnded(const gfx::Animation* animation) { 43 animation_.Reset(); 44 animation_.Show(); 45 } 46 47 void ThrobberGtk::AnimationProgressed(const gfx::Animation* animation) { 48 gtk_widget_queue_draw(widget_.get()); 49 } 50 51 void ThrobberGtk::Observe(int type, 52 const content::NotificationSource& source, 53 const content::NotificationDetails& details) { 54 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type); 55 LoadFrames(); 56 gtk_widget_queue_draw(widget_.get()); 57 } 58 59 gboolean ThrobberGtk::OnExpose(GtkWidget* widget, GdkEventExpose* expose) { 60 cairo_t* cairo_context = 61 gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget))); 62 GtkAllocation allocation; 63 gtk_widget_get_allocation(widget, &allocation); 64 cairo_translate(cairo_context, allocation.x, allocation.y); 65 66 gfx::CairoCachedSurface* cairo_frames = frames_.ToCairo(); 67 const int frame = 68 static_cast<int>(animation_.GetCurrentValue() * (num_frames_ - 1)); 69 const int image_size = cairo_frames->Height(); 70 const int image_offset = frame * image_size; 71 72 cairo_frames->SetSource(cairo_context, widget, -image_offset, 0); 73 cairo_rectangle(cairo_context, 0, 0, image_size, image_size); 74 cairo_fill(cairo_context); 75 cairo_destroy(cairo_context); 76 77 return TRUE; 78 } 79 80 void ThrobberGtk::Init() { 81 animation_.SetSlideDuration(kThrobberDurationMs); 82 animation_.SetTweenType(gfx::Tween::LINEAR); 83 widget_.Own(gtk_image_new()); 84 gtk_widget_set_can_focus(widget_.get(), FALSE); 85 g_signal_connect(widget_.get(), "expose-event", G_CALLBACK(OnExposeThunk), 86 this); 87 88 theme_service_->InitThemesFor(this); 89 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, 90 content::Source<ThemeService>(theme_service_)); 91 } 92 93 void ThrobberGtk::LoadFrames() { 94 frames_ = theme_service_->GetImageNamed(IDR_THROBBER); 95 DCHECK(!frames_.IsEmpty()); 96 const int width = frames_.ToCairo()->Width(); 97 const int height = frames_.ToCairo()->Height(); 98 DCHECK_EQ(0, width % height); 99 num_frames_ = width / height; 100 101 gtk_widget_set_size_request(widget_.get(), height, height); 102 } 103