| 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/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| (...skipping 1008 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1019 drag_in_progress_ = false; | 1019 drag_in_progress_ = false; |
| 1020 // During a drag, mouse events are processed directly by the widget, and not | 1020 // During a drag, mouse events are processed directly by the widget, and not |
| 1021 // sent to the MenuController. At drag completion, reset pressed state and | 1021 // sent to the MenuController. At drag completion, reset pressed state and |
| 1022 // the event target. | 1022 // the event target. |
| 1023 current_mouse_pressed_state_ = 0; | 1023 current_mouse_pressed_state_ = 0; |
| 1024 current_mouse_event_target_ = nullptr; | 1024 current_mouse_event_target_ = nullptr; |
| 1025 | 1025 |
| 1026 // Only attempt to close if the MenuHost said to. | 1026 // Only attempt to close if the MenuHost said to. |
| 1027 if (should_close) { | 1027 if (should_close) { |
| 1028 if (showing_) { | 1028 if (showing_) { |
| 1029 // Close showing widgets. | 1029 // During a drag operation there are several ways in which this can be |
| 1030 // canceled and deleted. Verify that this is still active before closing |
| 1031 // the widgets. |
| 1030 if (GetActiveInstance() == this) { | 1032 if (GetActiveInstance() == this) { |
| 1033 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 1031 CloseAllNestedMenus(); | 1034 CloseAllNestedMenus(); |
| 1032 Cancel(EXIT_ALL); | 1035 Cancel(EXIT_ALL); |
| 1036 // The above may have deleted us. If not perform a full shutdown. |
| 1037 if (!this_ref) |
| 1038 return; |
| 1039 ExitAsyncRun(); |
| 1033 } | 1040 } |
| 1034 // The above may have deleted us. If not perform a full shutdown. | |
| 1035 if (GetActiveInstance() == this) | |
| 1036 ExitAsyncRun(); | |
| 1037 } else if (exit_type_ == EXIT_ALL) { | 1041 } else if (exit_type_ == EXIT_ALL) { |
| 1038 // We may have been canceled during the drag. If so we still need to fully | 1042 // We may have been canceled during the drag. If so we still need to fully |
| 1039 // shutdown. | 1043 // shutdown. |
| 1040 ExitAsyncRun(); | 1044 ExitAsyncRun(); |
| 1041 } | 1045 } |
| 1042 } | 1046 } |
| 1043 } | 1047 } |
| 1044 | 1048 |
| 1045 ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent( | 1049 ui::PostDispatchAction MenuController::OnWillDispatchKeyEvent( |
| 1046 ui::KeyEvent* event) { | 1050 ui::KeyEvent* event) { |
| 1047 if (exit_type() == EXIT_ALL || exit_type() == EXIT_DESTROYED) { | 1051 if (exit_type() == EXIT_ALL || exit_type() == EXIT_DESTROYED) { |
| 1048 // If the event has arrived after the menu's exit type had changed but | 1052 // If the event has arrived after the menu's exit type had changed but |
| 1049 // before its message loop terminated, the event will continue its normal | 1053 // before its message loop terminated, the event will continue its normal |
| 1050 // propagation for the following reason: | 1054 // propagation for the following reason: |
| 1051 // If the user accepts a menu item in a nested menu, the menu item action is | 1055 // If the user accepts a menu item in a nested menu, the menu item action is |
| 1052 // run after the base::RunLoop for the innermost menu has quit but before | 1056 // run after the base::RunLoop for the innermost menu has quit but before |
| 1053 // the base::RunLoop for the outermost menu has quit. If the menu item | 1057 // the base::RunLoop for the outermost menu has quit. If the menu item |
| 1054 // action starts a base::RunLoop, the outermost menu's base::RunLoop will | 1058 // action starts a base::RunLoop, the outermost menu's base::RunLoop will |
| 1055 // not quit till the action's base::RunLoop ends. IDC_BOOKMARK_BAR_OPEN_ALL | 1059 // not quit till the action's base::RunLoop ends. IDC_BOOKMARK_BAR_OPEN_ALL |
| 1056 // sometimes opens a modal dialog. The modal dialog starts a base::RunLoop | 1060 // sometimes opens a modal dialog. The modal dialog starts a base::RunLoop |
| 1057 // and keeps the base::RunLoop running for the duration of its lifetime. | 1061 // and keeps the base::RunLoop running for the duration of its lifetime. |
| 1058 TerminateNestedMessageLoopIfNecessary(); | 1062 TerminateNestedMessageLoopIfNecessary(); |
| 1059 return ui::POST_DISPATCH_PERFORM_DEFAULT; | 1063 return ui::POST_DISPATCH_PERFORM_DEFAULT; |
| 1060 } | 1064 } |
| 1061 | 1065 |
| 1062 event->StopPropagation(); | 1066 event->StopPropagation(); |
| 1063 | 1067 |
| 1064 if (event->type() == ui::ET_KEY_PRESSED) { | 1068 if (event->type() == ui::ET_KEY_PRESSED) { |
| 1069 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 1065 OnKeyDown(event->key_code()); | 1070 OnKeyDown(event->key_code()); |
| 1066 // Menu controller might have been deleted. | 1071 // Key events can lead to this being deleted. |
| 1067 if (!GetActiveInstance()) | 1072 if (!this_ref) |
| 1068 return ui::POST_DISPATCH_NONE; | 1073 return ui::POST_DISPATCH_NONE; |
| 1069 | 1074 |
| 1070 // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For | 1075 // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. For |
| 1071 // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic. | 1076 // example Ctrl+<T> is an accelerator, but <T> only is a mnemonic. |
| 1072 const int kKeyFlagsMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN; | 1077 const int kKeyFlagsMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN; |
| 1073 const int flags = event->flags(); | 1078 const int flags = event->flags(); |
| 1074 if (exit_type() == EXIT_NONE && (flags & kKeyFlagsMask) == 0) { | 1079 if (exit_type() == EXIT_NONE && (flags & kKeyFlagsMask) == 0) { |
| 1075 base::char16 c = event->GetCharacter(); | 1080 base::char16 c = event->GetCharacter(); |
| 1076 SelectByChar(c); | 1081 SelectByChar(c); |
| 1077 // Menu controller might have been deleted. | 1082 // SelectByChar can lead to this being deleted. |
| 1078 if (!GetActiveInstance()) | 1083 if (!this_ref) |
| 1079 return ui::POST_DISPATCH_NONE; | 1084 return ui::POST_DISPATCH_NONE; |
| 1080 } | 1085 } |
| 1081 } | 1086 } |
| 1082 | 1087 |
| 1083 if (!TerminateNestedMessageLoopIfNecessary()) { | 1088 if (!TerminateNestedMessageLoopIfNecessary()) { |
| 1084 ui::Accelerator accelerator(*event); | 1089 ui::Accelerator accelerator(*event); |
| 1085 ViewsDelegate::ProcessMenuAcceleratorResult result = | 1090 ViewsDelegate::ProcessMenuAcceleratorResult result = |
| 1086 ViewsDelegate::GetInstance()->ProcessAcceleratorWhileMenuShowing( | 1091 ViewsDelegate::GetInstance()->ProcessAcceleratorWhileMenuShowing( |
| 1087 accelerator); | 1092 accelerator); |
| 1088 if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU) | 1093 if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1105 void MenuController::OnWidgetDestroying(Widget* widget) { | 1110 void MenuController::OnWidgetDestroying(Widget* widget) { |
| 1106 DCHECK_EQ(owner_, widget); | 1111 DCHECK_EQ(owner_, widget); |
| 1107 owner_->RemoveObserver(this); | 1112 owner_->RemoveObserver(this); |
| 1108 owner_ = NULL; | 1113 owner_ = NULL; |
| 1109 } | 1114 } |
| 1110 | 1115 |
| 1111 bool MenuController::IsCancelAllTimerRunningForTest() { | 1116 bool MenuController::IsCancelAllTimerRunningForTest() { |
| 1112 return cancel_all_timer_.IsRunning(); | 1117 return cancel_all_timer_.IsRunning(); |
| 1113 } | 1118 } |
| 1114 | 1119 |
| 1120 void MenuController::ClearStateForTest() { |
| 1121 state_ = State(); |
| 1122 pending_state_ = State(); |
| 1123 } |
| 1124 |
| 1115 // static | 1125 // static |
| 1116 void MenuController::TurnOffMenuSelectionHoldForTest() { | 1126 void MenuController::TurnOffMenuSelectionHoldForTest() { |
| 1117 menu_selection_hold_time_ms = -1; | 1127 menu_selection_hold_time_ms = -1; |
| 1118 } | 1128 } |
| 1119 | 1129 |
| 1120 void MenuController::SetSelection(MenuItemView* menu_item, | 1130 void MenuController::SetSelection(MenuItemView* menu_item, |
| 1121 int selection_types) { | 1131 int selection_types) { |
| 1122 size_t paths_differ_at = 0; | 1132 size_t paths_differ_at = 0; |
| 1123 std::vector<MenuItemView*> current_path; | 1133 std::vector<MenuItemView*> current_path; |
| 1124 std::vector<MenuItemView*> new_path; | 1134 std::vector<MenuItemView*> new_path; |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1242 item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG); | 1252 item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG); |
| 1243 | 1253 |
| 1244 OSExchangeData data; | 1254 OSExchangeData data; |
| 1245 item->GetDelegate()->WriteDragData(item, &data); | 1255 item->GetDelegate()->WriteDragData(item, &data); |
| 1246 drag_utils::SetDragImageOnDataObject(*canvas, | 1256 drag_utils::SetDragImageOnDataObject(*canvas, |
| 1247 press_loc.OffsetFromOrigin(), | 1257 press_loc.OffsetFromOrigin(), |
| 1248 &data); | 1258 &data); |
| 1249 StopScrolling(); | 1259 StopScrolling(); |
| 1250 int drag_ops = item->GetDelegate()->GetDragOperations(item); | 1260 int drag_ops = item->GetDelegate()->GetDragOperations(item); |
| 1251 did_initiate_drag_ = true; | 1261 did_initiate_drag_ = true; |
| 1262 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 1252 // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. | 1263 // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. |
| 1253 item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops, | 1264 item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops, |
| 1254 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); | 1265 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); |
| 1255 // MenuController may have been deleted if |async_run_| so check for an active | 1266 // MenuController may have been deleted if |async_run_| so check before |
| 1256 // instance before accessing member variables. | 1267 // accessing member variables. |
| 1257 if (GetActiveInstance() == this) | 1268 if (this_ref) |
| 1258 did_initiate_drag_ = false; | 1269 did_initiate_drag_ = false; |
| 1259 } | 1270 } |
| 1260 | 1271 |
| 1261 void MenuController::OnKeyDown(ui::KeyboardCode key_code) { | 1272 void MenuController::OnKeyDown(ui::KeyboardCode key_code) { |
| 1262 // Do not process while performing drag-and-drop | 1273 // Do not process while performing drag-and-drop |
| 1263 if (!blocking_run_) | 1274 if (!blocking_run_) |
| 1264 return; | 1275 return; |
| 1265 | 1276 |
| 1266 switch (key_code) { | 1277 switch (key_code) { |
| 1267 case ui::VKEY_UP: | 1278 case ui::VKEY_UP: |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1408 | 1419 |
| 1409 void MenuController::RunMessageLoop() { | 1420 void MenuController::RunMessageLoop() { |
| 1410 message_loop_->Run(); | 1421 message_loop_->Run(); |
| 1411 } | 1422 } |
| 1412 | 1423 |
| 1413 bool MenuController::SendAcceleratorToHotTrackedView() { | 1424 bool MenuController::SendAcceleratorToHotTrackedView() { |
| 1414 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); | 1425 CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); |
| 1415 if (!hot_view) | 1426 if (!hot_view) |
| 1416 return false; | 1427 return false; |
| 1417 | 1428 |
| 1429 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 1418 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); | 1430 ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE); |
| 1419 hot_view->AcceleratorPressed(accelerator); | 1431 hot_view->AcceleratorPressed(accelerator); |
| 1420 // An accelerator may have canceled the menu after activation. | 1432 // An accelerator may have canceled the menu after activation. |
| 1421 if (GetActiveInstance()) { | 1433 if (this_ref) { |
| 1422 CustomButton* button = static_cast<CustomButton*>(hot_view); | 1434 CustomButton* button = static_cast<CustomButton*>(hot_view); |
| 1423 SetHotTrackedButton(button); | 1435 SetHotTrackedButton(button); |
| 1424 } | 1436 } |
| 1425 return true; | 1437 return true; |
| 1426 } | 1438 } |
| 1427 | 1439 |
| 1428 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, | 1440 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, |
| 1429 MenuAnchorPosition position, | 1441 MenuAnchorPosition position, |
| 1430 bool context_menu) { | 1442 bool context_menu) { |
| 1431 pending_state_.context_menu = context_menu; | 1443 pending_state_.context_menu = context_menu; |
| (...skipping 945 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2377 gfx::NativeView native_view = source->GetWidget()->GetNativeView(); | 2389 gfx::NativeView native_view = source->GetWidget()->GetNativeView(); |
| 2378 gfx::NativeWindow window = nullptr; | 2390 gfx::NativeWindow window = nullptr; |
| 2379 if (native_view) { | 2391 if (native_view) { |
| 2380 display::Screen* screen = display::Screen::GetScreen(); | 2392 display::Screen* screen = display::Screen::GetScreen(); |
| 2381 window = screen->GetWindowAtScreenPoint(screen_loc); | 2393 window = screen->GetWindowAtScreenPoint(screen_loc); |
| 2382 } | 2394 } |
| 2383 #endif | 2395 #endif |
| 2384 | 2396 |
| 2385 #if defined(OS_WIN) | 2397 #if defined(OS_WIN) |
| 2386 if (event->IsMouseEvent() || event->IsTouchEvent()) { | 2398 if (event->IsMouseEvent() || event->IsTouchEvent()) { |
| 2399 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 2387 bool async_run = async_run_; | 2400 bool async_run = async_run_; |
| 2388 if (state_.item) { | 2401 if (state_.item) { |
| 2389 state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); | 2402 state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); |
| 2403 // We're going to close and we own the event capture. We need to repost |
| 2404 // the event, otherwise the window the user clicked on won't get the |
| 2405 // event. |
| 2390 RepostEventImpl(event, screen_loc, native_view, window); | 2406 RepostEventImpl(event, screen_loc, native_view, window); |
| 2391 } else { | 2407 } else { |
| 2392 // We some times get an event after closing all the menus. Ignore it. Make | 2408 // We some times get an event after closing all the menus. Ignore it. Make |
| 2393 // sure the menu is in fact not visible. If the menu is visible, then | 2409 // sure the menu is in fact not visible. If the menu is visible, then |
| 2394 // we're in a bad state where we think the menu isn't visibile but it is. | 2410 // we're in a bad state where we think the menu isn't visibile but it is. |
| 2395 DCHECK(!source->GetWidget()->IsVisible()); | 2411 DCHECK(!source->GetWidget()->IsVisible()); |
| 2396 } | 2412 } |
| 2397 | 2413 |
| 2398 // We're going to close and we own the event capture. We need to repost the | 2414 // Reposting the event may have deleted this, if so exit. |
| 2399 // event, otherwise the window the user clicked on won't get the event. | 2415 if (!this_ref) { |
| 2400 // RepostEvent(source, event, screen_loc, native_view, window); | |
| 2401 // MenuController may have been deleted if |async_run_| so check for an | |
| 2402 // active | |
| 2403 // instance before accessing member variables. | |
| 2404 if (!GetActiveInstance()) { | |
| 2405 DCHECK(async_run); | 2416 DCHECK(async_run); |
| 2406 return; | 2417 return; |
| 2407 } | 2418 } |
| 2408 } | 2419 } |
| 2409 #endif | 2420 #endif |
| 2410 | 2421 |
| 2411 // Determine target to see if a complete or partial close of the menu should | 2422 // Determine target to see if a complete or partial close of the menu should |
| 2412 // occur. | 2423 // occur. |
| 2413 ExitType exit_type = EXIT_ALL; | 2424 ExitType exit_type = EXIT_ALL; |
| 2414 if (!menu_stack_.empty()) { | 2425 if (!menu_stack_.empty()) { |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2572 void MenuController::ExitAsyncRun() { | 2583 void MenuController::ExitAsyncRun() { |
| 2573 if (!async_run_) | 2584 if (!async_run_) |
| 2574 return; | 2585 return; |
| 2575 bool nested = delegate_stack_.size() > 1; | 2586 bool nested = delegate_stack_.size() > 1; |
| 2576 // ExitMenuRun unwinds nested delegates | 2587 // ExitMenuRun unwinds nested delegates |
| 2577 internal::MenuControllerDelegate* delegate = delegate_; | 2588 internal::MenuControllerDelegate* delegate = delegate_; |
| 2578 // MenuController may have been deleted when releasing ViewsDelegate ref. | 2589 // MenuController may have been deleted when releasing ViewsDelegate ref. |
| 2579 // However as |delegate| can outlive this, it must still be notified of the | 2590 // However as |delegate| can outlive this, it must still be notified of the |
| 2580 // menu closing so that it can perform teardown. | 2591 // menu closing so that it can perform teardown. |
| 2581 int accept_event_flags = accept_event_flags_; | 2592 int accept_event_flags = accept_event_flags_; |
| 2593 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 2582 MenuItemView* result = ExitMenuRun(); | 2594 MenuItemView* result = ExitMenuRun(); |
| 2583 delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE, | 2595 delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE, |
| 2584 result, accept_event_flags); | 2596 result, accept_event_flags); |
| 2585 // MenuController may have been deleted by |delegate|. | 2597 // |delegate| may have deleted this. |
| 2586 if (GetActiveInstance() && nested && exit_type_ == EXIT_ALL) | 2598 if (this_ref && nested && exit_type_ == EXIT_ALL) |
| 2587 ExitAsyncRun(); | 2599 ExitAsyncRun(); |
| 2588 } | 2600 } |
| 2589 | 2601 |
| 2590 MenuItemView* MenuController::ExitMenuRun() { | 2602 MenuItemView* MenuController::ExitMenuRun() { |
| 2603 base::WeakPtr<MenuController> this_ref = AsWeakPtr(); |
| 2604 |
| 2591 // Release the lock which prevents Chrome from shutting down while the menu is | 2605 // Release the lock which prevents Chrome from shutting down while the menu is |
| 2592 // showing. | 2606 // showing. |
| 2593 if (async_run_ && ViewsDelegate::GetInstance()) | 2607 if (async_run_ && ViewsDelegate::GetInstance()) |
| 2594 ViewsDelegate::GetInstance()->ReleaseRef(); | 2608 ViewsDelegate::GetInstance()->ReleaseRef(); |
| 2595 | 2609 |
| 2596 // Releasing the lock can result in Chrome shutting down, deleting this. | 2610 // Releasing the lock can result in Chrome shutting down, deleting this. |
| 2597 if (!GetActiveInstance()) | 2611 if (!this_ref) |
| 2598 return nullptr; | 2612 return nullptr; |
| 2599 | 2613 |
| 2600 // Close any open menus. | 2614 // Close any open menus. |
| 2601 SetSelection(nullptr, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); | 2615 SetSelection(nullptr, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); |
| 2602 | 2616 |
| 2603 #if defined(OS_WIN) | 2617 #if defined(OS_WIN) |
| 2604 // On Windows, if we select the menu item by touch and if the window at the | 2618 // On Windows, if we select the menu item by touch and if the window at the |
| 2605 // location is another window on the same thread, that window gets a | 2619 // location is another window on the same thread, that window gets a |
| 2606 // WM_MOUSEACTIVATE message and ends up activating itself, which is not | 2620 // WM_MOUSEACTIVATE message and ends up activating itself, which is not |
| 2607 // correct. We workaround this by setting a property on the window at the | 2621 // correct. We workaround this by setting a property on the window at the |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2732 if (hot_button_) | 2746 if (hot_button_) |
| 2733 hot_button_->SetHotTracked(false); | 2747 hot_button_->SetHotTracked(false); |
| 2734 hot_button_ = hot_button; | 2748 hot_button_ = hot_button; |
| 2735 if (hot_button) { | 2749 if (hot_button) { |
| 2736 hot_button->SetHotTracked(true); | 2750 hot_button->SetHotTracked(true); |
| 2737 hot_button->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); | 2751 hot_button->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); |
| 2738 } | 2752 } |
| 2739 } | 2753 } |
| 2740 | 2754 |
| 2741 } // namespace views | 2755 } // namespace views |
| OLD | NEW |