app_menu_model_adapter.cc 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/app_menu/app_menu_model_adapter.h"
  5. #include "ash/app_menu/notification_menu_controller.h"
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/public/cpp/shelf_model.h"
  8. #include "base/metrics/histogram_functions.h"
  9. #include "base/metrics/histogram_macros.h"
  10. #include "ui/accessibility/ax_enums.mojom.h"
  11. #include "ui/base/models/simple_menu_model.h"
  12. #include "ui/views/controls/menu/menu_item_view.h"
  13. #include "ui/views/controls/menu/menu_model_adapter.h"
  14. #include "ui/views/controls/menu/menu_runner.h"
  15. #include "ui/views/controls/menu/submenu_view.h"
  16. #include "ui/views/widget/widget.h"
  17. namespace ash {
  18. namespace {
  19. // The UMA histogram that logs the commands which are executed on non-app
  20. // context menus.
  21. constexpr char kNonAppContextMenuExecuteCommand[] =
  22. "Apps.ContextMenuExecuteCommand.NotFromApp";
  23. constexpr char kNonAppContextMenuExecuteCommandInTablet[] =
  24. "Apps.ContextMenuExecuteCommand.NotFromApp.TabletMode";
  25. constexpr char kNonAppContextMenuExecuteCommandInClamshell[] =
  26. "Apps.ContextMenuExecuteCommand.NotFromApp.ClamshellMode";
  27. // The UMA histogram that logs the commands which are executed on app context
  28. // menus.
  29. constexpr char kAppContextMenuExecuteCommand[] =
  30. "Apps.ContextMenuExecuteCommand.FromApp";
  31. constexpr char kAppContextMenuExecuteCommandInTablet[] =
  32. "Apps.ContextMenuExecuteCommand.FromApp.TabletMode";
  33. constexpr char kAppContextMenuExecuteCommandInClamshell[] =
  34. "Apps.ContextMenuExecuteCommand.FromApp.ClamshellMode";
  35. } // namespace
  36. AppMenuModelAdapter::AppMenuModelAdapter(
  37. const std::string& app_id,
  38. std::unique_ptr<ui::SimpleMenuModel> model,
  39. views::Widget* widget_owner,
  40. ui::MenuSourceType source_type,
  41. base::OnceClosure on_menu_closed_callback,
  42. bool is_tablet_mode)
  43. : views::MenuModelAdapter(model.get()),
  44. app_id_(app_id),
  45. model_(std::move(model)),
  46. widget_owner_(widget_owner),
  47. source_type_(source_type),
  48. on_menu_closed_callback_(std::move(on_menu_closed_callback)),
  49. is_tablet_mode_(is_tablet_mode) {}
  50. AppMenuModelAdapter::~AppMenuModelAdapter() = default;
  51. void AppMenuModelAdapter::Run(const gfx::Rect& menu_anchor_rect,
  52. views::MenuAnchorPosition menu_anchor_position,
  53. int run_types) {
  54. DCHECK(!root_);
  55. DCHECK(model_);
  56. menu_open_time_ = base::TimeTicks::Now();
  57. root_ = CreateMenu();
  58. if (ash::features::IsNotificationsInContextMenuEnabled()) {
  59. notification_menu_controller_ =
  60. std::make_unique<NotificationMenuController>(app_id_, root_, this);
  61. }
  62. menu_runner_ = std::make_unique<views::MenuRunner>(root_, run_types);
  63. menu_runner_->RunMenuAt(widget_owner_, nullptr /* MenuButtonController */,
  64. menu_anchor_rect, menu_anchor_position, source_type_);
  65. }
  66. bool AppMenuModelAdapter::IsShowingMenu() const {
  67. return menu_runner_ && menu_runner_->IsRunning();
  68. }
  69. void AppMenuModelAdapter::Cancel() {
  70. if (!IsShowingMenu())
  71. return;
  72. menu_runner_->Cancel();
  73. }
  74. int AppMenuModelAdapter::GetCommandIdForHistograms(int command_id) {
  75. return command_id;
  76. }
  77. base::TimeTicks AppMenuModelAdapter::GetClosingEventTime() {
  78. DCHECK(menu_runner_);
  79. return menu_runner_->closing_event_time();
  80. }
  81. views::Widget* AppMenuModelAdapter::GetSubmenuWidget() {
  82. if (root_ && root_->GetSubmenu())
  83. return root_->GetSubmenu()->GetWidget();
  84. return nullptr;
  85. }
  86. void AppMenuModelAdapter::ExecuteCommand(int id, int mouse_event_flags) {
  87. // Note that the command execution may cause this to get deleted - for
  88. // example, for search result menus, the command could open an app window
  89. // causing the app list search to get cleared, destroying non-zero state
  90. // search results.
  91. RecordExecuteCommandHistogram(GetCommandIdForHistograms(id));
  92. views::MenuModelAdapter::ExecuteCommand(id, mouse_event_flags);
  93. }
  94. void AppMenuModelAdapter::OnMenuClosed(views::MenuItemView* menu) {
  95. DCHECK_NE(base::TimeTicks(), menu_open_time_);
  96. RecordHistogramOnMenuClosed();
  97. // No |widget_owner_| in tests.
  98. if (widget_owner_ && widget_owner_->GetRootView()) {
  99. widget_owner_->GetRootView()->NotifyAccessibilityEvent(
  100. ax::mojom::Event::kMenuEnd,
  101. /*send_native_event=*/true);
  102. }
  103. if (on_menu_closed_callback_)
  104. std::move(on_menu_closed_callback_).Run();
  105. }
  106. void AppMenuModelAdapter::RecordExecuteCommandHistogram(int command_id) {
  107. const bool is_not_from_app = app_id().empty();
  108. base::UmaHistogramSparse(is_not_from_app ? kNonAppContextMenuExecuteCommand
  109. : kAppContextMenuExecuteCommand,
  110. command_id);
  111. if (is_tablet_mode_) {
  112. base::UmaHistogramSparse(is_not_from_app
  113. ? kNonAppContextMenuExecuteCommandInTablet
  114. : kAppContextMenuExecuteCommandInTablet,
  115. command_id);
  116. } else {
  117. base::UmaHistogramSparse(is_not_from_app
  118. ? kNonAppContextMenuExecuteCommandInClamshell
  119. : kAppContextMenuExecuteCommandInClamshell,
  120. command_id);
  121. }
  122. }
  123. } // namespace ash