OLD | NEW |
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/run_loop.h" | 9 #include "base/run_loop.h" |
10 #include "base/time.h" | 10 #include "base/time.h" |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 static const int kCloseOnExitTime = 1200; | 50 static const int kCloseOnExitTime = 1200; |
51 | 51 |
52 // If a context menu is invoked by touch, we shift the menu by this offset so | 52 // If a context menu is invoked by touch, we shift the menu by this offset so |
53 // that the finger does not obscure the menu. | 53 // that the finger does not obscure the menu. |
54 static const int kCenteredContextMenuYOffset = -15; | 54 static const int kCenteredContextMenuYOffset = -15; |
55 | 55 |
56 namespace views { | 56 namespace views { |
57 | 57 |
58 namespace { | 58 namespace { |
59 | 59 |
| 60 // When showing context menu on mouse down, the user might accidentally select |
| 61 // the menu item on the subsequent mouse up. To prevent this, we add the |
| 62 // following delay before the user is able to select an item. |
| 63 static int context_menu_selection_hold_time_ms = 200; |
| 64 |
60 // The spacing offset for the bubble tip. | 65 // The spacing offset for the bubble tip. |
61 const int kBubbleTipSizeLeftRight = 12; | 66 const int kBubbleTipSizeLeftRight = 12; |
62 const int kBubbleTipSizeTopBottom = 11; | 67 const int kBubbleTipSizeTopBottom = 11; |
63 | 68 |
64 // Returns true if the mnemonic of |menu| matches key. | 69 // Returns true if the mnemonic of |menu| matches key. |
65 bool MatchesMnemonic(MenuItemView* menu, char16 key) { | 70 bool MatchesMnemonic(MenuItemView* menu, char16 key) { |
66 return menu->GetMnemonic() == key; | 71 return menu->GetMnemonic() == key; |
67 } | 72 } |
68 | 73 |
69 // Returns true if |menu| doesn't have a mnemonic and first character of the its | 74 // Returns true if |menu| doesn't have a mnemonic and first character of the its |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 MenuButton* button, | 284 MenuButton* button, |
280 MenuItemView* root, | 285 MenuItemView* root, |
281 const gfx::Rect& bounds, | 286 const gfx::Rect& bounds, |
282 MenuItemView::AnchorPosition position, | 287 MenuItemView::AnchorPosition position, |
283 bool context_menu, | 288 bool context_menu, |
284 int* result_event_flags) { | 289 int* result_event_flags) { |
285 exit_type_ = EXIT_NONE; | 290 exit_type_ = EXIT_NONE; |
286 possible_drag_ = false; | 291 possible_drag_ = false; |
287 drag_in_progress_ = false; | 292 drag_in_progress_ = false; |
288 closing_event_time_ = base::TimeDelta(); | 293 closing_event_time_ = base::TimeDelta(); |
| 294 menu_start_time_ = base::TimeTicks::Now(); |
| 295 |
| 296 // If we are shown on mouse press, we will eat the subsequent mouse down and |
| 297 // the parent widget will not be able to reset its state (it might have mouse |
| 298 // capture from the mouse down). So we clear its state here. |
| 299 if (parent && parent->GetRootView()) |
| 300 parent->GetRootView()->SetMouseHandler(NULL); |
289 | 301 |
290 bool nested_menu = showing_; | 302 bool nested_menu = showing_; |
291 if (showing_) { | 303 if (showing_) { |
292 // Only support nesting of blocking_run menus, nesting of | 304 // Only support nesting of blocking_run menus, nesting of |
293 // blocking/non-blocking shouldn't be needed. | 305 // blocking/non-blocking shouldn't be needed. |
294 DCHECK(blocking_run_); | 306 DCHECK(blocking_run_); |
295 | 307 |
296 // We're already showing, push the current state. | 308 // We're already showing, push the current state. |
297 menu_stack_.push_back(state_); | 309 menu_stack_.push_back(state_); |
298 | 310 |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 return; | 501 return; |
490 } | 502 } |
491 if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu( | 503 if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu( |
492 part.menu->GetCommand(), event)) { | 504 part.menu->GetCommand(), event)) { |
493 part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(), | 505 part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(), |
494 event.flags()); | 506 event.flags()); |
495 return; | 507 return; |
496 } | 508 } |
497 if (!part.menu->NonIconChildViewsCount() && | 509 if (!part.menu->NonIconChildViewsCount() && |
498 part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) { | 510 part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) { |
499 Accept(part.menu, event.flags()); | 511 int64 time_since_menu_start = |
| 512 (base::TimeTicks::Now() - menu_start_time_).InMilliseconds(); |
| 513 if (!state_.context_menu || !View::ShouldShowContextMenuOnMousePress() || |
| 514 time_since_menu_start > context_menu_selection_hold_time_ms) |
| 515 Accept(part.menu, event.flags()); |
500 return; | 516 return; |
501 } | 517 } |
502 } else if (part.type == MenuPart::MENU_ITEM) { | 518 } else if (part.type == MenuPart::MENU_ITEM) { |
503 // User either clicked on empty space, or a menu that has children. | 519 // User either clicked on empty space, or a menu that has children. |
504 SetSelection(part.menu ? part.menu : state_.item, | 520 SetSelection(part.menu ? part.menu : state_.item, |
505 SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); | 521 SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); |
506 } | 522 } |
507 SendMouseCaptureLostToActiveView(); | 523 SendMouseCaptureLostToActiveView(); |
508 } | 524 } |
509 | 525 |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
725 HandleMouseLocation(submenu, point); | 741 HandleMouseLocation(submenu, point); |
726 } | 742 } |
727 } | 743 } |
728 | 744 |
729 void MenuController::OnWidgetDestroying(Widget* widget) { | 745 void MenuController::OnWidgetDestroying(Widget* widget) { |
730 DCHECK_EQ(owner_, widget); | 746 DCHECK_EQ(owner_, widget); |
731 owner_->RemoveObserver(this); | 747 owner_->RemoveObserver(this); |
732 owner_ = NULL; | 748 owner_ = NULL; |
733 } | 749 } |
734 | 750 |
| 751 // static |
| 752 void MenuController::TurnOffContextMenuSelectionHoldForTest() { |
| 753 context_menu_selection_hold_time_ms = -1; |
| 754 } |
| 755 |
735 void MenuController::SetSelection(MenuItemView* menu_item, | 756 void MenuController::SetSelection(MenuItemView* menu_item, |
736 int selection_types) { | 757 int selection_types) { |
737 size_t paths_differ_at = 0; | 758 size_t paths_differ_at = 0; |
738 std::vector<MenuItemView*> current_path; | 759 std::vector<MenuItemView*> current_path; |
739 std::vector<MenuItemView*> new_path; | 760 std::vector<MenuItemView*> new_path; |
740 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, ¤t_path, | 761 BuildPathsAndCalculateDiff(pending_state_.item, menu_item, ¤t_path, |
741 &new_path, &paths_differ_at); | 762 &new_path, &paths_differ_at); |
742 | 763 |
743 size_t current_size = current_path.size(); | 764 size_t current_size = current_path.size(); |
744 size_t new_size = new_path.size(); | 765 size_t new_size = new_path.size(); |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1086 possible_drag_(false), | 1107 possible_drag_(false), |
1087 drag_in_progress_(false), | 1108 drag_in_progress_(false), |
1088 valid_drop_coordinates_(false), | 1109 valid_drop_coordinates_(false), |
1089 last_drop_operation_(MenuDelegate::DROP_UNKNOWN), | 1110 last_drop_operation_(MenuDelegate::DROP_UNKNOWN), |
1090 showing_submenu_(false), | 1111 showing_submenu_(false), |
1091 menu_button_(NULL), | 1112 menu_button_(NULL), |
1092 active_mouse_view_(NULL), | 1113 active_mouse_view_(NULL), |
1093 delegate_(delegate), | 1114 delegate_(delegate), |
1094 message_loop_depth_(0), | 1115 message_loop_depth_(0), |
1095 menu_config_(theme), | 1116 menu_config_(theme), |
1096 closing_event_time_(base::TimeDelta()) { | 1117 closing_event_time_(base::TimeDelta()), |
| 1118 menu_start_time_(base::TimeTicks()) { |
1097 active_instance_ = this; | 1119 active_instance_ = this; |
1098 } | 1120 } |
1099 | 1121 |
1100 MenuController::~MenuController() { | 1122 MenuController::~MenuController() { |
1101 DCHECK(!showing_); | 1123 DCHECK(!showing_); |
1102 if (owner_) | 1124 if (owner_) |
1103 owner_->RemoveObserver(this); | 1125 owner_->RemoveObserver(this); |
1104 if (active_instance_ == this) | 1126 if (active_instance_ == this) |
1105 active_instance_ = NULL; | 1127 active_instance_ = NULL; |
1106 StopShowTimer(); | 1128 StopShowTimer(); |
(...skipping 1146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2253 (!pending_state_.item->HasSubmenu() || | 2275 (!pending_state_.item->HasSubmenu() || |
2254 !pending_state_.item->GetSubmenu()->IsShowing())) { | 2276 !pending_state_.item->GetSubmenu()->IsShowing())) { |
2255 // On exit if the user hasn't selected an item with a submenu, move the | 2277 // On exit if the user hasn't selected an item with a submenu, move the |
2256 // selection back to the parent menu item. | 2278 // selection back to the parent menu item. |
2257 SetSelection(pending_state_.item->GetParentMenuItem(), | 2279 SetSelection(pending_state_.item->GetParentMenuItem(), |
2258 SELECTION_OPEN_SUBMENU); | 2280 SELECTION_OPEN_SUBMENU); |
2259 } | 2281 } |
2260 } | 2282 } |
2261 | 2283 |
2262 } // namespace views | 2284 } // namespace views |
OLD | NEW |