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/callback.h" | 7 #include "base/callback.h" |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
10 #include "build/build_config.h" | 10 #include "build/build_config.h" |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 | 400 |
401 void SetIsCombobox(bool is_combobox) { | 401 void SetIsCombobox(bool is_combobox) { |
402 menu_controller_->set_is_combobox(is_combobox); | 402 menu_controller_->set_is_combobox(is_combobox); |
403 } | 403 } |
404 | 404 |
405 void SetSelectionOnPointerDown(SubmenuView* source, | 405 void SetSelectionOnPointerDown(SubmenuView* source, |
406 const ui::LocatedEvent* event) { | 406 const ui::LocatedEvent* event) { |
407 menu_controller_->SetSelectionOnPointerDown(source, event); | 407 menu_controller_->SetSelectionOnPointerDown(source, event); |
408 } | 408 } |
409 | 409 |
| 410 void UpdateSelection(View* view) { |
| 411 menu_controller_->SetSelection(menu_controller_->pending_state_.item, 0); |
| 412 } |
| 413 |
410 void RunMenu() { | 414 void RunMenu() { |
411 #if defined(USE_AURA) | 415 #if defined(USE_AURA) |
412 scoped_ptr<MenuKeyEventHandler> key_event_handler(new MenuKeyEventHandler); | 416 scoped_ptr<MenuKeyEventHandler> key_event_handler(new MenuKeyEventHandler); |
413 #endif | 417 #endif |
414 | 418 |
415 menu_controller_->message_loop_depth_++; | 419 menu_controller_->message_loop_depth_++; |
416 menu_controller_->RunMessageLoop(false); | 420 menu_controller_->RunMessageLoop(false); |
417 menu_controller_->message_loop_depth_--; | 421 menu_controller_->message_loop_depth_--; |
418 } | 422 } |
419 | 423 |
(...skipping 14 matching lines...) Expand all Loading... |
434 return menu_controller_delegate_.get(); | 438 return menu_controller_delegate_.get(); |
435 } | 439 } |
436 MenuController* menu_controller() { return menu_controller_; } | 440 MenuController* menu_controller() { return menu_controller_; } |
437 const MenuItemView* pending_state_item() const { | 441 const MenuItemView* pending_state_item() const { |
438 return menu_controller_->pending_state_.item; | 442 return menu_controller_->pending_state_.item; |
439 } | 443 } |
440 MenuController::ExitType menu_exit_type() const { | 444 MenuController::ExitType menu_exit_type() const { |
441 return menu_controller_->exit_type_; | 445 return menu_controller_->exit_type_; |
442 } | 446 } |
443 | 447 |
| 448 void AddButtonMenuItems() { |
| 449 MenuItemView* item_view = |
| 450 menu_item()->AppendMenuItemWithLabel(5, base::ASCIIToUTF16("Five")); |
| 451 for (int i = 0; i < 3; ++i) { |
| 452 LabelButton* button = |
| 453 new LabelButton(nullptr, base::ASCIIToUTF16("Label")); |
| 454 button->SetFocusable(true); |
| 455 item_view->AddChildView(button); |
| 456 } |
| 457 menu_item()->GetSubmenu()->ShowAt(owner(), menu_item()->bounds(), false); |
| 458 } |
| 459 |
444 private: | 460 private: |
445 void DestroyMenuController() { | 461 void DestroyMenuController() { |
446 if (!menu_controller_) | 462 if (!menu_controller_) |
447 return; | 463 return; |
448 | 464 |
449 if (!owner_->IsClosed()) | 465 if (!owner_->IsClosed()) |
450 owner_->RemoveObserver(menu_controller_); | 466 owner_->RemoveObserver(menu_controller_); |
451 | 467 |
452 menu_controller_->showing_ = false; | 468 menu_controller_->showing_ = false; |
453 menu_controller_->owner_ = nullptr; | 469 menu_controller_->owner_ = nullptr; |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
695 EXPECT_EQ(0, pending_state_item()->GetCommand()); | 711 EXPECT_EQ(0, pending_state_item()->GetCommand()); |
696 | 712 |
697 // Handle searching for 'f'; should find "Four". | 713 // Handle searching for 'f'; should find "Four". |
698 SelectByChar('f'); | 714 SelectByChar('f'); |
699 EXPECT_EQ(4, pending_state_item()->GetCommand()); | 715 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
700 | 716 |
701 // Clear references in menu controller to the menu item that is going away. | 717 // Clear references in menu controller to the menu item that is going away. |
702 ResetSelection(); | 718 ResetSelection(); |
703 } | 719 } |
704 | 720 |
| 721 TEST_F(MenuControllerTest, SelectChildButtonView) { |
| 722 AddButtonMenuItems(); |
| 723 |
| 724 // Handle searching for 'f'; should find "Four". |
| 725 SelectByChar('f'); |
| 726 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
| 727 |
| 728 View* buttons_view = menu_item()->GetSubmenu()->child_at(4); |
| 729 ASSERT_NE(nullptr, buttons_view); |
| 730 CustomButton* button1 = |
| 731 CustomButton::AsCustomButton(buttons_view->child_at(0)); |
| 732 ASSERT_NE(nullptr, button1); |
| 733 CustomButton* button2 = |
| 734 CustomButton::AsCustomButton(buttons_view->child_at(1)); |
| 735 ASSERT_NE(nullptr, button2); |
| 736 CustomButton* button3 = |
| 737 CustomButton::AsCustomButton(buttons_view->child_at(2)); |
| 738 ASSERT_NE(nullptr, button2); |
| 739 EXPECT_FALSE(button1->IsHotTracked()); |
| 740 EXPECT_FALSE(button2->IsHotTracked()); |
| 741 EXPECT_FALSE(button3->IsHotTracked()); |
| 742 |
| 743 // Move selection to |button1|. |
| 744 IncrementSelection(); |
| 745 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 746 EXPECT_TRUE(button1->IsHotTracked()); |
| 747 EXPECT_FALSE(button2->IsHotTracked()); |
| 748 EXPECT_FALSE(button3->IsHotTracked()); |
| 749 |
| 750 // Move selection to |button2|. |
| 751 IncrementSelection(); |
| 752 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 753 EXPECT_FALSE(button1->IsHotTracked()); |
| 754 EXPECT_TRUE(button2->IsHotTracked()); |
| 755 EXPECT_FALSE(button3->IsHotTracked()); |
| 756 |
| 757 // Move selection to |button3|. |
| 758 IncrementSelection(); |
| 759 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 760 EXPECT_FALSE(button1->IsHotTracked()); |
| 761 EXPECT_FALSE(button2->IsHotTracked()); |
| 762 EXPECT_TRUE(button3->IsHotTracked()); |
| 763 |
| 764 // Externally set hot tracked to the first child button and update. |
| 765 button1->SetHotTracked(true); |
| 766 // Emulate a mouse move that would update menu controller selection. |
| 767 UpdateSelection(buttons_view); |
| 768 |
| 769 // Incrementing selection should move hot tracking to the second button (next |
| 770 // after the first button). |
| 771 IncrementSelection(); |
| 772 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 773 EXPECT_FALSE(button1->IsHotTracked()); |
| 774 EXPECT_TRUE(button2->IsHotTracked()); |
| 775 EXPECT_FALSE(button3->IsHotTracked()); |
| 776 |
| 777 // Increment selection twice to wrap around. |
| 778 IncrementSelection(); |
| 779 IncrementSelection(); |
| 780 EXPECT_EQ(1, pending_state_item()->GetCommand()); |
| 781 |
| 782 // Clear references in menu controller to the menu item that is going away. |
| 783 ResetSelection(); |
| 784 } |
| 785 |
| 786 TEST_F(MenuControllerTest, DeleteChildButtonView) { |
| 787 AddButtonMenuItems(); |
| 788 |
| 789 // Handle searching for 'f'; should find "Four". |
| 790 SelectByChar('f'); |
| 791 EXPECT_EQ(4, pending_state_item()->GetCommand()); |
| 792 |
| 793 View* buttons_view = menu_item()->GetSubmenu()->child_at(4); |
| 794 ASSERT_NE(nullptr, buttons_view); |
| 795 CustomButton* button1 = |
| 796 CustomButton::AsCustomButton(buttons_view->child_at(0)); |
| 797 ASSERT_NE(nullptr, button1); |
| 798 CustomButton* button2 = |
| 799 CustomButton::AsCustomButton(buttons_view->child_at(1)); |
| 800 ASSERT_NE(nullptr, button2); |
| 801 CustomButton* button3 = |
| 802 CustomButton::AsCustomButton(buttons_view->child_at(2)); |
| 803 ASSERT_NE(nullptr, button2); |
| 804 EXPECT_FALSE(button1->IsHotTracked()); |
| 805 EXPECT_FALSE(button2->IsHotTracked()); |
| 806 EXPECT_FALSE(button3->IsHotTracked()); |
| 807 |
| 808 // Increment twice to move selection to |button2|. |
| 809 IncrementSelection(); |
| 810 IncrementSelection(); |
| 811 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 812 EXPECT_FALSE(button1->IsHotTracked()); |
| 813 EXPECT_TRUE(button2->IsHotTracked()); |
| 814 EXPECT_FALSE(button3->IsHotTracked()); |
| 815 |
| 816 // Externally set hot tracked to the first child button and update. |
| 817 button1->SetHotTracked(true); |
| 818 // Delete |button2| while another child button view is hot-tracked. |
| 819 // This should update MenuController via ViewHierarchyChanged and reset |
| 820 // |hot_button_|. |
| 821 delete button2; |
| 822 // Emulate a mouse move that would update menu controller selection. |
| 823 // This should not crash. |
| 824 UpdateSelection(buttons_view); |
| 825 |
| 826 // Incrementing selection should now set hot-tracked item to |button3|. |
| 827 IncrementSelection(); |
| 828 EXPECT_EQ(5, pending_state_item()->GetCommand()); |
| 829 EXPECT_FALSE(button1->IsHotTracked()); |
| 830 EXPECT_TRUE(button3->IsHotTracked()); |
| 831 } |
| 832 |
705 // Tests that a menu opened asynchronously, will notify its | 833 // Tests that a menu opened asynchronously, will notify its |
706 // MenuControllerDelegate when Accept is called. | 834 // MenuControllerDelegate when Accept is called. |
707 TEST_F(MenuControllerTest, AsynchronousAccept) { | 835 TEST_F(MenuControllerTest, AsynchronousAccept) { |
708 MenuController* controller = menu_controller(); | 836 MenuController* controller = menu_controller(); |
709 controller->SetAsyncRun(true); | 837 controller->SetAsyncRun(true); |
710 | 838 |
711 int mouse_event_flags = 0; | 839 int mouse_event_flags = 0; |
712 MenuItemView* run_result = | 840 MenuItemView* run_result = |
713 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), | 841 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), |
714 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); | 842 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
869 EXPECT_TRUE(IsAsyncRun()); | 997 EXPECT_TRUE(IsAsyncRun()); |
870 EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); | 998 EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); |
871 | 999 |
872 MenuItemView* item = menu_item(); | 1000 MenuItemView* item = menu_item(); |
873 int mouse_event_flags = 0; | 1001 int mouse_event_flags = 0; |
874 MenuItemView* run_result = | 1002 MenuItemView* run_result = |
875 controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 1003 controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, |
876 false, false, &mouse_event_flags); | 1004 false, false, &mouse_event_flags); |
877 EXPECT_EQ(run_result, nullptr); | 1005 EXPECT_EQ(run_result, nullptr); |
878 | 1006 |
879 // Show a sub menu to targert with a pointer selection. However have the event | 1007 // Show a sub menu to target with a pointer selection. However have the event |
880 // occur outside of the bounds of the entire menu. | 1008 // occur outside of the bounds of the entire menu. |
881 SubmenuView* sub_menu = item->GetSubmenu(); | 1009 SubmenuView* sub_menu = item->GetSubmenu(); |
882 sub_menu->ShowAt(owner(), item->bounds(), false); | 1010 sub_menu->ShowAt(owner(), item->bounds(), false); |
883 gfx::Point location(sub_menu->bounds().bottom_right()); | 1011 gfx::Point location(sub_menu->bounds().bottom_right()); |
884 location.Offset(1, 1); | 1012 location.Offset(1, 1); |
885 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, | 1013 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, |
886 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); | 1014 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); |
887 | 1015 |
888 // When attempting to select outside of all menus this should lead to a | 1016 // When attempting to select outside of all menus this should lead to a |
889 // shutdown. This should not crash while attempting to repost the event. | 1017 // shutdown. This should not crash while attempting to repost the event. |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
968 EXPECT_TRUE(IsAsyncRun()); | 1096 EXPECT_TRUE(IsAsyncRun()); |
969 EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); | 1097 EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); |
970 | 1098 |
971 MenuItemView* item = menu_item(); | 1099 MenuItemView* item = menu_item(); |
972 int mouse_event_flags = 0; | 1100 int mouse_event_flags = 0; |
973 MenuItemView* run_result = | 1101 MenuItemView* run_result = |
974 controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, | 1102 controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, |
975 false, false, &mouse_event_flags); | 1103 false, false, &mouse_event_flags); |
976 EXPECT_EQ(run_result, nullptr); | 1104 EXPECT_EQ(run_result, nullptr); |
977 | 1105 |
978 // Show a sub menu to targert with a pointer selection. However have the event | 1106 // Show a sub menu to target with a pointer selection. However have the event |
979 // occur outside of the bounds of the entire menu. | 1107 // occur outside of the bounds of the entire menu. |
980 SubmenuView* sub_menu = item->GetSubmenu(); | 1108 SubmenuView* sub_menu = item->GetSubmenu(); |
981 sub_menu->ShowAt(owner(), item->bounds(), true); | 1109 sub_menu->ShowAt(owner(), item->bounds(), true); |
982 gfx::Point location(sub_menu->bounds().bottom_right()); | 1110 gfx::Point location(sub_menu->bounds().bottom_right()); |
983 location.Offset(1, 1); | 1111 location.Offset(1, 1); |
984 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, | 1112 ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, |
985 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); | 1113 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); |
986 | 1114 |
987 // This will lead to MenuController being deleted during the event repost. | 1115 // This will lead to MenuController being deleted during the event repost. |
988 // The remainder of this test, and TearDown should not crash. | 1116 // The remainder of this test, and TearDown should not crash. |
989 DestroyMenuControllerOnMenuClosed(nested_delegate.get()); | 1117 DestroyMenuControllerOnMenuClosed(nested_delegate.get()); |
990 // When attempting to select outside of all menus this should lead to a | 1118 // When attempting to select outside of all menus this should lead to a |
991 // shutdown. This should not crash while attempting to repost the event. | 1119 // shutdown. This should not crash while attempting to repost the event. |
992 SetSelectionOnPointerDown(sub_menu, &event); | 1120 SetSelectionOnPointerDown(sub_menu, &event); |
993 | 1121 |
994 // Close to remove observers before test TearDown | 1122 // Close to remove observers before test TearDown |
995 sub_menu->Close(); | 1123 sub_menu->Close(); |
996 EXPECT_EQ(1, nested_delegate->on_menu_closed_called()); | 1124 EXPECT_EQ(1, nested_delegate->on_menu_closed_called()); |
997 } | 1125 } |
998 | 1126 |
999 } // namespace test | 1127 } // namespace test |
1000 } // namespace views | 1128 } // namespace views |
OLD | NEW |