OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ui/views/focus/focus_manager.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "ui/views/focus/accelerator_handler.h" |
| 9 #include "ui/views/focus/focus_manager_test.h" |
| 10 #include "ui/views/widget/widget.h" |
| 11 #include "views/controls/button/text_button.h" |
| 12 |
| 13 namespace views { |
| 14 |
| 15 namespace { |
| 16 |
| 17 class MessageTrackingView : public View { |
| 18 public: |
| 19 MessageTrackingView() : accelerator_pressed_(false) { |
| 20 } |
| 21 |
| 22 void Reset() { |
| 23 accelerator_pressed_ = false; |
| 24 keys_pressed_.clear(); |
| 25 keys_released_.clear(); |
| 26 } |
| 27 |
| 28 const std::vector<ui::KeyboardCode>& keys_pressed() const { |
| 29 return keys_pressed_; |
| 30 } |
| 31 |
| 32 const std::vector<ui::KeyboardCode>& keys_released() const { |
| 33 return keys_released_; |
| 34 } |
| 35 |
| 36 bool accelerator_pressed() const { |
| 37 return accelerator_pressed_; |
| 38 } |
| 39 |
| 40 // Overridden from View: |
| 41 virtual bool OnKeyPressed(const KeyEvent& e) OVERRIDE { |
| 42 keys_pressed_.push_back(e.key_code()); |
| 43 return true; |
| 44 } |
| 45 virtual bool OnKeyReleased(const KeyEvent& e) OVERRIDE { |
| 46 keys_released_.push_back(e.key_code()); |
| 47 return true; |
| 48 } |
| 49 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE { |
| 50 accelerator_pressed_ = true; |
| 51 return true; |
| 52 } |
| 53 |
| 54 private: |
| 55 bool accelerator_pressed_; |
| 56 std::vector<ui::KeyboardCode> keys_pressed_; |
| 57 std::vector<ui::KeyboardCode> keys_released_; |
| 58 |
| 59 DISALLOW_COPY_AND_ASSIGN(MessageTrackingView); |
| 60 }; |
| 61 |
| 62 } // namespace |
| 63 |
| 64 // Test that when activating/deactivating the top window, the focus is stored/ |
| 65 // restored properly. |
| 66 TEST_F(FocusManagerTest, FocusStoreRestore) { |
| 67 // Simulate an activate, otherwise the deactivate isn't going to do anything. |
| 68 SimulateActivateWindow(); |
| 69 |
| 70 NativeTextButton* button = new NativeTextButton(NULL, |
| 71 ASCIIToUTF16("Press me")); |
| 72 View* view = new View(); |
| 73 view->set_focusable(true); |
| 74 |
| 75 GetContentsView()->AddChildView(button); |
| 76 button->SetBounds(10, 10, 200, 30); |
| 77 GetContentsView()->AddChildView(view); |
| 78 RunPendingMessages(); |
| 79 |
| 80 TestFocusChangeListener listener; |
| 81 AddFocusChangeListener(&listener); |
| 82 |
| 83 view->RequestFocus(); |
| 84 RunPendingMessages(); |
| 85 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler()); |
| 86 |
| 87 // Visual Studio 2010 has problems converting NULL to the null pointer for |
| 88 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/
520043/error-converting-from-null-to-a-pointer-type-in-std-pair |
| 89 // It will work if we pass nullptr. |
| 90 #if defined(_MSC_VER) && _MSC_VER >= 1600 |
| 91 views::View* null_view = nullptr; |
| 92 #else |
| 93 views::View* null_view = NULL; |
| 94 #endif |
| 95 |
| 96 // Deacivate the window, it should store its focus. |
| 97 SimulateDeactivateWindow(); |
| 98 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); |
| 99 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); |
| 100 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); |
| 101 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(view, null_view)); |
| 102 listener.ClearFocusChanges(); |
| 103 |
| 104 // Reactivate, focus should come-back to the previously focused view. |
| 105 SimulateActivateWindow(); |
| 106 EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); |
| 107 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); |
| 108 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); |
| 109 listener.ClearFocusChanges(); |
| 110 |
| 111 // Same test with a NativeControl. |
| 112 button->RequestFocus(); |
| 113 SimulateDeactivateWindow(); |
| 114 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); |
| 115 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); |
| 116 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view, button)); |
| 117 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(button, null_view)); |
| 118 listener.ClearFocusChanges(); |
| 119 |
| 120 SimulateActivateWindow(); |
| 121 EXPECT_EQ(button, GetFocusManager()->GetFocusedView()); |
| 122 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); |
| 123 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, button)); |
| 124 listener.ClearFocusChanges(); |
| 125 |
| 126 /* |
| 127 // Now test that while the window is inactive we can change the focused view |
| 128 // (we do that in several places). |
| 129 SimulateDeactivateWindow(); |
| 130 // TODO: would have to mock the window being inactive (with a TestWidgetWin |
| 131 // that would return false on IsActive()). |
| 132 GetFocusManager()->SetFocusedView(view); |
| 133 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL); |
| 134 |
| 135 EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); |
| 136 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); |
| 137 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(button, null_view)); |
| 138 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(null_view, view)); |
| 139 */ |
| 140 } |
| 141 |
| 142 // Test that the focus manager is created successfully for the first view |
| 143 // window parented to a native dialog. |
| 144 TEST_F(FocusManagerTest, CreationForNativeRoot) { |
| 145 // Create a window class. |
| 146 WNDCLASSEX class_ex; |
| 147 memset(&class_ex, 0, sizeof(class_ex)); |
| 148 class_ex.cbSize = sizeof(WNDCLASSEX); |
| 149 class_ex.lpfnWndProc = &DefWindowProc; |
| 150 class_ex.lpszClassName = L"TestWindow"; |
| 151 ATOM atom = RegisterClassEx(&class_ex); |
| 152 ASSERT_TRUE(atom); |
| 153 |
| 154 // Create a native dialog window. |
| 155 HWND hwnd = CreateWindowEx(0, class_ex.lpszClassName, NULL, |
| 156 WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, |
| 157 NULL, NULL, NULL, NULL); |
| 158 ASSERT_TRUE(hwnd); |
| 159 |
| 160 // Create a view window parented to native dialog. |
| 161 scoped_ptr<Widget> widget1(new Widget); |
| 162 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); |
| 163 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 164 params.parent = hwnd; |
| 165 params.bounds = gfx::Rect(0, 0, 100, 100); |
| 166 params.top_level = true; // This is top level in views hierarchy. |
| 167 widget1->Init(params); |
| 168 |
| 169 // Get the focus manager directly from the first window. Should exist |
| 170 // because the first window is the root widget. |
| 171 views::FocusManager* focus_manager1 = widget1->GetFocusManager(); |
| 172 EXPECT_TRUE(focus_manager1); |
| 173 |
| 174 // Create another view window parented to the first view window. |
| 175 scoped_ptr<Widget> widget2(new Widget); |
| 176 params.parent = widget1->GetNativeView(); |
| 177 params.top_level = false; // This is child widget. |
| 178 widget2->Init(params); |
| 179 |
| 180 // Access the shared focus manager directly from the second window. |
| 181 views::FocusManager* focus_manager2 = widget2->GetFocusManager(); |
| 182 EXPECT_EQ(focus_manager2, focus_manager1); |
| 183 |
| 184 // Access the shared focus manager indirectly from the first window handle. |
| 185 gfx::NativeWindow native_window = widget1->GetNativeWindow(); |
| 186 views::Widget* widget = |
| 187 views::Widget::GetWidgetForNativeWindow(native_window); |
| 188 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); |
| 189 |
| 190 // Access the shared focus manager indirectly from the second window handle. |
| 191 native_window = widget2->GetNativeWindow(); |
| 192 widget = views::Widget::GetWidgetForNativeWindow(native_window); |
| 193 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); |
| 194 |
| 195 // Access the shared focus manager indirectly from the first view handle. |
| 196 gfx::NativeView native_view = widget1->GetNativeView(); |
| 197 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); |
| 198 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); |
| 199 |
| 200 // Access the shared focus manager indirectly from the second view handle. |
| 201 native_view = widget2->GetNativeView(); |
| 202 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); |
| 203 EXPECT_EQ(widget->GetFocusManager(), focus_manager1); |
| 204 |
| 205 DestroyWindow(hwnd); |
| 206 } |
| 207 |
| 208 // Tests that the keyup messages are eaten for accelerators. |
| 209 // Windows-only, Windows is the only platform that handles accelerators in |
| 210 // AcceleratorHandler. NativeWidgetAura/NativeWidgetGtk::OnKeyEvent handles |
| 211 // them in other configurations. |
| 212 TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) { |
| 213 FocusManager* focus_manager = GetFocusManager(); |
| 214 MessageTrackingView* mtv = new MessageTrackingView(); |
| 215 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_0, false, false, false)); |
| 216 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_1, false, false, false)); |
| 217 GetContentsView()->AddChildView(mtv); |
| 218 focus_manager->SetFocusedView(mtv); |
| 219 |
| 220 // First send a non-accelerator key sequence. |
| 221 PostKeyDown(ui::VKEY_9); |
| 222 PostKeyUp(ui::VKEY_9); |
| 223 AcceleratorHandler accelerator_handler; |
| 224 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| 225 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); |
| 226 // Make sure we get a key-up and key-down. |
| 227 ASSERT_EQ(1U, mtv->keys_pressed().size()); |
| 228 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); |
| 229 ASSERT_EQ(1U, mtv->keys_released().size()); |
| 230 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); |
| 231 EXPECT_FALSE(mtv->accelerator_pressed()); |
| 232 mtv->Reset(); |
| 233 |
| 234 // Same thing with repeat and more than one key at once. |
| 235 PostKeyDown(ui::VKEY_9); |
| 236 PostKeyDown(ui::VKEY_9); |
| 237 PostKeyDown(ui::VKEY_8); |
| 238 PostKeyDown(ui::VKEY_9); |
| 239 PostKeyDown(ui::VKEY_7); |
| 240 PostKeyUp(ui::VKEY_9); |
| 241 PostKeyUp(ui::VKEY_7); |
| 242 PostKeyUp(ui::VKEY_8); |
| 243 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| 244 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); |
| 245 // Make sure we get a key-up and key-down. |
| 246 ASSERT_EQ(5U, mtv->keys_pressed().size()); |
| 247 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); |
| 248 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[1]); |
| 249 EXPECT_EQ(ui::VKEY_8, mtv->keys_pressed()[2]); |
| 250 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[3]); |
| 251 EXPECT_EQ(ui::VKEY_7, mtv->keys_pressed()[4]); |
| 252 ASSERT_EQ(3U, mtv->keys_released().size()); |
| 253 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); |
| 254 EXPECT_EQ(ui::VKEY_7, mtv->keys_released()[1]); |
| 255 EXPECT_EQ(ui::VKEY_8, mtv->keys_released()[2]); |
| 256 EXPECT_FALSE(mtv->accelerator_pressed()); |
| 257 mtv->Reset(); |
| 258 |
| 259 // Now send an accelerator key sequence. |
| 260 PostKeyDown(ui::VKEY_0); |
| 261 PostKeyUp(ui::VKEY_0); |
| 262 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| 263 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); |
| 264 EXPECT_TRUE(mtv->keys_pressed().empty()); |
| 265 EXPECT_TRUE(mtv->keys_released().empty()); |
| 266 EXPECT_TRUE(mtv->accelerator_pressed()); |
| 267 mtv->Reset(); |
| 268 |
| 269 // Same thing with repeat and more than one key at once. |
| 270 PostKeyDown(ui::VKEY_0); |
| 271 PostKeyDown(ui::VKEY_1); |
| 272 PostKeyDown(ui::VKEY_1); |
| 273 PostKeyDown(ui::VKEY_0); |
| 274 PostKeyDown(ui::VKEY_0); |
| 275 PostKeyUp(ui::VKEY_1); |
| 276 PostKeyUp(ui::VKEY_0); |
| 277 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| 278 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler); |
| 279 EXPECT_TRUE(mtv->keys_pressed().empty()); |
| 280 EXPECT_TRUE(mtv->keys_released().empty()); |
| 281 EXPECT_TRUE(mtv->accelerator_pressed()); |
| 282 mtv->Reset(); |
| 283 } |
| 284 |
| 285 } // namespace views |
OLD | NEW |