diff options
-rw-r--r-- | base/keyboard_code_conversion_gtk.cc | 4 | ||||
-rw-r--r-- | chrome/browser/browser_keyevents_browsertest.cc | 62 | ||||
-rw-r--r-- | chrome/browser/renderer_host/gtk_im_context_wrapper.cc | 11 | ||||
-rw-r--r-- | chrome/browser/views/frame/browser_view.cc | 7 | ||||
-rw-r--r-- | views/focus/accelerator_handler.h | 11 | ||||
-rw-r--r-- | views/focus/accelerator_handler_gtk.cc | 107 | ||||
-rw-r--r-- | views/focus/accelerator_handler_gtk_unittest.cc | 8 | ||||
-rw-r--r-- | views/focus/focus_manager.cc | 4 | ||||
-rw-r--r-- | views/focus/focus_manager_unittest.cc | 9 | ||||
-rw-r--r-- | views/widget/gtk_views_window.cc | 13 | ||||
-rw-r--r-- | views/widget/widget_gtk.cc | 84 | ||||
-rw-r--r-- | views/widget/widget_gtk.h | 10 |
12 files changed, 176 insertions, 154 deletions
diff --git a/base/keyboard_code_conversion_gtk.cc b/base/keyboard_code_conversion_gtk.cc index 9e6bfd1..83f4197 100644 --- a/base/keyboard_code_conversion_gtk.cc +++ b/base/keyboard_code_conversion_gtk.cc @@ -93,6 +93,7 @@ base::KeyboardCode WindowsKeyCodeForGdkKeyCode(int keycode) { case GDK_Control_R: return VKEY_CONTROL; // (11) CTRL key case GDK_Menu: + return VKEY_APPS; // (5D) Applications key (Natural keyboard) case GDK_Alt_L: case GDK_Alt_R: return VKEY_MENU; // (12) ALT key @@ -265,7 +266,6 @@ base::KeyboardCode WindowsKeyCodeForGdkKeyCode(int keycode) { case GDK_Meta_R: case GDK_Super_R: return VKEY_RWIN; // (5C) Right Windows key (Natural keyboard) - // VKEY_APPS (5D) Applications key (Natural keyboard) // VKEY_SLEEP (5F) Computer Sleep key // VKEY_SEPARATOR (6C) Separator key // VKEY_SUBTRACT (6D) Subtract key @@ -456,6 +456,8 @@ int GdkKeyCodeForWindowsKeyCode(base::KeyboardCode keycode, bool shift) { case VKEY_CONTROL: return GDK_Control_L; case VKEY_MENU: + return GDK_Alt_L; + case VKEY_APPS: return GDK_Menu; case VKEY_PAUSE: diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc index e943bef..0c4bd37db 100644 --- a/chrome/browser/browser_keyevents_browsertest.cc +++ b/chrome/browser/browser_keyevents_browsertest.cc @@ -532,14 +532,7 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, CommandKeyEvents) { } #endif -#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) -// See http://crbug.com/40037 for details. -#define MAYBE_AccessKeys DISABLED_AccessKeys -#else -#define MAYBE_AccessKeys AccessKeys -#endif - -IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_AccessKeys) { +IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, AccessKeys) { #if defined(OS_MACOSX) // On Mac, access keys use ctrl+alt modifiers. static const KeyEventTestData kTestAccessA = { @@ -891,4 +884,57 @@ IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, PageUpDownKeys) { EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"A", L"")); } +#if defined(OS_WIN) || defined(TOOLKIT_VIEWS) +IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, FocusMenuBarByAltKey) { + static const KeyEventTestData kTestAltKey = { + base::VKEY_MENU, false, false, false, false, + false, false, false, false, 2, + { "D 18 0 false false true false", + "U 18 0 false false true false" } + }; + + static const KeyEventTestData kTestAltKeySuppress = { + base::VKEY_MENU, false, false, false, false, + true, false, false, false, 2, + { "D 18 0 false false true false", + "U 18 0 false false true false" } + }; + + static const KeyEventTestData kTestCtrlAltKey = { + base::VKEY_MENU, true, false, false, false, + false, false, false, false, 4, + { "D 17 0 true false false false", + "D 18 0 true false true false", + "U 18 0 true false true false", + "U 17 0 true false false false" } + }; + + net::HTTPTestServer* server = StartHTTPServer(); + ASSERT_TRUE(server); + + BringBrowserWindowToFront(); + GURL url = server->TestServerPage(kTestingPage); + ui_test_utils::NavigateToURL(browser(), url); + + ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER)); + ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); + + int tab_index = browser()->selected_index(); + // Press and release Alt key to focus wrench menu button. + EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKey)); + EXPECT_TRUE(IsViewFocused(VIEW_ID_APP_MENU)); + + ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER)); + ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); + + // Alt key can be suppressed. + EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKeySuppress)); + ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); + + // Ctrl+Alt should have no effect. + EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlAltKey)); + ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW)); +} +#endif + } // namespace diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc index 8aa7f8c..4ac138b 100644 --- a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc +++ b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc @@ -132,6 +132,13 @@ void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { NativeWebKeyboardEvent wke(event); + // If the key event was handled by the input method, then we need to prevent + // RenderView::UnhandledKeyboardEvent() from processing it. + // Otherwise unexpected result may occur. For example if it's a + // Backspace key event, the browser may go back to previous page. + if (filtered) + wke.skip_in_browser = true; + // Send filtered keydown event before sending IME result. if (event->type == GDK_KEY_PRESS && filtered) ProcessFilteredKeyPressEvent(&wke); @@ -321,10 +328,6 @@ void GtkIMContextWrapper::ProcessFilteredKeyPressEvent( // keyidentifier must be updated accordingly, otherwise this key event may // still be processed by webkit. wke->setKeyIdentifierFromWindowsKeyCode(); - // Prevent RenderView::UnhandledKeyboardEvent() from processing it. - // Otherwise unexpected result may occur. For example if it's a - // Backspace key event, the browser may go back to previous page. - wke->skip_in_browser = true; } host_view_->ForwardKeyboardEvent(*wke); } diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index 0e11eaa..d8ab622 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -73,6 +73,7 @@ #elif defined(OS_LINUX) #include "chrome/browser/views/accelerator_table_gtk.h" #include "views/window/hit_test.h" +#include "views/window/window_gtk.h" #endif using base::TimeDelta; @@ -1238,8 +1239,14 @@ bool BrowserView::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, } void BrowserView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { +#if defined(OS_LINUX) + views::Window* window = GetWidget()->GetWindow(); + if (window && event.os_event && !event.skip_in_browser) + static_cast<views::WindowGtk*>(window)->HandleKeyboardEvent(event.os_event); +#else unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, GetFocusManager()); +#endif } // TODO(devint): http://b/issue?id=1117225 Cut, Copy, and Paste are always diff --git a/views/focus/accelerator_handler.h b/views/focus/accelerator_handler.h index 6140d364..3726d96 100644 --- a/views/focus/accelerator_handler.h +++ b/views/focus/accelerator_handler.h @@ -35,17 +35,6 @@ class AcceleratorHandler : public MessageLoopForUI::Dispatcher { #if defined(OS_WIN) // The keys currently pressed and consumed by the FocusManager. std::set<WPARAM> pressed_keys_; -#else // OS_LINUX - // The set of hardware keycodes that are currently pressed. We use this - // to keep track of whether or not a key is being pressed in isolation - // or not. We must use hardware keycodes because higher-level keycodes - // may change between press and release depending on what modifier keys - // are pressed in the interim. - std::set<int> pressed_hardware_keys_; - // The set of vkey codes that were consumed by the FocusManager. - std::set<int> consumed_vkeys_; - // True if only the menu key (Alt key) was pressed, and no other keys. - bool only_menu_pressed_; #endif DISALLOW_COPY_AND_ASSIGN(AcceleratorHandler); diff --git a/views/focus/accelerator_handler_gtk.cc b/views/focus/accelerator_handler_gtk.cc index ee91906..e0af89b 100644 --- a/views/focus/accelerator_handler_gtk.cc +++ b/views/focus/accelerator_handler_gtk.cc @@ -13,112 +13,11 @@ namespace views { -AcceleratorHandler::AcceleratorHandler() : only_menu_pressed_(false) {} +AcceleratorHandler::AcceleratorHandler() {} bool AcceleratorHandler::Dispatch(GdkEvent* event) { - // When focus changes, we may not see a full key-press / key-release - // pair, so clear the set of keys we think were pressed. - if (event->type == GDK_FOCUS_CHANGE) { - pressed_hardware_keys_.clear(); - consumed_vkeys_.clear(); - only_menu_pressed_ = false; - } - - // Skip accelerator handling for non key events. - bool skip = event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE; - - // Skip if there is a grabbed widget and it is not a window. This is because - // of the following two reasons: - // 1. Widget such as pop up from GtkComboBox is a grabbed widget and use ESC - // as its accelerator key to dismiss itself. We will break Gtk's code if - // we eat this key. See http://crosbug.com/2355 - // 2. Modal dialogs in Gtk are grabbed windows and we want to have our - // accelerator key processing in this case. See http://crogbug.com/3701 - if (!skip) { - GtkWidget* grabbed_widget = gtk_grab_get_current(); - skip = grabbed_widget && !GTK_IS_WINDOW(grabbed_widget); - } - - if (skip) { - // Let Gtk processes the event. - gtk_main_do_event(event); - return true; - } - - GdkEventKey* key_event = reinterpret_cast<GdkEventKey*>(event); - // Let's retrieve the focus manager for the GdkWindow. - GdkWindow* window = gdk_window_get_toplevel(key_event->window); - gpointer ptr; - gdk_window_get_user_data(window, &ptr); - if (!ptr && !gdk_window_is_visible(window)) { - // The window is destroyed while we're handling key events. - gtk_main_do_event(event); - return true; - } - - DCHECK(ptr); - - // The top-level window or window widget is expected to always be associated - // with the top-level gtk widget. - WidgetGtk* widget = - WidgetGtk::GetViewForNative(reinterpret_cast<GtkWidget*>(ptr)); - if (!widget) { - // During dnd we get events for windows we don't control (such as the - // window being dragged). - gtk_main_do_event(event); - return true; - } - FocusManager* focus_manager = widget->GetFocusManager(); - if (!focus_manager) { - NOTREACHED(); - return true; - } - - KeyEvent view_key_event(key_event); - int key_code = view_key_event.GetKeyCode(); - int hardware_keycode = key_event->hardware_keycode; - if (event->type == GDK_KEY_PRESS) { - pressed_hardware_keys_.insert(hardware_keycode); - - // If it's the Alt key, don't send it to the focus manager until release - // (to handle focusing the menu bar). - if (key_code == base::VKEY_MENU && pressed_hardware_keys_.size() == 1) { - only_menu_pressed_ = true; - return true; - } - - if (pressed_hardware_keys_.size() != 1) - only_menu_pressed_ = false; - - // FocusManager::OnKeyEvent will return false if this message has been - // consumed and should not be propagated further. - if (!focus_manager->OnKeyEvent(view_key_event)) { - consumed_vkeys_.insert(key_code); - return true; - } - } - - if (event->type == GDK_KEY_RELEASE) { - pressed_hardware_keys_.erase(hardware_keycode); - - // Make sure to filter out the key release for a key press consumed - // as an accelerator, to avoid calling gtk_main_do_event with an - // unpaired key release. - if (consumed_vkeys_.find(key_code) != consumed_vkeys_.end()) { - consumed_vkeys_.erase(key_code); - return true; - } - - // Special case: the Alt key can trigger an accelerator on release - // rather than on press, but only if no other keys were pressed. - if (key_code == base::VKEY_MENU && only_menu_pressed_) { - Accelerator accelerator(base::VKEY_MENU, false, false, false); - focus_manager->ProcessAccelerator(accelerator); - return true; - } - } - - // Pass the event to gtk if we didn't consume it above. + // The logic for handling keyboard accelerators has been moved into + // WidgetGtk::OnKeyEvent handler (views/widget/widget_gtk.cc). gtk_main_do_event(event); return true; } diff --git a/views/focus/accelerator_handler_gtk_unittest.cc b/views/focus/accelerator_handler_gtk_unittest.cc index ef05312..78e9110 100644 --- a/views/focus/accelerator_handler_gtk_unittest.cc +++ b/views/focus/accelerator_handler_gtk_unittest.cc @@ -99,7 +99,7 @@ TEST_F(AcceleratorHandlerGtkTest, TestHomepageAccelerator) { ASSERT_FALSE(menu_pressed_); ASSERT_FALSE(home_pressed_); - evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0); + evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Alt_L, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt))); ASSERT_FALSE(menu_pressed_); @@ -113,7 +113,7 @@ TEST_F(AcceleratorHandlerGtkTest, TestHomepageAccelerator) { evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Home, GDK_MOD1_MASK); EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt))); - evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0); + evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt))); ASSERT_FALSE(menu_pressed_); @@ -127,12 +127,12 @@ TEST_F(AcceleratorHandlerGtkTest, TestMenuAccelerator) { ASSERT_FALSE(menu_pressed_); - evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0); + evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Alt_L, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt))); ASSERT_FALSE(menu_pressed_); - evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0); + evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt))); ASSERT_TRUE(menu_pressed_); diff --git a/views/focus/focus_manager.cc b/views/focus/focus_manager.cc index 5a94c95..f0f5f99 100644 --- a/views/focus/focus_manager.cc +++ b/views/focus/focus_manager.cc @@ -81,9 +81,13 @@ FocusManager::~FocusManager() { } bool FocusManager::OnKeyEvent(const KeyEvent& event) { +#if defined(OS_WIN) // If the focused view wants to process the key event as is, let it be. + // On Linux we always dispatch key events to the focused view first, so + // we should not do this check here. See also WidgetGtk::OnKeyEvent(). if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event)) return true; +#endif // Intercept Tab related messages for focus traversal. // Note that we don't do focus traversal if the root window is not part of the diff --git a/views/focus/focus_manager_unittest.cc b/views/focus/focus_manager_unittest.cc index 955a83a..f786b1d 100644 --- a/views/focus/focus_manager_unittest.cc +++ b/views/focus/focus_manager_unittest.cc @@ -1485,6 +1485,10 @@ class MessageTrackingView : public View { DISALLOW_COPY_AND_ASSIGN(MessageTrackingView); }; +#if defined(OS_WIN) +// This test is now Windows only. Linux Views port does not handle accelerator +// keys in AcceleratorHandler anymore. The logic has been moved into +// WidgetGtk::OnKeyEvent(). // Tests that the keyup messages are eaten for accelerators. TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) { FocusManager* focus_manager = GetFocusManager(); @@ -1553,15 +1557,12 @@ TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) { PostKeyUp(base::VKEY_0); MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); MessageLoopForUI::current()->Run(&accelerator_handler); -#if defined(OS_WIN) - // Linux eats only last accelerator's release event. - // See http://crbug.com/23383 for details. EXPECT_TRUE(mtv->keys_pressed().empty()); EXPECT_TRUE(mtv->keys_released().empty()); -#endif EXPECT_TRUE(mtv->accelerator_pressed()); mtv->Reset(); } +#endif #if defined(OS_WIN) // Test that the focus manager is created successfully for the first view diff --git a/views/widget/gtk_views_window.cc b/views/widget/gtk_views_window.cc index fb3657d..2eff8f1 100644 --- a/views/widget/gtk_views_window.cc +++ b/views/widget/gtk_views_window.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <gtk/gtk.h> +#include "views/event.h" #include "views/widget/gtk_views_window.h" #include "views/focus/focus_manager.h" @@ -9,15 +11,16 @@ G_BEGIN_DECLS G_DEFINE_TYPE(GtkViewsWindow, gtk_views_window, GTK_TYPE_WINDOW) -static void gtk_views_window_move_focus(GtkWindow *window, +static void gtk_views_window_move_focus(GtkWindow* window, GtkDirectionType dir) { views::FocusManager* focus_manager = views::FocusManager::GetFocusManagerForNativeWindow(window); if (focus_manager) { - // We only support tab traversing by tab keys, so just ignore all other - // cases silently. - if (dir == GTK_DIR_TAB_BACKWARD || dir == GTK_DIR_TAB_FORWARD) - focus_manager->AdvanceFocus(dir == GTK_DIR_TAB_BACKWARD); + GdkEvent* key = gtk_get_current_event(); + if (key && key->type == GDK_KEY_PRESS) { + views::KeyEvent key_event(reinterpret_cast<GdkEventKey*>(key)); + focus_manager->OnKeyEvent(key_event); + } } else if (GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus) { GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus(window, dir); } diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc index de4ad9a..93fdcdc 100644 --- a/views/widget/widget_gtk.cc +++ b/views/widget/widget_gtk.cc @@ -246,7 +246,8 @@ WidgetGtk::WidgetGtk(Type type) has_focus_(false), delegate_(NULL), always_on_top_(false), - is_double_buffered_(false) { + is_double_buffered_(false), + should_handle_menu_key_release_(false) { static bool installed_message_loop_observer = false; if (!installed_message_loop_observer) { installed_message_loop_observer = true; @@ -546,9 +547,9 @@ void WidgetGtk::Init(GtkWidget* parent, // See views::Views::Focus and views::FocusManager::ClearNativeFocus // for more details. g_signal_connect(widget_, "key_press_event", - G_CALLBACK(&OnKeyPressThunk), this); + G_CALLBACK(&OnKeyEventThunk), this); g_signal_connect(widget_, "key_release_event", - G_CALLBACK(&OnKeyReleaseThunk), this); + G_CALLBACK(&OnKeyEventThunk), this); // Drag and drop. gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0), @@ -861,6 +862,38 @@ void WidgetGtk::ClearNativeFocus() { gtk_window_set_focus(GTK_WINDOW(GetNativeView()), NULL); } +bool WidgetGtk::HandleKeyboardEvent(GdkEventKey* event) { + if (!focus_manager_) + return false; + + KeyEvent key(event); + int key_code = key.GetKeyCode(); + bool handled = false; + + // Always reset |should_handle_menu_key_release_| unless we are handling a + // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only + // be activated when handling a VKEY_MENU key release event which is preceded + // by an unhandled VKEY_MENU key press event. + if (key_code != base::VKEY_MENU || event->type != GDK_KEY_RELEASE) + should_handle_menu_key_release_ = false; + + if (event->type == GDK_KEY_PRESS) { + // VKEY_MENU is triggered by key release event. + if (key_code != base::VKEY_MENU) + handled = focus_manager_->OnKeyEvent(key); + else + should_handle_menu_key_release_ = true; + } else if (key_code == base::VKEY_MENU && should_handle_menu_key_release_ && + (key.GetFlags() & ~Event::EF_ALT_DOWN) == 0) { + // Trigger VKEY_MENU when only this key is pressed and released, and both + // press and release events are not handled by others. + Accelerator accelerator(base::VKEY_MENU, false, false, false); + handled = focus_manager_->ProcessAccelerator(accelerator); + } + + return handled; +} + //////////////////////////////////////////////////////////////////////////////// // WidgetGtk, protected: @@ -1088,6 +1121,8 @@ gboolean WidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) { return false; // This is the second focus-in event in a row, ignore it. has_focus_ = true; + should_handle_menu_key_release_ = false; + if (type_ == TYPE_CHILD) return false; @@ -1114,14 +1149,41 @@ gboolean WidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) { return false; } -gboolean WidgetGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) { - KeyEvent key_event(event); - return root_view_->ProcessKeyEvent(key_event); -} - -gboolean WidgetGtk::OnKeyRelease(GtkWidget* widget, GdkEventKey* event) { - KeyEvent key_event(event); - return root_view_->ProcessKeyEvent(key_event); +gboolean WidgetGtk::OnKeyEvent(GtkWidget* widget, GdkEventKey* event) { + KeyEvent key(event); + + // Always reset |should_handle_menu_key_release_| unless we are handling a + // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only + // be activated when handling a VKEY_MENU key release event which is preceded + // by an unhandled VKEY_MENU key press event. See also HandleKeyboardEvent(). + if (key.GetKeyCode() != base::VKEY_MENU || event->type != GDK_KEY_RELEASE) + should_handle_menu_key_release_ = false; + + bool handled = false; + + // Dispatch the key event to View hierarchy first. + handled = root_view_->ProcessKeyEvent(key); + + // Dispatch the key event to native GtkWidget hierarchy. + // To prevent GtkWindow from handling the key event as a keybinding, we need + // to bypass GtkWindow's default key event handler and dispatch the event + // here. + if (!handled && GTK_IS_WINDOW(widget)) + handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), event); + + // On Linux, in order to handle VKEY_MENU (Alt) accelerator key correctly and + // avoid issues like: http://crbug.com/40966 and http://crbug.com/49701, we + // should only send the key event to the focus manager if it's not handled by + // any View or native GtkWidget. + // The flow is different when the focus is in a RenderWidgetHostViewGtk, which + // always consumes the key event and send it back to us later by calling + // HandleKeyboardEvent() directly, if it's not handled by webkit. + if (!handled) + handled = HandleKeyboardEvent(event); + + // Always return true for toplevel window to prevents GtkWindow's default key + // event handler. + return GTK_IS_WINDOW(widget) ? true : handled; } gboolean WidgetGtk::OnQueryTooltip(GtkWidget* widget, diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h index d010b03..7e1bdd1 100644 --- a/views/widget/widget_gtk.h +++ b/views/widget/widget_gtk.h @@ -198,6 +198,10 @@ class WidgetGtk // Clears the focus on the native widget having the focus. virtual void ClearNativeFocus(); + // Handles a keyboard event by sending it to our focus manager. + // Returns true if it's handled by the focus manager. + bool HandleKeyboardEvent(GdkEventKey* event); + protected: // If widget containes another widget, translates event coordinates to the // contained widget's coordinates, else returns original event coordinates. @@ -247,8 +251,7 @@ class WidgetGtk CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnButtonRelease, GdkEventButton*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusIn, GdkEventFocus*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnFocusOut, GdkEventFocus*); - CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyPress, GdkEventKey*); - CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyRelease, GdkEventKey*); + CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnKeyEvent, GdkEventKey*); CHROMEGTK_CALLBACK_4(WidgetGtk, gboolean, OnQueryTooltip, gint, gint, gboolean, GtkTooltip*); CHROMEGTK_CALLBACK_1(WidgetGtk, gboolean, OnScroll, GdkEventScroll*); @@ -436,6 +439,9 @@ class WidgetGtk // This is false by default. bool is_double_buffered_; + // Indicates if we should handle the upcoming Alt key release event. + bool should_handle_menu_key_release_; + DISALLOW_COPY_AND_ASSIGN(WidgetGtk); }; |