Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(319)

Side by Side Diff: ui/views/controls/menu/menu_controller.cc

Issue 1741093002: Fixes alternating keyboard and mouse hot-tracking in menus (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixes alternating keyboard and mouse hot-tracking in menus (comments) Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/i18n/case_conversion.h" 7 #include "base/i18n/case_conversion.h"
8 #include "base/i18n/rtl.h" 8 #include "base/i18n/rtl.h"
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 94
95 base::string16 lower_title = base::i18n::ToLower(menu->title()); 95 base::string16 lower_title = base::i18n::ToLower(menu->title());
96 return !lower_title.empty() && lower_title[0] == key; 96 return !lower_title.empty() && lower_title[0] == key;
97 } 97 }
98 98
99 // Returns the first descendant of |view| that is hot tracked. 99 // Returns the first descendant of |view| that is hot tracked.
100 CustomButton* GetFirstHotTrackedView(View* view) { 100 CustomButton* GetFirstHotTrackedView(View* view) {
101 if (!view) 101 if (!view)
102 return NULL; 102 return NULL;
103 CustomButton* button = CustomButton::AsCustomButton(view); 103 CustomButton* button = CustomButton::AsCustomButton(view);
104 if (button) { 104 if (button && button->IsHotTracked())
varkha 2016/02/29 19:09:44 Equivalent change, just seems simpler.
105 if (button->IsHotTracked()) 105 return button;
106 return button;
107 }
108 106
109 for (int i = 0; i < view->child_count(); ++i) { 107 for (int i = 0; i < view->child_count(); ++i) {
110 CustomButton* hot_view = GetFirstHotTrackedView(view->child_at(i)); 108 CustomButton* hot_view = GetFirstHotTrackedView(view->child_at(i));
111 if (hot_view) 109 if (hot_view)
112 return hot_view; 110 return hot_view;
113 } 111 }
114 return NULL; 112 return NULL;
115 } 113 }
116 114
117 // Recurses through the child views of |view| returning the first view starting 115 // Recurses through the child views of |view| returning the first view starting
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 // is the same root view we've been forwarding to. Otherwise, it's the root 546 // is the same root view we've been forwarding to. Otherwise, it's the root
549 // view of the target. 547 // view of the target.
550 MenuHostRootView* forward_to_root = 548 MenuHostRootView* forward_to_root =
551 current_mouse_pressed_state_ ? current_mouse_event_target_ 549 current_mouse_pressed_state_ ? current_mouse_event_target_
552 : GetRootView(source, event.location()); 550 : GetRootView(source, event.location());
553 551
554 current_mouse_pressed_state_ |= event.changed_button_flags(); 552 current_mouse_pressed_state_ |= event.changed_button_flags();
555 553
556 if (forward_to_root) { 554 if (forward_to_root) {
557 ui::MouseEvent event_for_root(event); 555 ui::MouseEvent event_for_root(event);
556 // Reset hot-tracking if a different view is getting a mouse press.
558 ConvertLocatedEventForRootView(source, forward_to_root, &event_for_root); 557 ConvertLocatedEventForRootView(source, forward_to_root, &event_for_root);
559 View* view = 558 View* view =
560 forward_to_root->GetEventHandlerForPoint(event_for_root.location()); 559 forward_to_root->GetEventHandlerForPoint(event_for_root.location());
560 CustomButton* button = CustomButton::AsCustomButton(view);
561 if (hot_button_ && hot_button_ != button)
562 SetHotTrackedButton(nullptr);
563
561 // Empty menu items are always handled by the menu controller. 564 // Empty menu items are always handled by the menu controller.
562 if (!view || view->id() != MenuItemView::kEmptyMenuItemViewID) { 565 if (!view || view->id() != MenuItemView::kEmptyMenuItemViewID) {
563 bool processed = forward_to_root->ProcessMousePressed(event_for_root); 566 bool processed = forward_to_root->ProcessMousePressed(event_for_root);
564 // If the event was processed, the root view becomes our current mouse 567 // If the event was processed, the root view becomes our current mouse
565 // handler... 568 // handler...
566 if (processed && !current_mouse_event_target_) { 569 if (processed && !current_mouse_event_target_) {
567 current_mouse_event_target_ = forward_to_root; 570 current_mouse_event_target_ = forward_to_root;
568 } 571 }
569 572
570 // ...and we always return the result of the current handler. 573 // ...and we always return the result of the current handler.
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
708 const ui::MouseEvent& event) { 711 const ui::MouseEvent& event) {
709 if (current_mouse_event_target_) { 712 if (current_mouse_event_target_) {
710 ui::MouseEvent event_for_root(event); 713 ui::MouseEvent event_for_root(event);
711 ConvertLocatedEventForRootView(source, current_mouse_event_target_, 714 ConvertLocatedEventForRootView(source, current_mouse_event_target_,
712 &event_for_root); 715 &event_for_root);
713 current_mouse_event_target_->ProcessMouseMoved(event_for_root); 716 current_mouse_event_target_->ProcessMouseMoved(event_for_root);
714 return; 717 return;
715 } 718 }
716 719
717 MenuHostRootView* root_view = GetRootView(source, event.location()); 720 MenuHostRootView* root_view = GetRootView(source, event.location());
718 if (root_view) 721 if (root_view) {
719 root_view->ProcessMouseMoved(event); 722 root_view->ProcessMouseMoved(event);
720 // TODO(varkha): It is possible that another child CustomButton has become 723
721 // hot-tracked as a result of this event. We need to track it for accurate 724 // Update hot-tracked button when a button state is changed with a mouse
722 // hot-tracking when both mouse and keyboard are used to navigate the menu. 725 // event. It is necessary to track it for accurate hot-tracking when both
726 // mouse and keyboard are used to navigate the menu.
727 ui::MouseEvent event_for_root(event);
728 ConvertLocatedEventForRootView(source, root_view, &event_for_root);
729 View* view =
730 root_view->GetEventHandlerForPoint(event_for_root.location());
731 CustomButton* button = CustomButton::AsCustomButton(view);
732 if (button && button->IsHotTracked())
733 SetHotTrackedButton(button);
734 }
735
723 HandleMouseLocation(source, event.location()); 736 HandleMouseLocation(source, event.location());
724 } 737 }
725 738
726 void MenuController::OnMouseEntered(SubmenuView* source, 739 void MenuController::OnMouseEntered(SubmenuView* source,
727 const ui::MouseEvent& event) { 740 const ui::MouseEvent& event) {
728 // MouseEntered is always followed by a mouse moved, so don't need to 741 // MouseEntered is always followed by a mouse moved, so don't need to
729 // do anything here. 742 // do anything here.
730 } 743 }
731 744
732 bool MenuController::OnMouseWheel(SubmenuView* source, 745 bool MenuController::OnMouseWheel(SubmenuView* source,
733 const ui::MouseWheelEvent& event) { 746 const ui::MouseWheelEvent& event) {
734 MenuPart part = GetMenuPart(source, event.location()); 747 MenuPart part = GetMenuPart(source, event.location());
735 return part.submenu && part.submenu->OnMouseWheel(event); 748 return part.submenu && part.submenu->OnMouseWheel(event);
736 } 749 }
737 750
738 void MenuController::OnGestureEvent(SubmenuView* source, 751 void MenuController::OnGestureEvent(SubmenuView* source,
739 ui::GestureEvent* event) { 752 ui::GestureEvent* event) {
753 MenuHostRootView* root_view = GetRootView(source, event->location());
754 if (root_view) {
755 // Reset hot-tracking if a different view is getting a touch event.
756 ui::GestureEvent event_for_root(*event);
757 ConvertLocatedEventForRootView(source, root_view, &event_for_root);
758 View* view =
759 root_view->GetEventHandlerForPoint(event_for_root.location());
760 CustomButton* button = CustomButton::AsCustomButton(view);
761 if (hot_button_ && hot_button_ != button)
762 SetHotTrackedButton(nullptr);
763 }
764
740 MenuPart part = GetMenuPart(source, event->location()); 765 MenuPart part = GetMenuPart(source, event->location());
741 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { 766 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
742 SetSelectionOnPointerDown(source, event); 767 SetSelectionOnPointerDown(source, event);
743 event->StopPropagation(); 768 event->StopPropagation();
744 } else if (event->type() == ui::ET_GESTURE_LONG_PRESS) { 769 } else if (event->type() == ui::ET_GESTURE_LONG_PRESS) {
745 if (part.type == MenuPart::MENU_ITEM && part.menu) { 770 if (part.type == MenuPart::MENU_ITEM && part.menu) {
746 gfx::Point screen_location(event->location()); 771 gfx::Point screen_location(event->location());
747 View::ConvertPointToScreen(source->GetScrollViewContainer(), 772 View::ConvertPointToScreen(source->GetScrollViewContainer(),
748 &screen_location); 773 &screen_location);
749 if (ShowContextMenu(part.menu, screen_location, ui::MENU_SOURCE_TOUCH)) 774 if (ShowContextMenu(part.menu, screen_location, ui::MENU_SOURCE_TOUCH))
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
796 View* MenuController::GetTooltipHandlerForPoint(SubmenuView* source, 821 View* MenuController::GetTooltipHandlerForPoint(SubmenuView* source,
797 const gfx::Point& point) { 822 const gfx::Point& point) {
798 MenuHostRootView* root_view = GetRootView(source, point); 823 MenuHostRootView* root_view = GetRootView(source, point);
799 return root_view ? root_view->ProcessGetTooltipHandlerForPoint(point) 824 return root_view ? root_view->ProcessGetTooltipHandlerForPoint(point)
800 : nullptr; 825 : nullptr;
801 } 826 }
802 827
803 void MenuController::ViewHierarchyChanged( 828 void MenuController::ViewHierarchyChanged(
804 SubmenuView* source, 829 SubmenuView* source,
805 const View::ViewHierarchyChangedDetails& details) { 830 const View::ViewHierarchyChangedDetails& details) {
806 // If the current mouse handler is removed, remove it as the handler. 831 if (!details.is_add) {
807 if (!details.is_add && details.child == current_mouse_event_target_) { 832 // If the current mouse handler is removed, remove it as the handler.
808 current_mouse_event_target_ = nullptr; 833 if (details.child == current_mouse_event_target_) {
809 current_mouse_pressed_state_ = 0; 834 current_mouse_event_target_ = nullptr;
835 current_mouse_pressed_state_ = 0;
836 }
837 // Update |hot_button_| if it gets removed while a menu is up.
838 if (details.child == hot_button_)
839 hot_button_ = nullptr;
810 } 840 }
811 } 841 }
812 842
813 bool MenuController::GetDropFormats( 843 bool MenuController::GetDropFormats(
814 SubmenuView* source, 844 SubmenuView* source,
815 int* formats, 845 int* formats,
816 std::set<ui::Clipboard::FormatType>* format_types) { 846 std::set<ui::Clipboard::FormatType>* format_types) {
817 return source->GetMenuItem()->GetDelegate()->GetDropFormats( 847 return source->GetMenuItem()->GetDelegate()->GetDropFormats(
818 source->GetMenuItem(), formats, format_types); 848 source->GetMenuItem(), formats, format_types);
819 } 849 }
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
1027 size_t paths_differ_at = 0; 1057 size_t paths_differ_at = 0;
1028 std::vector<MenuItemView*> current_path; 1058 std::vector<MenuItemView*> current_path;
1029 std::vector<MenuItemView*> new_path; 1059 std::vector<MenuItemView*> new_path;
1030 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, &current_path, 1060 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, &current_path,
1031 &new_path, &paths_differ_at); 1061 &new_path, &paths_differ_at);
1032 1062
1033 size_t current_size = current_path.size(); 1063 size_t current_size = current_path.size();
1034 size_t new_size = new_path.size(); 1064 size_t new_size = new_path.size();
1035 1065
1036 bool pending_item_changed = pending_state_.item != menu_item; 1066 bool pending_item_changed = pending_state_.item != menu_item;
1037 if (pending_item_changed && pending_state_.item) { 1067 if (pending_item_changed && pending_state_.item)
1038 CustomButton* button = GetFirstHotTrackedView(pending_state_.item); 1068 SetHotTrackedButton(nullptr);
1039 if (button)
1040 button->SetHotTracked(false);
1041 }
1042 1069
1043 // Notify the old path it isn't selected. 1070 // Notify the old path it isn't selected.
1044 MenuDelegate* current_delegate = 1071 MenuDelegate* current_delegate =
1045 current_path.empty() ? NULL : current_path.front()->GetDelegate(); 1072 current_path.empty() ? NULL : current_path.front()->GetDelegate();
1046 for (size_t i = paths_differ_at; i < current_size; ++i) { 1073 for (size_t i = paths_differ_at; i < current_size; ++i) {
1047 if (current_delegate && 1074 if (current_delegate &&
1048 current_path[i]->GetType() == MenuItemView::SUBMENU) { 1075 current_path[i]->GetType() == MenuItemView::SUBMENU) {
1049 current_delegate->WillHideMenu(current_path[i]); 1076 current_delegate->WillHideMenu(current_path[i]);
1050 } 1077 }
1051 current_path[i]->SetSelected(false); 1078 current_path[i]->SetSelected(false);
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
1282 drop_target_(NULL), 1309 drop_target_(NULL),
1283 drop_position_(MenuDelegate::DROP_UNKNOWN), 1310 drop_position_(MenuDelegate::DROP_UNKNOWN),
1284 owner_(NULL), 1311 owner_(NULL),
1285 possible_drag_(false), 1312 possible_drag_(false),
1286 drag_in_progress_(false), 1313 drag_in_progress_(false),
1287 did_initiate_drag_(false), 1314 did_initiate_drag_(false),
1288 valid_drop_coordinates_(false), 1315 valid_drop_coordinates_(false),
1289 last_drop_operation_(MenuDelegate::DROP_UNKNOWN), 1316 last_drop_operation_(MenuDelegate::DROP_UNKNOWN),
1290 showing_submenu_(false), 1317 showing_submenu_(false),
1291 active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()), 1318 active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()),
1319 hot_button_(nullptr),
1292 delegate_(delegate), 1320 delegate_(delegate),
1293 message_loop_depth_(0), 1321 message_loop_depth_(0),
1294 closing_event_time_(base::TimeDelta()), 1322 closing_event_time_(base::TimeDelta()),
1295 menu_start_time_(base::TimeTicks()), 1323 menu_start_time_(base::TimeTicks()),
1296 async_run_(false), 1324 async_run_(false),
1297 is_combobox_(false), 1325 is_combobox_(false),
1298 item_selected_by_touch_(false), 1326 item_selected_by_touch_(false),
1299 current_mouse_event_target_(nullptr), 1327 current_mouse_event_target_(nullptr),
1300 current_mouse_pressed_state_(0), 1328 current_mouse_pressed_state_(0),
1301 message_loop_(MenuMessageLoop::Create()) { 1329 message_loop_(MenuMessageLoop::Create()) {
(...skipping 16 matching lines...) Expand all
1318 } 1346 }
1319 1347
1320 bool MenuController::SendAcceleratorToHotTrackedView() { 1348 bool MenuController::SendAcceleratorToHotTrackedView() {
1321 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); 1349 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item);
1322 if (!hot_view) 1350 if (!hot_view)
1323 return false; 1351 return false;
1324 1352
1325 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); 1353 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE);
1326 hot_view->AcceleratorPressed(accelerator); 1354 hot_view->AcceleratorPressed(accelerator);
1327 CustomButton* button = static_cast<CustomButton*>(hot_view); 1355 CustomButton* button = static_cast<CustomButton*>(hot_view);
1328 button->SetHotTracked(true); 1356 SetHotTrackedButton(button);
1329 return true; 1357 return true;
1330 } 1358 }
1331 1359
1332 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, 1360 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
1333 MenuAnchorPosition position, 1361 MenuAnchorPosition position,
1334 bool context_menu) { 1362 bool context_menu) {
1335 pending_state_.context_menu = context_menu; 1363 pending_state_.context_menu = context_menu;
1336 pending_state_.initial_bounds = bounds; 1364 pending_state_.initial_bounds = bounds;
1337 if (bounds.height() > 1) { 1365 if (bounds.height() > 1) {
1338 // Inset the bounds slightly, otherwise drag coordinates don't line up 1366 // Inset the bounds slightly, otherwise drag coordinates don't line up
(...skipping 749 matching lines...) Expand 10 before | Expand all | Expand 10 after
2088 if (item->GetSubmenu()->GetMenuItemCount()) { 2116 if (item->GetSubmenu()->GetMenuItemCount()) {
2089 MenuItemView* to_select = FindInitialSelectableMenuItem(item, direction); 2117 MenuItemView* to_select = FindInitialSelectableMenuItem(item, direction);
2090 SetInitialHotTrackedView(to_select, direction); 2118 SetInitialHotTrackedView(to_select, direction);
2091 return; 2119 return;
2092 } 2120 }
2093 } 2121 }
2094 2122
2095 if (item->has_children()) { 2123 if (item->has_children()) {
2096 CustomButton* button = GetFirstHotTrackedView(item); 2124 CustomButton* button = GetFirstHotTrackedView(item);
2097 if (button) { 2125 if (button) {
2098 button->SetHotTracked(false); 2126 DCHECK_EQ(hot_button_, button);
2099 View* to_make_hot = GetNextFocusableView( 2127 SetHotTrackedButton(nullptr);
2100 item, button, direction == INCREMENT_SELECTION_DOWN); 2128 }
2101 CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot); 2129 bool direction_is_down = direction == INCREMENT_SELECTION_DOWN;
2102 if (button_hot) { 2130 View* to_make_hot = button
2103 button_hot->SetHotTracked(true); 2131 ? GetNextFocusableView(item, button, direction_is_down)
2104 return; 2132 : GetInitialFocusableView(item, direction_is_down);
2105 } 2133 CustomButton* hot_button = CustomButton::AsCustomButton(to_make_hot);
2106 } else { 2134 if (hot_button) {
2107 View* to_make_hot = 2135 SetHotTrackedButton(hot_button);
2108 GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN); 2136 return;
varkha 2016/02/29 19:09:44 Equivalent change, just seems simpler.
2109 CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot);
2110 if (button_hot) {
2111 button_hot->SetHotTracked(true);
2112 return;
2113 }
2114 } 2137 }
2115 } 2138 }
2116 2139
2117 MenuItemView* parent = item->GetParentMenuItem(); 2140 MenuItemView* parent = item->GetParentMenuItem();
2118 if (parent) { 2141 if (parent) {
2119 int parent_count = parent->GetSubmenu()->GetMenuItemCount(); 2142 int parent_count = parent->GetSubmenu()->GetMenuItemCount();
2120 if (parent_count > 1) { 2143 if (parent_count > 1) {
2121 for (int i = 0; i < parent_count; ++i) { 2144 for (int i = 0; i < parent_count; ++i) {
2122 if (parent->GetSubmenu()->GetMenuItemAt(i) == item) { 2145 if (parent->GetSubmenu()->GetMenuItemAt(i) == item) {
2123 MenuItemView* to_select = 2146 MenuItemView* to_select =
(...skipping 481 matching lines...) Expand 10 before | Expand all | Expand 10 after
2605 } 2628 }
2606 2629
2607 void MenuController::SetInitialHotTrackedView( 2630 void MenuController::SetInitialHotTrackedView(
2608 MenuItemView* item, 2631 MenuItemView* item,
2609 SelectionIncrementDirectionType direction) { 2632 SelectionIncrementDirectionType direction) {
2610 if (!item) 2633 if (!item)
2611 return; 2634 return;
2612 SetSelection(item, SELECTION_DEFAULT); 2635 SetSelection(item, SELECTION_DEFAULT);
2613 View* hot_view = 2636 View* hot_view =
2614 GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN); 2637 GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN);
2615 CustomButton* hot_button = CustomButton::AsCustomButton(hot_view); 2638 SetHotTrackedButton(CustomButton::AsCustomButton(hot_view));
2639 }
2640
2641 void MenuController::SetHotTrackedButton(CustomButton* hot_button) {
2642 if (hot_button == hot_button_)
2643 return;
2644 if (hot_button_)
2645 hot_button_->SetHotTracked(false);
2646 hot_button_ = hot_button;
2616 if (hot_button) 2647 if (hot_button)
2617 hot_button->SetHotTracked(true); 2648 hot_button->SetHotTracked(true);
2618 } 2649 }
2619 2650
2620 } // namespace views 2651 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/menu/menu_controller.h ('k') | ui/views/controls/menu/menu_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698