| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/menu/menu_controller.h" | 5 #include "ui/views/controls/menu/menu_controller.h" |
| 6 | 6 |
| 7 #include "base/run_loop.h" | 7 #include "base/run_loop.h" |
| 8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "ui/events/event_handler.h" | 9 #include "ui/events/event_handler.h" |
| 10 #include "ui/events/null_event_targeter.h" | 10 #include "ui/events/null_event_targeter.h" |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 | 39 |
| 40 class TestMenuItemView : public MenuItemView { | 40 class TestMenuItemView : public MenuItemView { |
| 41 public: | 41 public: |
| 42 TestMenuItemView() : MenuItemView(nullptr) {} | 42 TestMenuItemView() : MenuItemView(nullptr) {} |
| 43 ~TestMenuItemView() override {} | 43 ~TestMenuItemView() override {} |
| 44 | 44 |
| 45 private: | 45 private: |
| 46 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); | 46 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); |
| 47 }; | 47 }; |
| 48 | 48 |
| 49 class SubmenuViewShown : public SubmenuView { |
| 50 public: |
| 51 SubmenuViewShown(MenuItemView* parent) : SubmenuView(parent) {} |
| 52 ~SubmenuViewShown() override {} |
| 53 bool IsShowing() override { return true; } |
| 54 |
| 55 private: |
| 56 DISALLOW_COPY_AND_ASSIGN(SubmenuViewShown); |
| 57 }; |
| 58 |
| 59 class TestMenuItemViewShown : public MenuItemView { |
| 60 public: |
| 61 TestMenuItemViewShown() : MenuItemView(nullptr) { |
| 62 submenu_ = new SubmenuViewShown(this); |
| 63 } |
| 64 ~TestMenuItemViewShown() override {} |
| 65 |
| 66 private: |
| 67 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); |
| 68 }; |
| 69 |
| 49 class TestPlatformEventSource : public ui::PlatformEventSource { | 70 class TestPlatformEventSource : public ui::PlatformEventSource { |
| 50 public: | 71 public: |
| 51 TestPlatformEventSource() { | 72 TestPlatformEventSource() { |
| 52 #if defined(USE_X11) | 73 #if defined(USE_X11) |
| 53 ui::DeviceDataManagerX11::CreateInstance(); | 74 ui::DeviceDataManagerX11::CreateInstance(); |
| 54 #endif | 75 #endif |
| 55 } | 76 } |
| 56 ~TestPlatformEventSource() override {} | 77 ~TestPlatformEventSource() override {} |
| 57 | 78 |
| 58 uint32_t Dispatch(const ui::PlatformEvent& event) { | 79 uint32_t Dispatch(const ui::PlatformEvent& event) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 #endif | 184 #endif |
| 164 return widget.Pass(); | 185 return widget.Pass(); |
| 165 } | 186 } |
| 166 | 187 |
| 167 const MenuItemView* pending_state_item() const { | 188 const MenuItemView* pending_state_item() const { |
| 168 return controller_->pending_state_.item; | 189 return controller_->pending_state_.item; |
| 169 } | 190 } |
| 170 | 191 |
| 171 void SetPendingStateItem(MenuItemView* item) { | 192 void SetPendingStateItem(MenuItemView* item) { |
| 172 controller_->pending_state_.item = item; | 193 controller_->pending_state_.item = item; |
| 194 controller_->pending_state_.submenu_open = true; |
| 173 } | 195 } |
| 174 | 196 |
| 175 void ResetSelection() { | 197 void ResetSelection() { |
| 176 controller_->SetSelection(nullptr, | 198 controller_->SetSelection(nullptr, |
| 177 MenuController::SELECTION_EXIT | | 199 MenuController::SELECTION_EXIT | |
| 178 MenuController::SELECTION_UPDATE_IMMEDIATELY); | 200 MenuController::SELECTION_UPDATE_IMMEDIATELY); |
| 179 } | 201 } |
| 180 | 202 |
| 181 void IncrementSelection(int delta) { | 203 void IncrementSelection() { |
| 182 controller_->IncrementSelection(delta); | 204 controller_->IncrementSelection(MenuController::INCREMENT_SELECTION_DOWN); |
| 183 } | 205 } |
| 184 | 206 |
| 185 MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) { | 207 void DecrementSelection() { |
| 186 return controller_->FindFirstSelectableMenuItem(parent); | 208 controller_->IncrementSelection(MenuController::INCREMENT_SELECTION_UP); |
| 209 } |
| 210 |
| 211 MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { |
| 212 return controller_->FindInitialSelectableMenuItem( |
| 213 parent, MenuController::INCREMENT_SELECTION_DOWN); |
| 214 } |
| 215 |
| 216 MenuItemView* FindInitialSelectableMenuItemUp(MenuItemView* parent) { |
| 217 return controller_->FindInitialSelectableMenuItem( |
| 218 parent, MenuController::INCREMENT_SELECTION_UP); |
| 187 } | 219 } |
| 188 | 220 |
| 189 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, | 221 MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, |
| 190 int index, | 222 int index) { |
| 191 int delta) { | 223 return controller_->FindNextSelectableMenuItem( |
| 192 return controller_->FindNextSelectableMenuItem(parent, index, delta); | 224 parent, index, MenuController::INCREMENT_SELECTION_DOWN); |
| 193 } | 225 } |
| 226 |
| 227 MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent, |
| 228 int index) { |
| 229 return controller_->FindNextSelectableMenuItem( |
| 230 parent, index, MenuController::INCREMENT_SELECTION_UP); |
| 231 } |
| 232 |
| 194 void SetupMenu(views::Widget* owner, views::MenuItemView* item) { | 233 void SetupMenu(views::Widget* owner, views::MenuItemView* item) { |
| 195 ResetMenuController(); | 234 ResetMenuController(); |
| 196 controller_ = new MenuController(nullptr, true, nullptr); | 235 controller_ = new MenuController(nullptr, true, nullptr); |
| 197 controller_->owner_ = owner; | 236 controller_->owner_ = owner; |
| 198 controller_->showing_ = true; | 237 controller_->showing_ = true; |
| 199 controller_->SetSelection(item, | 238 controller_->SetSelection(item, |
| 200 MenuController::SELECTION_UPDATE_IMMEDIATELY); | 239 MenuController::SELECTION_UPDATE_IMMEDIATELY); |
| 201 } | 240 } |
| 202 | 241 |
| 203 void RunMenu(views::Widget* owner) { | 242 void RunMenu(views::Widget* owner) { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 break; | 373 break; |
| 335 } | 374 } |
| 336 } | 375 } |
| 337 | 376 |
| 338 int outstanding_touches() const { return outstanding_touches_; } | 377 int outstanding_touches() const { return outstanding_touches_; } |
| 339 | 378 |
| 340 private: | 379 private: |
| 341 int outstanding_touches_; | 380 int outstanding_touches_; |
| 342 }; | 381 }; |
| 343 | 382 |
| 344 // Tests that touch event ids are released correctly. See | 383 // Tests that touch event ids are released correctly. See crbug.com/439051 for |
| 345 // crbug.com/439051 for details. When the ids aren't managed | 384 // details. When the ids aren't managed correctly, we get stuck down touches. |
| 346 // correctly, we get stuck down touches. | |
| 347 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { | 385 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { |
| 348 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 386 scoped_ptr<Widget> owner(CreateOwnerWidget()); |
| 349 TestEventHandler test_event_handler; | 387 TestEventHandler test_event_handler; |
| 350 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( | 388 owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( |
| 351 &test_event_handler); | 389 &test_event_handler); |
| 352 | 390 |
| 353 std::vector<int> devices; | 391 std::vector<int> devices; |
| 354 devices.push_back(1); | 392 devices.push_back(1); |
| 355 ui::SetUpTouchDevicesForTest(devices); | 393 ui::SetUpTouchDevicesForTest(devices); |
| 356 | 394 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 368 base::Unretained(this), MenuController::EXIT_OUTERMOST)); | 406 base::Unretained(this), MenuController::EXIT_OUTERMOST)); |
| 369 | 407 |
| 370 RunMenu(owner.get()); | 408 RunMenu(owner.get()); |
| 371 EXPECT_EQ(0, test_event_handler.outstanding_touches()); | 409 EXPECT_EQ(0, test_event_handler.outstanding_touches()); |
| 372 | 410 |
| 373 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( | 411 owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler( |
| 374 &test_event_handler); | 412 &test_event_handler); |
| 375 } | 413 } |
| 376 #endif // defined(USE_X11) | 414 #endif // defined(USE_X11) |
| 377 | 415 |
| 378 TEST_F(MenuControllerTest, FirstSelectedItem) { | 416 // Tests that initial selected menu items are correct when items are enabled or |
| 417 // disabled. |
| 418 TEST_F(MenuControllerTest, InitialSelectedItem) { |
| 379 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 419 scoped_ptr<Widget> owner(CreateOwnerWidget()); |
| 380 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | 420 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); |
| 381 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | 421 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); |
| 382 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | 422 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); |
| 383 // Disabling the item "One" gets it skipped when a menu is first opened. | 423 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); |
| 384 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); | |
| 385 | |
| 386 SetupMenu(owner.get(), menu_item.get()); | 424 SetupMenu(owner.get(), menu_item.get()); |
| 387 | 425 |
| 388 // First selectable item should be item "Two". | 426 // Leave items "Two" and "Three" enabled. |
| 389 MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item.get()); | 427 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
| 428 // The first selectable item should be item "Two". |
| 429 MenuItemView* first_selectable = |
| 430 FindInitialSelectableMenuItemDown(menu_item.get()); |
| 431 ASSERT_NE(nullptr, first_selectable); |
| 390 EXPECT_EQ(2, first_selectable->GetCommand()); | 432 EXPECT_EQ(2, first_selectable->GetCommand()); |
| 433 // The last selectable item should be item "Three". |
| 434 MenuItemView* last_selectable = |
| 435 FindInitialSelectableMenuItemUp(menu_item.get()); |
| 436 ASSERT_NE(nullptr, last_selectable); |
| 437 EXPECT_EQ(3, last_selectable->GetCommand()); |
| 438 |
| 439 // Leave items "One" and "Two" enabled. |
| 440 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
| 441 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
| 442 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 443 // The first selectable item should be item "One". |
| 444 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); |
| 445 ASSERT_NE(nullptr, first_selectable); |
| 446 EXPECT_EQ(1, first_selectable->GetCommand()); |
| 447 // The last selectable item should be item "Two". |
| 448 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); |
| 449 ASSERT_NE(nullptr, last_selectable); |
| 450 EXPECT_EQ(2, last_selectable->GetCommand()); |
| 451 |
| 452 // Leave only a single item "One" enabled. |
| 453 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(true); |
| 454 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
| 455 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 456 // The first selectable item should be item "One". |
| 457 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); |
| 458 ASSERT_NE(nullptr, first_selectable); |
| 459 EXPECT_EQ(1, first_selectable->GetCommand()); |
| 460 // The last selectable item should be item "One". |
| 461 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); |
| 462 ASSERT_NE(nullptr, last_selectable); |
| 463 EXPECT_EQ(1, last_selectable->GetCommand()); |
| 464 |
| 465 // Leave only a single item "Three" enabled. |
| 466 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
| 467 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(false); |
| 468 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(true); |
| 469 // The first selectable item should be item "Three". |
| 470 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); |
| 471 ASSERT_NE(nullptr, first_selectable); |
| 472 EXPECT_EQ(3, first_selectable->GetCommand()); |
| 473 // The last selectable item should be item "Three". |
| 474 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); |
| 475 ASSERT_NE(nullptr, last_selectable); |
| 476 EXPECT_EQ(3, last_selectable->GetCommand()); |
| 477 |
| 478 // Leave only a single item ("Two") selected. It should be the first and the |
| 479 // last selectable item. |
| 480 menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false); |
| 481 menu_item->GetSubmenu()->GetMenuItemAt(1)->SetEnabled(true); |
| 482 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 483 first_selectable = FindInitialSelectableMenuItemDown(menu_item.get()); |
| 484 ASSERT_NE(nullptr, first_selectable); |
| 485 EXPECT_EQ(2, first_selectable->GetCommand()); |
| 486 last_selectable = FindInitialSelectableMenuItemUp(menu_item.get()); |
| 487 ASSERT_NE(nullptr, last_selectable); |
| 488 EXPECT_EQ(2, last_selectable->GetCommand()); |
| 391 | 489 |
| 392 // There should be no next or previous selectable item since there is only a | 490 // There should be no next or previous selectable item since there is only a |
| 393 // single enabled item in the menu. | 491 // single enabled item in the menu. |
| 394 SetPendingStateItem(first_selectable); | 492 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1)); |
| 395 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, 1)); | 493 EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item.get(), 1)); |
| 396 EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, -1)); | |
| 397 | 494 |
| 398 // Clear references in menu controller to the menu item that is going away. | 495 // Clear references in menu controller to the menu item that is going away. |
| 399 ResetSelection(); | 496 ResetSelection(); |
| 400 } | 497 } |
| 401 | 498 |
| 499 // Tests that opening menu and pressing 'Down' and 'Up' iterates over enabled |
| 500 // items. |
| 402 TEST_F(MenuControllerTest, NextSelectedItem) { | 501 TEST_F(MenuControllerTest, NextSelectedItem) { |
| 403 scoped_ptr<Widget> owner(CreateOwnerWidget()); | 502 scoped_ptr<Widget> owner(CreateOwnerWidget()); |
| 404 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); | 503 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); |
| 405 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); | 504 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); |
| 406 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); | 505 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); |
| 407 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); | 506 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); |
| 408 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); | 507 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); |
| 409 // Disabling the item "Three" gets it skipped when using keyboard to navigate. | 508 // Disabling the item "Three" gets it skipped when using keyboard to navigate. |
| 410 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); | 509 menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false); |
| 411 | 510 |
| 412 SetupMenu(owner.get(), menu_item.get()); | 511 SetupMenu(owner.get(), menu_item.get()); |
| 413 | 512 |
| 414 // Fake initial hot selection. | 513 // Fake initial hot selection. |
| 415 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); | 514 SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0)); |
| 416 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 515 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 417 | 516 |
| 418 // Move down in the menu. | 517 // Move down in the menu. |
| 419 // Select next item. | 518 // Select next item. |
| 420 IncrementSelection(1); | 519 IncrementSelection(); |
| 421 EXPECT_EQ(2, pending_state_item()->GetCommand()); | 520 EXPECT_EQ(2, pending_state_item()->GetCommand()); |
| 422 | 521 |
| 423 // Skip disabled item. | 522 // Skip disabled item. |
| 424 IncrementSelection(1); | 523 IncrementSelection(); |
| 425 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 524 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
| 426 | 525 |
| 427 // Wrap around. | 526 // Wrap around. |
| 428 IncrementSelection(1); | 527 IncrementSelection(); |
| 429 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 528 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 430 | 529 |
| 431 // Move up in the menu. | 530 // Move up in the menu. |
| 432 // Wrap around. | 531 // Wrap around. |
| 433 IncrementSelection(-1); | 532 DecrementSelection(); |
| 434 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 533 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
| 435 | 534 |
| 436 // Skip disabled item. | 535 // Skip disabled item. |
| 437 IncrementSelection(-1); | 536 DecrementSelection(); |
| 438 EXPECT_EQ(2, pending_state_item()->GetCommand()); | 537 EXPECT_EQ(2, pending_state_item()->GetCommand()); |
| 439 | 538 |
| 440 // Select previous item. | 539 // Select previous item. |
| 441 IncrementSelection(-1); | 540 DecrementSelection(); |
| 442 EXPECT_EQ(1, pending_state_item()->GetCommand()); | 541 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 443 | 542 |
| 444 // Clear references in menu controller to the menu item that is going away. | 543 // Clear references in menu controller to the menu item that is going away. |
| 445 ResetSelection(); | 544 ResetSelection(); |
| 446 } | 545 } |
| 447 | 546 |
| 547 // Tests that opening menu and pressing 'Up' selects the last enabled menu item. |
| 548 TEST_F(MenuControllerTest, PreviousSelectedItem) { |
| 549 scoped_ptr<Widget> owner(CreateOwnerWidget()); |
| 550 scoped_ptr<TestMenuItemViewShown> menu_item(new TestMenuItemViewShown); |
| 551 menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One")); |
| 552 menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two")); |
| 553 menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three")); |
| 554 menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four")); |
| 555 // Disabling the item "Four" gets it skipped when using keyboard to navigate. |
| 556 menu_item->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false); |
| 557 |
| 558 SetupMenu(owner.get(), menu_item.get()); |
| 559 |
| 560 // Fake initial root item selection and submenu showing. |
| 561 SetPendingStateItem(menu_item.get()); |
| 562 EXPECT_EQ(0, pending_state_item()->GetCommand()); |
| 563 |
| 564 // Move up and select a previous (in our case the last enabled) item. |
| 565 DecrementSelection(); |
| 566 EXPECT_EQ(3, pending_state_item()->GetCommand()); |
| 567 |
| 568 // Clear references in menu controller to the menu item that is going away. |
| 569 ResetSelection(); |
| 570 } |
| 571 |
| 448 } // namespace views | 572 } // namespace views |
| OLD | NEW |