| // 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 <memory> |
| #include <utility> |
| |
| #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 showing Assistant. |
| if (view_->IsShowingEmbeddedAssistantUI()) |
| 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. |
| 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<aura::Window*>(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)) { |
| auto shelf_visibility_lock = |
| std::make_unique<ShelfLayoutManager::ScopedVisibilityLock>( |
| shelf->shelf_layout_manager()); |
| |
| // Use a task runner to delete the |shelf_visibility_lock| and update the |
| // shelf visibility after the current event has been handled by the shelf. |
| // This is important for the case where dismissing the app list might hide |
| // the shelf, which would stop the shelf from handling the event. |
| // TODO(crbug.com/1186479): Investigate whether there is a better way to |
| // do this, instead of using a task runner here. |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon( |
| FROM_HERE, std::move(shelf_visibility_lock)); |
| } |
| |
| // 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 |