summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 19:08:28 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-25 19:08:28 +0000
commitfa4ce08390f4ce6017dda5aa0b753f2f0f7e52a0 (patch)
tree7efca7ac7426b58e8be04b7fe9d8be2c540dc1df
parentc87b2881458763fb9d82066bbfd2fe696431e2db (diff)
downloadchromium_src-fa4ce08390f4ce6017dda5aa0b753f2f0f7e52a0.zip
chromium_src-fa4ce08390f4ce6017dda5aa0b753f2f0f7e52a0.tar.gz
chromium_src-fa4ce08390f4ce6017dda5aa0b753f2f0f7e52a0.tar.bz2
GTK: hide the current grab widget when showing an info bubble.
Problem: Extension popups can take a while to appear after the user clicks the icon. If the user clicks something else that causes a grab (like a context or dropdown menu, or another info bubble), then that grab is shadowed and the widget behaves oddly (having two info bubbles open simultaneously, having an unclosable menu, etc.) Solution: Hide any widget that has a grab when the info bubble is being created. Delete info bubbles that get hidden. Etc.: You can still pretty easily get into a weird trapped state when the delayed info bubble interacts with tab dragging. BUG=none TEST=left click then quickly right click a browser action with extension popup Review URL: http://codereview.chromium.org/1317001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42645 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/gtk/info_bubble_gtk.cc76
-rw-r--r--chrome/browser/gtk/info_bubble_gtk.h75
2 files changed, 54 insertions, 97 deletions
diff --git a/chrome/browser/gtk/info_bubble_gtk.cc b/chrome/browser/gtk/info_bubble_gtk.cc
index 5e1c2f2..beae5d4 100644
--- a/chrome/browser/gtk/info_bubble_gtk.cc
+++ b/chrome/browser/gtk/info_bubble_gtk.cc
@@ -88,11 +88,11 @@ InfoBubbleGtk::~InfoBubbleGtk() {
if (toplevel_window_) {
g_signal_handlers_disconnect_by_func(
toplevel_window_,
- reinterpret_cast<gpointer>(HandleToplevelConfigureThunk),
+ reinterpret_cast<gpointer>(OnToplevelConfigureThunk),
this);
g_signal_handlers_disconnect_by_func(
toplevel_window_,
- reinterpret_cast<gpointer>(HandleToplevelUnmapThunk),
+ reinterpret_cast<gpointer>(OnToplevelUnmapThunk),
this);
}
toplevel_window_ = NULL;
@@ -103,6 +103,11 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
GtkWidget* content,
ArrowLocationGtk arrow_location,
bool grab_input) {
+ // If there is a current grab widget (menu, other info bubble, etc.), hide it.
+ GtkWidget* current_grab_widget = gtk_grab_get_current();
+ if (current_grab_widget)
+ gtk_widget_hide(current_grab_widget);
+
DCHECK(!window_);
toplevel_window_ = toplevel_window;
rect_ = rect;
@@ -120,7 +125,7 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
// Attach our accelerator group to the window with an escape accelerator.
gtk_accel_group_connect(accel_group_, GDK_Escape,
static_cast<GdkModifierType>(0), static_cast<GtkAccelFlags>(0),
- g_cclosure_new(G_CALLBACK(&HandleEscapeThunk), this, NULL));
+ g_cclosure_new(G_CALLBACK(&OnEscapeThunk), this, NULL));
gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group_);
GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
@@ -133,51 +138,37 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
// GtkWidget only exposes the bitmap mask interface. Use GDK to more
// efficently mask a GdkRegion. Make sure the window is realized during
- // HandleSizeAllocate, so the mask can be applied to the GdkWindow.
+ // OnSizeAllocate, so the mask can be applied to the GdkWindow.
gtk_widget_realize(window_);
UpdateArrowLocation(true); // Force move and reshape.
StackWindow();
- gtk_widget_add_events(window_, GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_add_events(window_, GDK_BUTTON_PRESS_MASK);
g_signal_connect(window_, "expose-event",
- G_CALLBACK(HandleExposeThunk), this);
+ G_CALLBACK(OnExposeThunk), this);
g_signal_connect(window_, "size-allocate",
- G_CALLBACK(HandleSizeAllocateThunk), this);
+ G_CALLBACK(OnSizeAllocateThunk), this);
g_signal_connect(window_, "button-press-event",
- G_CALLBACK(&HandleButtonPressThunk), this);
+ G_CALLBACK(OnButtonPressThunk), this);
g_signal_connect(window_, "destroy",
- G_CALLBACK(&HandleDestroyThunk), this);
-
- g_signal_connect(toplevel_window, "configure-event",
- G_CALLBACK(&HandleToplevelConfigureThunk), this);
- g_signal_connect(toplevel_window, "unmap-event",
- G_CALLBACK(&HandleToplevelUnmapThunk), this);
+ G_CALLBACK(OnDestroyThunk), this);
+ g_signal_connect(window_, "hide",
+ G_CALLBACK(OnHideThunk), this);
+
+ g_signal_connect(toplevel_window_, "configure-event",
+ G_CALLBACK(OnToplevelConfigureThunk), this);
+ g_signal_connect(toplevel_window_, "unmap-event",
+ G_CALLBACK(OnToplevelUnmapThunk), this);
// Set |toplevel_window_| to NULL if it gets destroyed.
- g_signal_connect(toplevel_window, "destroy",
+ g_signal_connect(toplevel_window_, "destroy",
G_CALLBACK(gtk_widget_destroyed), &toplevel_window_);
gtk_widget_show_all(window_);
if (grab_input_) {
- // We add a GTK (application-level) grab. This means we will get all
- // mouse events for our application, even if they were delivered on another
- // window. We don't need this to get button presses outside of the bubble's
- // window so we'll know to close it (the pointer grab takes care of that),
- // but it prevents other widgets from getting highlighted when the pointer
- // moves over them.
- //
- // (Ideally we wouldn't add the window to a group and it would just get all
- // the mouse events, but gtk_grab_add() doesn't appear to do anything in
- // that case. Adding it to the toplevel window's group first appears to
- // block enter/leave events for that window and its subwindows, although
- // other browser windows still receive them).
- gtk_window_group_add_window(gtk_window_get_group(toplevel_window),
- GTK_WINDOW(window_));
gtk_grab_add(window_);
-
GrabPointerAndKeyboard();
}
@@ -350,7 +341,7 @@ void InfoBubbleGtk::Close() {
// automatically do that when we destroy our window.
DCHECK(window_);
gtk_widget_destroy(window_);
- // |this| has been deleted, see HandleDestroy.
+ // |this| has been deleted, see OnDestroy.
}
void InfoBubbleGtk::GrabPointerAndKeyboard() {
@@ -379,13 +370,13 @@ void InfoBubbleGtk::GrabPointerAndKeyboard() {
}
}
-gboolean InfoBubbleGtk::HandleEscape() {
+gboolean InfoBubbleGtk::OnEscape() {
closed_by_escape_ = true;
Close();
return TRUE;
}
-gboolean InfoBubbleGtk::HandleExpose() {
+gboolean InfoBubbleGtk::OnExpose(GtkWidget* widget, GdkEventExpose* expose) {
GdkDrawable* drawable = GDK_DRAWABLE(window_->window);
GdkGC* gc = gdk_gc_new(drawable);
gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
@@ -403,7 +394,8 @@ gboolean InfoBubbleGtk::HandleExpose() {
// When our size is initially allocated or changed, we need to recompute
// and apply our shape mask region.
-void InfoBubbleGtk::HandleSizeAllocate() {
+void InfoBubbleGtk::OnSizeAllocate(GtkWidget* widget,
+ GtkAllocation* allocation) {
if (!UpdateArrowLocation(false)) {
UpdateWindowShape();
if (current_arrow_location_ == ARROW_LOCATION_TOP_RIGHT)
@@ -411,7 +403,8 @@ void InfoBubbleGtk::HandleSizeAllocate() {
}
}
-gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) {
+gboolean InfoBubbleGtk::OnButtonPress(GtkWidget* widget,
+ GdkEventButton* event) {
// If we got a click in our own window, that's okay (we need to additionally
// check that it falls within our bounds, since we've grabbed the pointer and
// some events that actually occurred in other windows will be reported with
@@ -436,7 +429,7 @@ gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) {
return FALSE;
}
-gboolean InfoBubbleGtk::HandleDestroy() {
+gboolean InfoBubbleGtk::OnDestroy(GtkWidget* widget) {
// We are self deleting, we have a destroy signal setup to catch when we
// destroy the widget manually, or the window was closed via X. This will
// delete the InfoBubbleGtk object.
@@ -444,14 +437,19 @@ gboolean InfoBubbleGtk::HandleDestroy() {
return FALSE; // Propagate.
}
-gboolean InfoBubbleGtk::HandleToplevelConfigure(GdkEventConfigure* event) {
+void InfoBubbleGtk::OnHide(GtkWidget* widget) {
+ gtk_widget_destroy(widget);
+}
+
+gboolean InfoBubbleGtk::OnToplevelConfigure(GtkWidget* widget,
+ GdkEventConfigure* event) {
if (!UpdateArrowLocation(false))
MoveWindow();
StackWindow();
return FALSE;
}
-gboolean InfoBubbleGtk::HandleToplevelUnmap() {
+gboolean InfoBubbleGtk::OnToplevelUnmap(GtkWidget* widget, GdkEvent* event) {
Close();
return FALSE;
}
diff --git a/chrome/browser/gtk/info_bubble_gtk.h b/chrome/browser/gtk/info_bubble_gtk.h
index 4d3d763..10274fa 100644
--- a/chrome/browser/gtk/info_bubble_gtk.h
+++ b/chrome/browser/gtk/info_bubble_gtk.h
@@ -15,6 +15,7 @@
#include <gtk/gtk.h>
+#include "app/gtk_signal.h"
#include "base/basictypes.h"
#include "chrome/common/notification_registrar.h"
#include "gfx/point.h"
@@ -139,65 +140,23 @@ class InfoBubbleGtk : public NotificationObserver {
// sure that we have the input focus.
void GrabPointerAndKeyboard();
- static gboolean HandleEscapeThunk(GtkAccelGroup* group,
- GObject* acceleratable,
- guint keyval,
- GdkModifierType modifier,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleEscape();
+ static gboolean OnEscapeThunk(GtkAccelGroup* group,
+ GObject* acceleratable,
+ guint keyval,
+ GdkModifierType modifier,
+ gpointer user_data) {
+ return reinterpret_cast<InfoBubbleGtk*>(user_data)->OnEscape();
}
- gboolean HandleEscape();
-
- static gboolean HandleExposeThunk(GtkWidget* widget,
- GdkEventExpose* event,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleExpose();
- }
- gboolean HandleExpose();
-
- static void HandleSizeAllocateThunk(GtkWidget* widget,
- GtkAllocation* allocation,
- gpointer user_data) {
- reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleSizeAllocate();
- }
- void HandleSizeAllocate();
-
- static gboolean HandleButtonPressThunk(GtkWidget* widget,
- GdkEventButton* event,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->
- HandleButtonPress(event);
- }
- gboolean HandleButtonPress(GdkEventButton* event);
-
- static gboolean HandleButtonReleaseThunk(GtkWidget* widget,
- GdkEventButton* event,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->
- HandleButtonRelease(event);
- }
- gboolean HandleButtonRelease(GdkEventButton* event);
-
- static gboolean HandleDestroyThunk(GtkWidget* widget,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleDestroy();
- }
- gboolean HandleDestroy();
-
- static gboolean HandleToplevelConfigureThunk(GtkWidget* widget,
- GdkEventConfigure* event,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->
- HandleToplevelConfigure(event);
- }
- gboolean HandleToplevelConfigure(GdkEventConfigure* event);
-
- static gboolean HandleToplevelUnmapThunk(GtkWidget* widget,
- GdkEvent* event,
- gpointer user_data) {
- return reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleToplevelUnmap();
- }
- gboolean HandleToplevelUnmap();
+ gboolean OnEscape();
+
+ CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnExpose, GdkEventExpose*);
+ CHROMEGTK_CALLBACK_1(InfoBubbleGtk, void, OnSizeAllocate, GtkAllocation*);
+ CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnButtonPress, GdkEventButton*);
+ CHROMEGTK_CALLBACK_0(InfoBubbleGtk, gboolean, OnDestroy);
+ CHROMEGTK_CALLBACK_0(InfoBubbleGtk, void, OnHide);
+ CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelConfigure,
+ GdkEventConfigure*);
+ CHROMEGTK_CALLBACK_1(InfoBubbleGtk, gboolean, OnToplevelUnmap, GdkEvent*);
// The caller supplied delegate, can be NULL.
InfoBubbleGtkDelegate* delegate_;