// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/app_list/app_list_presenter_event_filter.h" #include #include #include "ash/app_list/app_list_controller_impl.h" #include "ash/app_list/app_list_presenter_impl.h" #include "ash/app_list/app_list_util.h" #include "ash/app_list/views/app_list_main_view.h" #include "ash/app_list/views/search_box_view.h" #include "ash/bubble/bubble_utils.h" #include "ash/shelf/back_button.h" #include "ash/shelf/home_button.h" #include "ash/shelf/hotseat_widget.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_navigation_widget.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" #include "ash/system/status_area_widget.h" #include "base/check.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/aura/window.h" #include "ui/events/event.h" #include "ui/gfx/geometry/point.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" namespace ash { AppListPresenterEventFilter::AppListPresenterEventFilter( AppListControllerImpl* controller, AppListPresenterImpl* presenter, AppListView* view) : controller_(controller), presenter_(presenter), view_(view) { DCHECK(controller_); DCHECK(presenter_); DCHECK(view_); Shell::Get()->AddPreTargetHandler(this); } AppListPresenterEventFilter::~AppListPresenterEventFilter() { Shell::Get()->RemovePreTargetHandler(this); } void AppListPresenterEventFilter::OnMouseEvent(ui::MouseEvent* event) { // Moving the mouse shouldn't hide focus rings. if (event->IsAnyButton()) controller_->SetKeyboardTraversalMode(false); if (event->type() == ui::ET_MOUSE_PRESSED) ProcessLocatedEvent(event); } void AppListPresenterEventFilter::OnGestureEvent(ui::GestureEvent* event) { controller_->SetKeyboardTraversalMode(false); // Checks tap types instead of ui::ET_TOUCH_PRESSED so that swipes on the // shelf do not close the launcher. https://crbug.com/750274 if (event->type() == ui::ET_GESTURE_TAP || event->type() == ui::ET_GESTURE_TWO_FINGER_TAP || event->type() == ui::ET_GESTURE_LONG_PRESS) { ProcessLocatedEvent(event); } } void AppListPresenterEventFilter::OnKeyEvent(ui::KeyEvent* event) { // If keyboard traversal is already engaged, no-op. if (controller_->KeyboardTraversalEngaged()) return; // If the home launcher is not shown in tablet mode, ignore events. if (Shell::Get()->IsInTabletMode() && !controller_->IsVisible()) return; // Don't absorb the first event for the search box while it is open. if (view_->search_box_view()->is_search_box_active()) return; // Don't absorb the first event when renaming folder. if (view_->IsFolderBeingRenamed()) return; // Arrow keys or Tab will engage the traversal mode. if ((IsUnhandledArrowKeyEvent(*event) || event->key_code() == ui::VKEY_TAB)) { // Handle the first arrow key event to just show the focus rings (if not // showing Assistant). Don't absorb the first event when showing Assistant. if (!view_->IsShowingEmbeddedAssistantUI()) event->SetHandled(); controller_->SetKeyboardTraversalMode(true); } } void AppListPresenterEventFilter::ProcessLocatedEvent(ui::LocatedEvent* event) { // Check the general rules for closing bubbles. if (!bubble_utils::ShouldCloseBubbleForEvent(*event)) return; aura::Window* target = static_cast(event->target()); if (!target) return; // If the event happened on the home button's widget, it'll get handled by the // button. Shelf* shelf = Shelf::ForWindow(target); HomeButton* home_button = shelf->navigation_widget()->GetHomeButton(); if (home_button && home_button->GetWidget() && target == home_button->GetWidget()->GetNativeWindow()) { gfx::Point location_in_home_button = event->location(); views::View::ConvertPointFromWidget(home_button, &location_in_home_button); if (home_button->HitTestPoint(location_in_home_button)) return; } // If the event happened on the back button, it'll get handled by the // button. BackButton* back_button = shelf->navigation_widget()->GetBackButton(); if (back_button && back_button->GetWidget() && target == back_button->GetWidget()->GetNativeWindow()) { gfx::Point location_in_back_button = event->location(); views::View::ConvertPointFromWidget(back_button, &location_in_back_button); if (back_button->HitTestPoint(location_in_back_button)) return; } aura::Window* window = view_->GetWidget()->GetNativeView()->parent(); if (window->Contains(target)) return; // Try to close an open folder window: return if an open folder view was // closed successfully. if (presenter_->HandleCloseOpenFolder()) return; if (!Shell::Get()->IsInTabletMode()) { // Do not dismiss the app list if the event is targeting shelf area // containing app icons. if (target == shelf->hotseat_widget()->GetNativeWindow() && shelf->hotseat_widget()->EventTargetsShelfView(*event)) { return; } // Don't dismiss the auto-hide shelf if event happened in status area. Then // the event can still be propagated. const aura::Window* status_window = shelf->shelf_widget()->status_area_widget()->GetNativeWindow(); if (status_window && status_window->Contains(target)) return; // Record the current AppListViewState to be used later for metrics. The // AppListViewState will change on app launch, so this will record the // AppListViewState before the app was launched. controller_->RecordAppListState(); presenter_->Dismiss(event->time_stamp()); } } } // namespace ash