tray_event_filter.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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
  3. // found in the LICENSE file.
  4. #include "ash/system/tray/tray_event_filter.h"
  5. #include "ash/capture_mode/capture_mode_util.h"
  6. #include "ash/public/cpp/shell_window_ids.h"
  7. #include "ash/root_window_controller.h"
  8. #include "ash/shelf/shelf.h"
  9. #include "ash/shell.h"
  10. #include "ash/system/message_center/ash_message_popup_collection.h"
  11. #include "ash/system/message_center/unified_message_center_bubble.h"
  12. #include "ash/system/status_area_widget.h"
  13. #include "ash/system/tray/tray_background_view.h"
  14. #include "ash/system/tray/tray_bubble_base.h"
  15. #include "ash/system/unified/date_tray.h"
  16. #include "ash/system/unified/unified_system_tray.h"
  17. #include "ash/system/unified/unified_system_tray_bubble.h"
  18. #include "ash/wm/container_finder.h"
  19. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  20. #include "ui/aura/window.h"
  21. #include "ui/display/screen.h"
  22. #include "ui/gfx/native_widget_types.h"
  23. #include "ui/views/widget/widget.h"
  24. namespace ash {
  25. TrayEventFilter::TrayEventFilter() = default;
  26. TrayEventFilter::~TrayEventFilter() {
  27. DCHECK(bubbles_.empty());
  28. }
  29. void TrayEventFilter::AddBubble(TrayBubbleBase* bubble) {
  30. bool was_empty = bubbles_.empty();
  31. bubbles_.insert(bubble);
  32. if (was_empty && !bubbles_.empty())
  33. Shell::Get()->AddPreTargetHandler(this);
  34. }
  35. void TrayEventFilter::RemoveBubble(TrayBubbleBase* bubble) {
  36. bubbles_.erase(bubble);
  37. if (bubbles_.empty())
  38. Shell::Get()->RemovePreTargetHandler(this);
  39. }
  40. void TrayEventFilter::OnMouseEvent(ui::MouseEvent* event) {
  41. if (event->type() == ui::ET_MOUSE_PRESSED)
  42. ProcessPressedEvent(*event);
  43. }
  44. void TrayEventFilter::OnTouchEvent(ui::TouchEvent* event) {
  45. if (event->type() == ui::ET_TOUCH_PRESSED)
  46. ProcessPressedEvent(*event);
  47. }
  48. void TrayEventFilter::OnGestureEvent(ui::GestureEvent* event) {
  49. if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN)
  50. ProcessPressedEvent(*event);
  51. }
  52. void TrayEventFilter::ProcessPressedEvent(const ui::LocatedEvent& event) {
  53. // Users in a capture session may be trying to capture tray bubble(s).
  54. if (capture_mode_util::IsCaptureModeActive())
  55. return;
  56. // The hit target window for the virtual keyboard isn't the same as its
  57. // views::Widget.
  58. aura::Window* target = static_cast<aura::Window*>(event.target());
  59. const views::Widget* target_widget =
  60. views::Widget::GetTopLevelWidgetForNativeView(target);
  61. const aura::Window* container =
  62. target ? GetContainerForWindow(target) : nullptr;
  63. // TODO(https://crbug.com/1208083): Replace some of this logic with
  64. // bubble_utils::ShouldCloseBubbleForEvent().
  65. if (target && container) {
  66. const int container_id = container->GetId();
  67. // Don't process events that occurred inside an embedded menu, for example
  68. // the right-click menu in a popup notification.
  69. if (container_id == kShellWindowId_MenuContainer)
  70. return;
  71. // Don't process events that occurred inside a popup notification
  72. // from message center.
  73. if (container_id == kShellWindowId_ShelfContainer &&
  74. target->GetType() == aura::client::WINDOW_TYPE_POPUP &&
  75. target_widget->GetName() ==
  76. AshMessagePopupCollection::kMessagePopupWidgetName) {
  77. return;
  78. }
  79. // Don't process events that occurred inside a virtual keyboard.
  80. if (container_id == kShellWindowId_VirtualKeyboardContainer)
  81. return;
  82. }
  83. std::set<TrayBackgroundView*> trays;
  84. // Check the boundary for all bubbles, and do not handle the event if it
  85. // happens inside of any of those bubbles.
  86. const gfx::Point screen_location =
  87. event.target() ? event.target()->GetScreenLocation(event)
  88. : event.root_location();
  89. for (const TrayBubbleBase* bubble : bubbles_) {
  90. const views::Widget* bubble_widget = bubble->GetBubbleWidget();
  91. if (!bubble_widget)
  92. continue;
  93. gfx::Rect bounds = bubble_widget->GetWindowBoundsInScreen();
  94. bounds.Inset(bubble->GetBubbleView()->GetBorderInsets());
  95. // System tray can be dragged to show the bubble if it is in tablet mode.
  96. // During the drag, the bubble's logical bounds can extend outside of the
  97. // work area, but its visual bounds are only within the work area. Restrict
  98. // |bounds| so that events located outside the bubble's visual bounds are
  99. // treated as outside of the bubble.
  100. int bubble_container_id =
  101. GetContainerForWindow(bubble_widget->GetNativeWindow())->GetId();
  102. if (Shell::Get()->tablet_mode_controller()->InTabletMode() &&
  103. bubble_container_id == kShellWindowId_SettingBubbleContainer) {
  104. bounds.Intersect(bubble_widget->GetWorkAreaBoundsInScreen());
  105. }
  106. // The system tray and message center are separate bubbles but they need
  107. // to stay open together. We need to make sure to check if a click falls
  108. // with in both their bounds and not close them both in this case.
  109. if (bubble_container_id == kShellWindowId_SettingBubbleContainer) {
  110. int64_t display_id = display::Screen::GetScreen()
  111. ->GetDisplayNearestPoint(screen_location)
  112. .id();
  113. StatusAreaWidget* status_area =
  114. Shell::GetRootWindowControllerWithDisplayId(display_id)
  115. ->shelf()
  116. ->GetStatusAreaWidget();
  117. UnifiedSystemTray* tray = status_area->unified_system_tray();
  118. // When Quick Settings bubble is opened and the date tray is clicked, the
  119. // bubble should not be closed since it will transition to show calendar.
  120. if (features::IsCalendarViewEnabled() &&
  121. status_area->date_tray()->GetBoundsInScreen().Contains(
  122. screen_location)) {
  123. continue;
  124. }
  125. TrayBubbleBase* system_tray_bubble = tray->bubble();
  126. if (tray->IsBubbleShown() && system_tray_bubble != bubble) {
  127. bounds.Union(
  128. system_tray_bubble->GetBubbleWidget()->GetWindowBoundsInScreen());
  129. } else if (tray->IsMessageCenterBubbleShown()) {
  130. TrayBubbleBase* message_center_bubble = tray->message_center_bubble();
  131. bounds.Union(message_center_bubble->GetBubbleWidget()
  132. ->GetWindowBoundsInScreen());
  133. }
  134. }
  135. if (bounds.Contains(screen_location))
  136. continue;
  137. if (bubble->GetTray()) {
  138. // Maybe close the parent tray if the user drags on it. Otherwise, let the
  139. // tray logic handle the event and determine show/hide behavior if the
  140. // user clicks on the parent tray.
  141. bounds = bubble->GetTray()->GetBoundsInScreen();
  142. if (bubble->GetTray()->GetVisible() && bounds.Contains(screen_location) &&
  143. event.type() != ui::ET_GESTURE_SCROLL_BEGIN)
  144. continue;
  145. }
  146. trays.insert(bubble->GetTray());
  147. }
  148. // Close all bubbles other than the one that the user clicked on.
  149. for (TrayBackgroundView* tray_background_view : trays)
  150. tray_background_view->ClickedOutsideBubble();
  151. }
  152. } // namespace ash