diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 19:08:28 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 19:08:28 +0000 |
commit | fa4ce08390f4ce6017dda5aa0b753f2f0f7e52a0 (patch) | |
tree | 7efca7ac7426b58e8be04b7fe9d8be2c540dc1df | |
parent | c87b2881458763fb9d82066bbfd2fe696431e2db (diff) | |
download | chromium_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.cc | 76 | ||||
-rw-r--r-- | chrome/browser/gtk/info_bubble_gtk.h | 75 |
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_; |