summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-07 02:09:05 +0000
committerderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-07 02:09:05 +0000
commit0511c153260e5d402d7552ff7b47a2acb17bdf2b (patch)
tree53949c0ddc12b068179c50e7df52a386397d5e15
parent2f9bf8f9c6c6b11f7a440524d669f91e6750f28b (diff)
downloadchromium_src-0511c153260e5d402d7552ff7b47a2acb17bdf2b.zip
chromium_src-0511c153260e5d402d7552ff7b47a2acb17bdf2b.tar.gz
chromium_src-0511c153260e5d402d7552ff7b47a2acb17bdf2b.tar.bz2
Linux: Fix focus issues in bookmark bubble.
I wasn't aware of it in my initial change that made us use override-redirect windows for InfoBubbleGtk, but clicking on the bookmark bubble's folder combobox steals InfoBubbleGtk's pointer and keyboard grabs, resulting in the other widgets in the window behaving as if they're inactive after the combobox's popup is closed. This somewhat hacky change makes us reinstall the bubble's grabs after the popup closes. TEST=clicked in and out of bookmark bubble's combobox and checked that text entry still behaves normally Review URL: http://codereview.chromium.org/260019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@28218 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/gtk/bookmark_bubble_gtk.cc13
-rw-r--r--chrome/browser/gtk/bookmark_bubble_gtk.h8
-rw-r--r--chrome/browser/gtk/info_bubble_gtk.cc58
-rw-r--r--chrome/browser/gtk/info_bubble_gtk.h18
4 files changed, 70 insertions, 27 deletions
diff --git a/chrome/browser/gtk/bookmark_bubble_gtk.cc b/chrome/browser/gtk/bookmark_bubble_gtk.cc
index ca4f40a..94ef5ac 100644
--- a/chrome/browser/gtk/bookmark_bubble_gtk.cc
+++ b/chrome/browser/gtk/bookmark_bubble_gtk.cc
@@ -229,6 +229,8 @@ BookmarkBubbleGtk::BookmarkBubbleGtk(GtkWindow* toplevel_window,
G_CALLBACK(&HandleNameActivateThunk), this);
g_signal_connect(folder_combo_, "changed",
G_CALLBACK(&HandleFolderChangedThunk), this);
+ g_signal_connect(folder_combo_, "notify::popup-shown",
+ G_CALLBACK(&HandleFolderPopupShownThunk), this);
g_signal_connect(edit_button, "clicked",
G_CALLBACK(&HandleEditButtonThunk), this);
g_signal_connect(close_button, "clicked",
@@ -281,6 +283,17 @@ void BookmarkBubbleGtk::HandleFolderChanged() {
}
}
+void BookmarkBubbleGtk::HandleFolderPopupShown() {
+ // GtkComboBox grabs the keyboard and pointer when it displays its popup,
+ // which steals the grabs that InfoBubbleGtk had installed. When the popup is
+ // hidden, we notify InfoBubbleGtk so it can try to reacquire the grabs
+ // (otherwise, GTK won't activate our widgets when the user clicks in them).
+ gboolean popup_shown = FALSE;
+ g_object_get(G_OBJECT(folder_combo_), "popup-shown", &popup_shown, NULL);
+ if (!popup_shown)
+ bubble_->HandlePointerAndKeyboardUngrabbedByContent();
+}
+
void BookmarkBubbleGtk::HandleEditButton() {
UserMetrics::RecordAction(L"BookmarkBubble_Edit", profile_);
ShowEditor();
diff --git a/chrome/browser/gtk/bookmark_bubble_gtk.h b/chrome/browser/gtk/bookmark_bubble_gtk.h
index 2011d63..a071ca57 100644
--- a/chrome/browser/gtk/bookmark_bubble_gtk.h
+++ b/chrome/browser/gtk/bookmark_bubble_gtk.h
@@ -81,6 +81,14 @@ class BookmarkBubbleGtk : public InfoBubbleGtkDelegate,
}
void HandleFolderChanged();
+ static void HandleFolderPopupShownThunk(GObject* object,
+ GParamSpec* property,
+ gpointer user_data) {
+ return reinterpret_cast<BookmarkBubbleGtk*>(user_data)->
+ HandleFolderPopupShown();
+ }
+ void HandleFolderPopupShown();
+
static void HandleEditButtonThunk(GtkWidget* widget,
gpointer user_data) {
reinterpret_cast<BookmarkBubbleGtk*>(user_data)->
diff --git a/chrome/browser/gtk/info_bubble_gtk.cc b/chrome/browser/gtk/info_bubble_gtk.cc
index 47733e7..3875d80 100644
--- a/chrome/browser/gtk/info_bubble_gtk.cc
+++ b/chrome/browser/gtk/info_bubble_gtk.cc
@@ -239,29 +239,7 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window,
GTK_WINDOW(window_));
gtk_grab_add(window_);
- // Do X pointer and keyboard grabs to make sure that we have the focus and get
- // all mouse and keyboard events until we're closed.
- GdkGrabStatus pointer_grab_status =
- gdk_pointer_grab(window_->window,
- TRUE, // owner_events
- GDK_BUTTON_PRESS_MASK, // event_mask
- NULL, // confine_to
- NULL, // cursor
- GDK_CURRENT_TIME);
- if (pointer_grab_status != GDK_GRAB_SUCCESS) {
- // This will fail if someone else already has the pointer grabbed, but
- // there's not really anything we can do about that.
- DLOG(ERROR) << "Unable to grab pointer for info bubble (status="
- << pointer_grab_status << ")";
- }
- GdkGrabStatus keyboard_grab_status =
- gdk_keyboard_grab(window_->window,
- FALSE, // owner_events
- GDK_CURRENT_TIME);
- if (keyboard_grab_status != GDK_GRAB_SUCCESS) {
- DLOG(ERROR) << "Unable to grab keyboard for info bubble (status="
- << keyboard_grab_status << ")";
- }
+ GrabPointerAndKeyboard();
registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
NotificationService::AllSources());
@@ -290,7 +268,11 @@ void InfoBubbleGtk::Observe(NotificationType type,
}
}
-void InfoBubbleGtk::Close(bool closed_by_escape) {
+void InfoBubbleGtk::HandlePointerAndKeyboardUngrabbedByContent() {
+ GrabPointerAndKeyboard();
+}
+
+void InfoBubbleGtk::CloseInternal(bool closed_by_escape) {
// Notify the delegate that we're about to close. This gives the chance
// to save state / etc from the hosted widget before it's destroyed.
if (delegate_)
@@ -304,8 +286,34 @@ void InfoBubbleGtk::Close(bool closed_by_escape) {
// |this| has been deleted, see HandleDestroy.
}
+void InfoBubbleGtk::GrabPointerAndKeyboard() {
+ // Install X pointer and keyboard grabs to make sure that we have the focus
+ // and get all mouse and keyboard events until we're closed.
+ GdkGrabStatus pointer_grab_status =
+ gdk_pointer_grab(window_->window,
+ TRUE, // owner_events
+ GDK_BUTTON_PRESS_MASK, // event_mask
+ NULL, // confine_to
+ NULL, // cursor
+ GDK_CURRENT_TIME);
+ if (pointer_grab_status != GDK_GRAB_SUCCESS) {
+ // This will fail if someone else already has the pointer grabbed, but
+ // there's not really anything we can do about that.
+ DLOG(ERROR) << "Unable to grab pointer (status="
+ << pointer_grab_status << ")";
+ }
+ GdkGrabStatus keyboard_grab_status =
+ gdk_keyboard_grab(window_->window,
+ FALSE, // owner_events
+ GDK_CURRENT_TIME);
+ if (keyboard_grab_status != GDK_GRAB_SUCCESS) {
+ DLOG(ERROR) << "Unable to grab keyboard (status="
+ << keyboard_grab_status << ")";
+ }
+}
+
gboolean InfoBubbleGtk::HandleEscape() {
- Close(true); // Close by escape.
+ CloseInternal(true); // Close by escape.
return TRUE;
}
diff --git a/chrome/browser/gtk/info_bubble_gtk.h b/chrome/browser/gtk/info_bubble_gtk.h
index b5021df..738e0f1 100644
--- a/chrome/browser/gtk/info_bubble_gtk.h
+++ b/chrome/browser/gtk/info_bubble_gtk.h
@@ -54,13 +54,23 @@ class InfoBubbleGtk : public NotificationObserver {
// Close the bubble if it's open. This will delete the widgets and object,
// so you shouldn't hold a InfoBubbleGtk pointer after calling Close().
- void Close() { Close(false); }
+ void Close() { CloseInternal(false); }
// NotificationObserver implementation.
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
+ // If the content contains widgets that can steal our pointer and keyboard
+ // grabs (e.g. GtkComboBox), this method should be called after a widget
+ // releases the grabs so we can reacquire them. Note that this causes a race
+ // condition; another client could grab them before we do (ideally, GDK would
+ // transfer the grabs back to us when the widget releases them). The window
+ // is small, though, and the worst-case scenario for this seems to just be
+ // that the content's widgets will appear inactive even after the user clicks
+ // in them.
+ void HandlePointerAndKeyboardUngrabbedByContent();
+
private:
explicit InfoBubbleGtk(GtkThemeProvider* provider);
virtual ~InfoBubbleGtk();
@@ -78,7 +88,11 @@ class InfoBubbleGtk : public NotificationObserver {
// Closes the window and notifies the delegate. |closed_by_escape| is true if
// the close is the result of pressing escape.
- void Close(bool closed_by_escape);
+ void CloseInternal(bool closed_by_escape);
+
+ // Grab (in the X sense) the pointer and keyboard. This is needed to make
+ // sure that we have the input focus.
+ void GrabPointerAndKeyboard();
static gboolean HandleEscapeThunk(GtkAccelGroup* group,
GObject* acceleratable,