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 |