blob: bedd6d5add367de57519bf87059e5b93604d3e9f [file] [log] [blame]
// 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 <gdk/gdkkeysyms.h>
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/rect.h"
#include "views/focus/accelerator_handler.h"
#include "views/focus/focus_manager.h"
#include "views/view.h"
#include "views/widget/widget.h"
#include "views/widget/widget_delegate.h"
namespace views {
class AcceleratorHandlerGtkTest
: public testing::Test,
public WidgetDelegate,
public AcceleratorTarget {
public:
AcceleratorHandlerGtkTest()
: kMenuAccelerator(ui::VKEY_MENU, false, false, false),
kHomepageAccelerator(ui::VKEY_HOME, false, false, true),
content_view_(NULL) {
}
virtual void SetUp() {
window_ = Widget::CreateWindowWithBounds(this, gfx::Rect(0, 0, 500, 500));
window_->Show();
FocusManager* focus_manager = 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 application verifiers 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;
}
// WidgetDelegate Implementation.
virtual View* GetContentsView() {
if (!content_view_)
content_view_ = new View();
return content_view_;
}
virtual const views::Widget* GetWidget() const {
return content_view_->GetWidget();
}
virtual views::Widget* GetWidget() {
return content_view_->GetWidget();
}
virtual void InitContentView() {
}
protected:
bool menu_pressed_;
bool home_pressed_;
private:
Accelerator kMenuAccelerator;
Accelerator kHomepageAccelerator;
Widget* 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_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&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<GdkEvent*>(&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<GdkEvent*>(&evt)));
evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&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_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&evt)));
ASSERT_FALSE(menu_pressed_);
evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Alt_L, 0);
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&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<GdkEvent*>(&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<GdkEvent*>(&evt)));
// Release Shift.
evt = CreateKeyEvent(GDK_KEY_RELEASE, GDK_Shift_L, 0);
evt.hardware_keycode = 0x32;
EXPECT_TRUE(handler.Dispatch(reinterpret_cast<GdkEvent*>(&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<GdkEvent*>(&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<GdkEvent*>(&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<GdkEvent*>(&evt)));
ASSERT_TRUE(menu_pressed_);
}
} // namespace views