diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-28 01:18:29 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-28 01:18:29 +0000 |
commit | 2ca77d23336c7ff61b7856aed33197df1e851edb (patch) | |
tree | 8b8ad47ff6eafc1ab017e843e595d6e18f62ab97 /chrome/browser/gtk | |
parent | 1f7b4176c3b8db732c8cc1842283d78908aa9cf6 (diff) | |
download | chromium_src-2ca77d23336c7ff61b7856aed33197df1e851edb.zip chromium_src-2ca77d23336c7ff61b7856aed33197df1e851edb.tar.gz chromium_src-2ca77d23336c7ff61b7856aed33197df1e851edb.tar.bz2 |
GTK: throb a bookmark folder button when a URL is added to it.
BUG=16782
TEST=add bookmark as a descendant of a bookmark bar folder node, watch it throb. Clicking it should make the throbbing stop.
Review URL: http://codereview.chromium.org/543201
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37362 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_gtk.cc | 80 | ||||
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_gtk.h | 21 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_button.cc | 41 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_button.h | 8 | ||||
-rw-r--r-- | chrome/browser/gtk/nine_box.cc | 62 | ||||
-rw-r--r-- | chrome/browser/gtk/nine_box.h | 5 | ||||
-rw-r--r-- | chrome/browser/gtk/throb_controller_gtk.cc | 95 | ||||
-rw-r--r-- | chrome/browser/gtk/throb_controller_gtk.h | 67 |
8 files changed, 337 insertions, 42 deletions
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc index b8c3c20..ad75450 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk.cc @@ -30,6 +30,7 @@ #include "chrome/browser/gtk/rounded_window.h" #include "chrome/browser/gtk/tabstrip_origin_provider.h" #include "chrome/browser/gtk/tabs/tab_strip_gtk.h" +#include "chrome/browser/gtk/throb_controller_gtk.h" #include "chrome/browser/gtk/view_id_util.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/ntp_background_util.h" @@ -133,7 +134,7 @@ BookmarkBarGtk::BookmarkBarGtk(BrowserWindowGtk* window, menu_bar_helper_(this), floating_(false), last_allocation_width_(-1), - event_box_paint_factory_(this) { + method_factory_(this) { if (profile->GetProfileSyncService()) { // Obtain a pointer to the profile sync service and add our instance as an // observer. @@ -403,13 +404,13 @@ void BookmarkBarGtk::BookmarkNodeMoved(BookmarkModel* model, void BookmarkBarGtk::BookmarkNodeAdded(BookmarkModel* model, const BookmarkNode* parent, int index) { + const BookmarkNode* node = parent->GetChild(index); if (parent != model_->GetBookmarkBarNode()) { - // We only care about nodes on the bookmark bar. + StartThrobbing(node); return; } DCHECK(index >= 0 && index <= GetBookmarkButtonCount()); - const BookmarkNode* node = parent->GetChild(index); GtkToolItem* item = CreateBookmarkToolItem(node); gtk_toolbar_insert(GTK_TOOLBAR(bookmark_toolbar_.get()), item, index); @@ -418,6 +419,10 @@ void BookmarkBarGtk::BookmarkNodeAdded(BookmarkModel* model, SetInstructionState(); SetChevronState(); + + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &BookmarkBarGtk::StartThrobbing, node)); } void BookmarkBarGtk::BookmarkNodeRemoved(BookmarkModel* model, @@ -686,6 +691,61 @@ bool BookmarkBarGtk::GetTabContentsSize(gfx::Size* size) { return true; } +void BookmarkBarGtk::StartThrobbing(const BookmarkNode* node) { + const BookmarkNode* parent_on_bb = NULL; + for (const BookmarkNode* parent = node; parent; + parent = parent->GetParent()) { + if (parent->GetParent() == model_->GetBookmarkBarNode()) { + parent_on_bb = parent; + break; + } + } + + // Descendant of "Other Bookmarks". + if (!parent_on_bb) + return; + + int hidden = GetFirstHiddenBookmark(0, NULL); + int idx = model_->GetBookmarkBarNode()->IndexOfChild(parent_on_bb); + GtkWidget* widget_to_throb = NULL; + + if (hidden >= idx) { + widget_to_throb = overflow_button_; + } else { + if (parent_on_bb->is_url()) + return; + widget_to_throb = gtk_bin_get_child(GTK_BIN(gtk_toolbar_get_nth_item( + GTK_TOOLBAR(bookmark_toolbar_.get()), idx))); + } + + SetThrobbingWidget(widget_to_throb); +} + +void BookmarkBarGtk::SetThrobbingWidget(GtkWidget* widget) { + if (throbbing_widget_) { + ThrobControllerGtk* throbber = + ThrobControllerGtk::GetThrobControllerGtk(throbbing_widget_); + if (throbber) + throbber->Destroy(); + + g_signal_handlers_disconnect_by_func( + throbbing_widget_, + reinterpret_cast<gpointer>(OnThrobbingWidgetDestroy), + this); + g_object_unref(throbbing_widget_); + throbbing_widget_ = NULL; + } + + if (widget) { + throbbing_widget_ = widget; + g_object_ref(throbbing_widget_); + g_signal_connect(throbbing_widget_, "destroy", + G_CALLBACK(OnThrobbingWidgetDestroy), this); + + ThrobControllerGtk::ThrobFor(throbbing_widget_); + } +} + bool BookmarkBarGtk::IsAlwaysShown() { return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar); } @@ -962,6 +1022,12 @@ void BookmarkBarGtk::OnButtonDragGet(GtkWidget* widget, GdkDragContext* context, // static void BookmarkBarGtk::OnFolderClicked(GtkWidget* sender, BookmarkBarGtk* bar) { + // Stop its throbbing, if any. + ThrobControllerGtk* throbber = + ThrobControllerGtk::GetThrobControllerGtk(sender); + if (throbber) + throbber->Destroy(); + bar->PopupForButton(sender); } @@ -1164,7 +1230,7 @@ void BookmarkBarGtk::OnParentSizeAllocate(GtkWidget* widget, // be asynchronous. if (bar->floating_) { MessageLoop::current()->PostTask(FROM_HERE, - bar->event_box_paint_factory_.NewRunnableMethod( + bar->method_factory_.NewRunnableMethod( &BookmarkBarGtk::PaintEventBox)); } } @@ -1216,6 +1282,12 @@ gboolean BookmarkBarGtk::OnSeparatorExpose(GtkWidget* widget, return TRUE; } +// static +void BookmarkBarGtk::OnThrobbingWidgetDestroy(GtkWidget* widget, + BookmarkBarGtk* bar) { + bar->SetThrobbingWidget(NULL); +} + // MenuBarHelper::Delegate implementation -------------------------------------- void BookmarkBarGtk::PopupForButton(GtkWidget* button) { const BookmarkNode* node = GetNodeForToolButton(button); diff --git a/chrome/browser/gtk/bookmark_bar_gtk.h b/chrome/browser/gtk/bookmark_bar_gtk.h index 98e958e..837905a 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.h +++ b/chrome/browser/gtk/bookmark_bar_gtk.h @@ -133,7 +133,7 @@ class BookmarkBarGtk : public AnimationDelegate, // |extra_space| is how much extra space to give the toolbar during the // calculation (for the purposes of determining if ditching the chevron // would be a good idea). - // If non-NULL, |showing_folders| is packed with all the folders that are + // If non-NULL, |showing_folders| will be packed with all the folders that are // showing on the bar. int GetFirstHiddenBookmark(int extra_space, std::vector<GtkWidget*>* showing_folders); @@ -155,6 +155,14 @@ class BookmarkBarGtk : public AnimationDelegate, // DCHECKs and returns false. Otherwise, sets |size| to the correct value. bool GetTabContentsSize(gfx::Size* size); + // Makes the appropriate widget on the bookmark bar stop throbbing + // (a folder, the overflow chevron, or nothing). + void StartThrobbing(const BookmarkNode* node); + + // Set |throbbing_widget_| to |widget|. Also makes sure that + // |throbbing_widget_| doesn't become stale. + void SetThrobbingWidget(GtkWidget* widget); + // Overridden from BookmarkModelObserver: // Invoked when the bookmark model has finished loading. Creates a button @@ -262,6 +270,10 @@ class BookmarkBarGtk : public AnimationDelegate, GtkAllocation* allocation, BookmarkBarGtk* bar); + // |throbbing_widget_| callback. + static void OnThrobbingWidgetDestroy(GtkWidget* widget, + BookmarkBarGtk* bar); + // ProfileSyncServiceObserver method. virtual void OnStateChanged(); @@ -360,8 +372,11 @@ class BookmarkBarGtk : public AnimationDelegate, // of this so we don't force too many paints. gfx::Size last_tab_contents_size_; - // We post delayed paints using this factory. - ScopedRunnableMethodFactory<BookmarkBarGtk> event_box_paint_factory_; + // The currently throbbing widget. This is NULL if no widget is throbbing. + // We track it because we only want to allow one widget to throb at a time. + GtkWidget* throbbing_widget_; + + ScopedRunnableMethodFactory<BookmarkBarGtk> method_factory_; }; #endif // CHROME_BROWSER_GTK_BOOKMARK_BAR_GTK_H_ diff --git a/chrome/browser/gtk/gtk_chrome_button.cc b/chrome/browser/gtk/gtk_chrome_button.cc index ef3c75b..0be3f18 100644 --- a/chrome/browser/gtk/gtk_chrome_button.cc +++ b/chrome/browser/gtk/gtk_chrome_button.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -32,6 +32,8 @@ struct _GtkChromeButtonPrivate { // If true, we use images provided by the theme instead of GTK's default // button rendering. gboolean use_gtk_rendering; + + gdouble hover_state; }; G_DEFINE_TYPE(GtkChromeButton, gtk_chrome_button, GTK_TYPE_BUTTON) @@ -46,9 +48,8 @@ static void gtk_chrome_button_class_init(GtkChromeButtonClass* button_class) { "}" "widget_class \"*.<GtkChromeButton>\" style \"chrome-button\""); - GObjectClass* gobject_class = G_OBJECT_CLASS(button_class); - GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>( - button_class); + GtkWidgetClass* widget_class = + reinterpret_cast<GtkWidgetClass*>(button_class); widget_class->expose_event = gtk_chrome_button_expose; g_nine_box_prelight = new NineBox( @@ -73,6 +74,7 @@ static void gtk_chrome_button_class_init(GtkChromeButtonClass* button_class) { IDR_TEXTBUTTON_BOTTOM_P, IDR_TEXTBUTTON_BOTTOM_RIGHT_P); + GObjectClass* gobject_class = G_OBJECT_CLASS(button_class); g_type_class_add_private(gobject_class, sizeof(GtkChromeButtonPrivate)); } @@ -80,6 +82,7 @@ static void gtk_chrome_button_init(GtkChromeButton* button) { GtkChromeButtonPrivate* priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); priv->paint_state = -1; priv->use_gtk_rendering = FALSE; + priv->hover_state = -1.0; GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); } @@ -102,15 +105,17 @@ static gboolean gtk_chrome_button_expose(GtkWidget* widget, (widget, event); } } else { - NineBox* nine_box = NULL; - if (paint_state == GTK_STATE_PRELIGHT) - nine_box = g_nine_box_prelight; - else if (paint_state == GTK_STATE_ACTIVE) - nine_box = g_nine_box_active; - - // Only draw theme graphics if we have some. - if (nine_box) - nine_box->RenderToWidget(widget); + double effective_hover_state = paint_state == GTK_STATE_PRELIGHT ? + 1.0 : 0.0; + if (priv->hover_state >= 0.0) + effective_hover_state = priv->hover_state; + + if (paint_state == GTK_STATE_ACTIVE) { + g_nine_box_active->RenderToWidget(widget); + } else { + g_nine_box_prelight->RenderToWidgetWithOpacity(widget, + effective_hover_state); + } } // If we have a child widget, draw it. @@ -153,4 +158,14 @@ void gtk_chrome_button_set_use_gtk_rendering(GtkChromeButton* button, priv->use_gtk_rendering = value; } +void gtk_chrome_button_set_hover_state(GtkChromeButton* button, + gdouble state) { + GtkChromeButtonPrivate* priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); + if (state >= 0.0 && state <= 1.0) + priv->hover_state = state; + else + priv->hover_state = -1.0; + gtk_widget_queue_draw(GTK_WIDGET(button)); +} + G_END_DECLS diff --git a/chrome/browser/gtk/gtk_chrome_button.h b/chrome/browser/gtk/gtk_chrome_button.h index 921e1e2..56ef67c 100644 --- a/chrome/browser/gtk/gtk_chrome_button.h +++ b/chrome/browser/gtk/gtk_chrome_button.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -49,6 +49,12 @@ void gtk_chrome_button_unset_paint_state(GtkChromeButton* button); void gtk_chrome_button_set_use_gtk_rendering(GtkChromeButton* button, gboolean value); +// Sets the partial hover state of the button. The acceptable range is 0.0 to +// 1.0. If |state| is outside of that range, then revert the button to normal +// hovering. +void gtk_chrome_button_set_hover_state(GtkChromeButton* button, + gdouble state); + G_END_DECLS #endif // CHROME_BROWSER_GTK_GTK_CHROME_BUTTON_H_ diff --git a/chrome/browser/gtk/nine_box.cc b/chrome/browser/gtk/nine_box.cc index ddad809..572c1b5 100644 --- a/chrome/browser/gtk/nine_box.cc +++ b/chrome/browser/gtk/nine_box.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -15,18 +15,36 @@ namespace { // Draw pixbuf |src| into |dst| at position (x, y). -void DrawPixbuf(cairo_t* cr, GdkPixbuf* src, int x, int y) { +void DrawPixbuf(cairo_t* cr, GdkPixbuf* src, int x, int y, double alpha) { gdk_cairo_set_source_pixbuf(cr, src, x, y); - cairo_paint(cr); + cairo_paint_with_alpha(cr, alpha); } // Tile pixbuf |src| across |cr| at |x|, |y| for |width| and |height|. void TileImage(cairo_t* cr, GdkPixbuf* src, - int x, int y, int width, int height) { - gdk_cairo_set_source_pixbuf(cr, src, x, y); - cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); - cairo_rectangle(cr, x, y, width, height); - cairo_fill(cr); + int x, int y, int width, int height, double alpha) { + if (alpha == 1.0) { + gdk_cairo_set_source_pixbuf(cr, src, x, y); + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); + cairo_rectangle(cr, x, y, width, height); + cairo_fill(cr); + } else { + // Since there is no easy way to apply a mask to a fill operation, we create + // a secondary surface and tile into that, then paint it with |alpha|. + cairo_surface_t* surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width, height); + cairo_t* tiled = cairo_create(surface); + gdk_cairo_set_source_pixbuf(tiled, src, 0, 0); + cairo_pattern_set_extend(cairo_get_source(tiled), CAIRO_EXTEND_REPEAT); + cairo_rectangle(tiled, 0, 0, width, height); + cairo_fill(tiled); + + cairo_set_source_surface(cr, surface, x, y); + cairo_paint_with_alpha(cr, alpha); + + cairo_destroy(tiled); + cairo_surface_destroy(surface); + } } } // namespace @@ -88,6 +106,10 @@ NineBox::~NineBox() { } void NineBox::RenderToWidget(GtkWidget* dst) const { + RenderToWidgetWithOpacity(dst, 1.0); +} + +void NineBox::RenderToWidgetWithOpacity(GtkWidget* dst, double opacity) const { int dst_width = dst->allocation.width; int dst_height = dst->allocation.height; @@ -117,35 +139,35 @@ void NineBox::RenderToWidget(GtkWidget* dst) const { // Top row, center image is horizontally tiled. if (images_[0]) - DrawPixbuf(cr, images_[0], 0, 0); + DrawPixbuf(cr, images_[0], 0, 0, opacity); if (images_[1]) - RenderTopCenterStrip(cr, x1, 0, x2 - x1); + TileImage(cr, images_[1], x1, 0, x2 - x1, y1, opacity); if (images_[2]) - DrawPixbuf(cr, images_[2], x2, 0); + DrawPixbuf(cr, images_[2], x2, 0, opacity); // Center row, all images are vertically tiled, center is horizontally tiled. if (images_[3]) - TileImage(cr, images_[3], 0, y1, x1, y2 - y1); + TileImage(cr, images_[3], 0, y1, x1, y2 - y1, opacity); if (images_[4]) - TileImage(cr, images_[4], x1, y1, x2 - x1, y2 - y1); + TileImage(cr, images_[4], x1, y1, x2 - x1, y2 - y1, opacity); if (images_[5]) - TileImage(cr, images_[5], x2, y1, dst_width - x2, y2 - y1); + TileImage(cr, images_[5], x2, y1, dst_width - x2, y2 - y1, opacity); // Bottom row, center image is horizontally tiled. if (images_[6]) - DrawPixbuf(cr, images_[6], 0, y2); + DrawPixbuf(cr, images_[6], 0, y2, opacity); if (images_[7]) - TileImage(cr, images_[7], x1, y2, x2 - x1, dst_height - y2); + TileImage(cr, images_[7], x1, y2, x2 - x1, dst_height - y2, opacity); if (images_[8]) - DrawPixbuf(cr, images_[8], x2, y2); + DrawPixbuf(cr, images_[8], x2, y2, opacity); cairo_destroy(cr); } -void NineBox::RenderTopCenterStrip(cairo_t* cr, - int x, int y, int width) const { +void NineBox::RenderTopCenterStrip(cairo_t* cr, int x, int y, + int width) const { const int height = gdk_pixbuf_get_height(images_[1]); - TileImage(cr, images_[1], x, y, width, height); + TileImage(cr, images_[1], x, y, width, height, 1.0); } void NineBox::ChangeWhiteToTransparent() { diff --git a/chrome/browser/gtk/nine_box.h b/chrome/browser/gtk/nine_box.h index bd3dc1e..c00a99b 100644 --- a/chrome/browser/gtk/nine_box.h +++ b/chrome/browser/gtk/nine_box.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -35,6 +35,9 @@ class NineBox { // The images will be tiled to fit into the widget. void RenderToWidget(GtkWidget* dst) const; + // As above, but rendered partially transparent. + void RenderToWidgetWithOpacity(GtkWidget* dst, double opacity) const; + // Render the top row of images to |dst| between |x1| and |x1| + |width|. // This is split from RenderToWidget so the toolbar can use it. void RenderTopCenterStrip(cairo_t* cr, int x, int y, int width) const; diff --git a/chrome/browser/gtk/throb_controller_gtk.cc b/chrome/browser/gtk/throb_controller_gtk.cc new file mode 100644 index 0000000..422d388 --- /dev/null +++ b/chrome/browser/gtk/throb_controller_gtk.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2010 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/gtk/throb_controller_gtk.h" + +#include "base/message_loop.h" +#include "chrome/browser/gtk/gtk_chrome_button.h" + +static const gchar* kThrobControllerGtkKey = "__THROB_CONTROLLER_GTK__"; + +ThrobControllerGtk::ThrobControllerGtk(GtkWidget* button) + : animation_(this), + button_(button) { + g_object_ref(button_); + g_signal_connect(button_, "destroy", G_CALLBACK(OnButtonDestroy), this); + +#ifndef NDEBUG + if (g_object_get_data(G_OBJECT(button_), kThrobControllerGtkKey)) + NOTREACHED(); +#endif // !NDEBUG + + g_object_set_data(G_OBJECT(button), kThrobControllerGtkKey, this); +} + +ThrobControllerGtk::~ThrobControllerGtk() { +} + +void ThrobControllerGtk::StartThrobbing(int cycles) { + animation_.StartThrobbing(cycles); +} + +// static +ThrobControllerGtk* ThrobControllerGtk::GetThrobControllerGtk( + GtkWidget* button) { + return reinterpret_cast<ThrobControllerGtk*>( + g_object_get_data(G_OBJECT(button), kThrobControllerGtkKey)); +} + +// static +void ThrobControllerGtk::ThrobFor(GtkWidget* button) { + if (!GTK_IS_CHROME_BUTTON(button)) { + NOTREACHED(); + return; + } + + (new ThrobControllerGtk(button))-> + StartThrobbing(std::numeric_limits<int>::max()); +} + +void ThrobControllerGtk::Destroy() { + gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), -1.0); + g_signal_handlers_disconnect_by_func( + button_, + reinterpret_cast<gpointer>(OnButtonDestroy), + this); + g_object_set_data(G_OBJECT(button_), kThrobControllerGtkKey, NULL); + g_object_unref(button_); + button_ = NULL; + + // Since this can be called from within AnimationEnded(), which is called + // while ThrobAnimation is still doing work, we need to let the stack unwind + // before |animation_| gets deleted. + MessageLoop::current()->DeleteSoon(FROM_HERE, this); +} + +void ThrobControllerGtk::AnimationProgressed(const Animation* animation) { + if (!button_) + return; + + gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), + animation->GetCurrentValue()); +} + +void ThrobControllerGtk::AnimationEnded(const Animation* animation) { + if (!button_) + return; + + if (animation_.cycles_remaining() <= 1) + Destroy(); +} + +void ThrobControllerGtk::AnimationCanceled(const Animation* animation) { + if (!button_) + return; + + if (animation_.cycles_remaining() <= 1) + Destroy(); +} + +// static +void ThrobControllerGtk::OnButtonDestroy(GtkWidget* widget, + ThrobControllerGtk* button) { + button->Destroy(); +} diff --git a/chrome/browser/gtk/throb_controller_gtk.h b/chrome/browser/gtk/throb_controller_gtk.h new file mode 100644 index 0000000..9b596cc --- /dev/null +++ b/chrome/browser/gtk/throb_controller_gtk.h @@ -0,0 +1,67 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_GTK_THROB_CONTROLLER_GTK_H_ +#define CHROME_BROWSER_GTK_THROB_CONTROLLER_GTK_H_ + +#include "app/throb_animation.h" +#include "base/scoped_ptr.h" + +typedef struct _GtkWidget GtkWidget; + +// This class handles the "throbbing" of a GtkChromeButton. The visual effect +// of throbbing is created by painting partially transparent hover effects. It +// only works in non-gtk theme mode. This class mainly exists to glue an +// AnimationDelegate (C++ class) to a GtkChromeButton* (GTK/c object). Usage is +// as follows: +// +// GtkWidget* button = gtk_chrome_button_new(); +// ThrobControllerGtk::ThrobFor(button); +// +// The ThrobFor() function will handle creation of the ThrobControllerGtk +// object, which will delete itself automatically when the throbbing is done, +// or when the widget is destroyed. It may also be canceled prematurely with +// +// ThrobControllerGtk* throbber = +// ThrobControllerGtk::GetThrobControllerGtk(button); +// throbber->Destroy(); +class ThrobControllerGtk : public AnimationDelegate { + public: + virtual ~ThrobControllerGtk(); + + GtkWidget* button() { return button_; } + + // Throb for |cycles| cycles. This will override the current remaining + // number of cycles. + void StartThrobbing(int cycles); + + // Get the ThrobControllerGtk for a given GtkChromeButton*. It is an error + // to call this on a widget that is not a GtkChromeButton*. + static ThrobControllerGtk* GetThrobControllerGtk(GtkWidget* button); + + // Make |button| throb. It is an error to try to have two ThrobControllerGtk + // instances for one GtkChromeButton*. + static void ThrobFor(GtkWidget* button); + + // Stop throbbing and delete |this|. + void Destroy(); + + private: + explicit ThrobControllerGtk(GtkWidget* button); + + // Overridden from AnimationDelegate. + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationEnded(const Animation* animation); + virtual void AnimationCanceled(const Animation* animation); + + static void OnButtonDestroy(GtkWidget* widget, + ThrobControllerGtk* button); + + ThrobAnimation animation_; + GtkWidget* button_; + + DISALLOW_COPY_AND_ASSIGN(ThrobControllerGtk); +}; + +#endif // CHROME_BROWSER_GTK_THROB_CONTROLLER_GTK_H_ |