// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "gfx/rect.h" #include "testing/gtest/include/gtest/gtest.h" #include "views/focus/accelerator_handler.h" #include "views/view.h" #include "views/window/window_gtk.h" #include "views/window/window_delegate.h" namespace views { class AcceleratorHandlerGtkTest : public testing::Test, public WindowDelegate, public AcceleratorTarget { public: AcceleratorHandlerGtkTest() : kMenuAccelerator(base::VKEY_MENU, false, false, false), kHomepageAccelerator(base::VKEY_HOME, false, false, true), content_view_(NULL) { } virtual void SetUp() { window_ = Window::CreateChromeWindow( NULL, gfx::Rect(0, 0, 500, 500), this); window_->Show(); FocusManager* focus_manager = static_cast(window_)-> GetFocusManager(); focus_manager->RegisterAccelerator(kMenuAccelerator, this); focus_manager->RegisterAccelerator(kHomepageAccelerator, this); menu_pressed_ = false; home_pressed_ = false; } virtual void TearDown() { window_->Close(); // Flush the message loop to make Purify happy. message_loop_.RunAllPending(); } GdkEventKey CreateKeyEvent(GdkEventType type, guint keyval, guint state) { GdkEventKey evt; memset(&evt, 0, sizeof(evt)); evt.type = type; evt.keyval = keyval; // The keyval won't be a "correct" hardware keycode for any real hardware, // but the code should never depend on exact hardware keycodes, just the // fact that the code for presses and releases of the same key match. evt.hardware_keycode = keyval; evt.state = state; GtkWidget* widget = GTK_WIDGET(window_->GetNativeWindow()); evt.window = widget->window; return evt; } // AcceleratorTarget implementation. virtual bool AcceleratorPressed(const Accelerator& accelerator) { if (accelerator == kMenuAccelerator) menu_pressed_ = true; else if (accelerator == kHomepageAccelerator) home_pressed_ = true; return true; } // WindowDelegate Implementation. virtual View* GetContentsView() { if (!content_view_) content_view_ = new View(); return content_view_; } virtual void InitContentView() { } protected: bool menu_pressed_; bool home_pressed_; private: Accelerator kMenuAccelerator; Accelerator kHomepageAccelerator; Window* window_; View* content_view_; MessageLoopForUI message_loop_; DISALLOW_COPY_AND_ASSIGN(AcceleratorHandlerGtkTest); }; // Test that the homepage accelerator (Alt+Home) is activated on key down // and that the menu accelerator (Alt) is never activated. TEST_F(AcceleratorHandlerGtkTest, TestHomepageAccelerator) { AcceleratorHandler handler; GdkEventKey evt; ASSERT_FALSE(menu_pressed_); ASSERT_FALSE(home_pressed_); evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_FALSE(menu_pressed_); ASSERT_FALSE(home_pressed_); evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Home, GDK_MOD1_MASK); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_FALSE(menu_pressed_); ASSERT_TRUE(home_pressed_); evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Home, GDK_MOD1_MASK); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_FALSE(menu_pressed_); ASSERT_TRUE(home_pressed_); } // Test that the menu accelerator is activated on key up and not key down. TEST_F(AcceleratorHandlerGtkTest, TestMenuAccelerator) { AcceleratorHandler handler; GdkEventKey evt; ASSERT_FALSE(menu_pressed_); evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Menu, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_FALSE(menu_pressed_); evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Menu, 0); EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_TRUE(menu_pressed_); } // Test that the menu accelerator isn't confused by the interaction of the // Alt and Shift keys. Try the following sequence on Linux: // Press Alt // Press Shift // Release Alt // Release Shift // The key codes for pressing Alt and releasing Alt are different! This // caused a bug in a previous version of the code, which is now fixed by // keeping track of hardware keycodes, which are consistent. TEST_F(AcceleratorHandlerGtkTest, TestAltShiftInteraction) { AcceleratorHandler handler; GdkEventKey evt; ASSERT_FALSE(menu_pressed_); // Press Shift. evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Shift_L, 0); evt.hardware_keycode = 0x32; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); // Press Alt - but GDK calls this Meta when Shift is also down. evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Meta_L, 0); evt.hardware_keycode = 0x40; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); // Release Shift. evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Shift_L, 0); evt.hardware_keycode = 0x32; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); // Release Alt - with Shift not down, the keyval is now Alt, but // the hardware keycode is unchanged. evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0); evt.hardware_keycode = 0x40; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_FALSE(menu_pressed_); // Press Alt by itself. evt = CreateKeyEvent(GDK_KEY_PRESS, GDK_Alt_L, 0); evt.hardware_keycode = 0x40; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); // This line fails if we don't keep track of hardware keycodes. ASSERT_FALSE(menu_pressed_); // Release Alt - now this should trigger the menu shortcut. evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0); evt.hardware_keycode = 0x40; EXPECT_TRUE(handler.Dispatch(reinterpret_cast(&evt))); ASSERT_TRUE(menu_pressed_); } } // namespace views