15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/hover_controller_gtk.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
79ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_chrome_button.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/gtk/gtk_compat.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const gchar* kHoverControllerGtkKey = "__HOVER_CONTROLLER_GTK__";
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoverControllerGtk::HoverControllerGtk(GtkWidget* button)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : throb_animation_(this),
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hover_animation_(this),
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      button_(button) {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_object_ref(button_);
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), 0);
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(button_, "enter-notify-event",
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnEnterThunk), this);
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(button_, "leave-notify-event",
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnLeaveThunk), this);
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(button_, "destroy",
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnDestroyThunk), this);
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  signals_.Connect(button_, "hierarchy-changed",
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnHierarchyChangedThunk), this);
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef NDEBUG
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (g_object_get_data(G_OBJECT(button_), kHoverControllerGtkKey))
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // !NDEBUG
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_object_set_data(G_OBJECT(button), kHoverControllerGtkKey, this);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoverControllerGtk::~HoverControllerGtk() {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::StartThrobbing(int cycles) {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  throb_animation_.StartThrobbing(cycles);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GtkWidget* HoverControllerGtk::CreateChromeButton() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* widget = gtk_chrome_button_new();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new HoverControllerGtk(widget);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return widget;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HoverControllerGtk* HoverControllerGtk::GetHoverControllerGtk(
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GtkWidget* button) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return reinterpret_cast<HoverControllerGtk*>(
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_object_get_data(G_OBJECT(button), kHoverControllerGtkKey));
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::Destroy() {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), -1.0);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_object_set_data(G_OBJECT(button_), kHoverControllerGtkKey, NULL);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_object_unref(button_);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  button_ = NULL;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete this;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::AnimationProgressed(const ui::Animation* animation) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!button_)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore the hover animation if we are throbbing.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (animation == &hover_animation_ && throb_animation_.is_animating())
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_),
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    animation->GetCurrentValue());
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::AnimationEnded(const ui::Animation* animation) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!button_)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (animation != &throb_animation_)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (throb_animation_.cycles_remaining() <= 1)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), 0);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::AnimationCanceled(const ui::Animation* animation) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimationEnded(animation);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean HoverControllerGtk::OnEnter(GtkWidget* widget,
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GdkEventCrossing* event) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hover_animation_.Show();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean HoverControllerGtk::OnLeave(GtkWidget* widget,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GdkEventCrossing* event) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the user is holding a mouse button, we don't want to animate.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hover_animation_.Reset();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), 0);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hover_animation_.Hide();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::OnHierarchyChanged(GtkWidget* widget,
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            GtkWidget* previous_toplevel) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GTK+ does not emit leave-notify-event signals when a widget
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // becomes unanchored, so manually unset the hover states.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!gtk_widget_is_toplevel(gtk_widget_get_toplevel(widget))) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_widget_set_state(button_, GTK_STATE_NORMAL);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hover_animation_.Reset();
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_chrome_button_set_hover_state(GTK_CHROME_BUTTON(button_), 0.0);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HoverControllerGtk::OnDestroy(GtkWidget* widget) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Destroy();
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
128