| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/overview/overview_grid_event_handler.h" |
| |
| #include "ash/app_list/app_list_controller_impl.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/shell.h" |
| #include "ash/wallpaper/views/wallpaper_view.h" |
| #include "ash/wallpaper/views/wallpaper_widget_controller.h" |
| #include "ash/wm/gestures/wm_fling_handler.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_grid.h" |
| #include "ash/wm/overview/overview_utils.h" |
| #include "ash/wm/splitview/split_view_controller.h" |
| #include "base/functional/bind.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/geometry/vector2d_f.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Do not bother moving the grid until a series of scrolls has reached this |
| // threshold. |
| constexpr float kScrollOffsetThresholdDp = 1.f; |
| |
| WallpaperView* GetWallpaperViewForRoot(const aura::Window* root_window) { |
| auto* wallpaper_widget_controller = |
| RootWindowController::ForWindow(root_window) |
| ->wallpaper_widget_controller(); |
| if (!wallpaper_widget_controller) |
| return nullptr; |
| return wallpaper_widget_controller->wallpaper_view(); |
| } |
| |
| } // namespace |
| |
| OverviewGridEventHandler::OverviewGridEventHandler(OverviewGrid* grid) |
| : grid_(grid), overview_session_(grid_->overview_session()) { |
| DCHECK(overview_session_); |
| auto* wallpaper_view = GetWallpaperViewForRoot(grid_->root_window()); |
| if (wallpaper_view) |
| wallpaper_view->AddPreTargetHandler(this); |
| } |
| |
| OverviewGridEventHandler::~OverviewGridEventHandler() { |
| OnFlingEnd(); |
| grid_->EndScroll(); |
| |
| auto* wallpaper_view = GetWallpaperViewForRoot(grid_->root_window()); |
| if (wallpaper_view) |
| wallpaper_view->RemovePreTargetHandler(this); |
| } |
| |
| void OverviewGridEventHandler::OnMouseEvent(ui::MouseEvent* event) { |
| // The following can only happen if a user is dragging a window with touch and |
| // then they move the mouse to click on the wallpaper. This is an extreme edge |
| // case, so just exit overview. Note that this is done here instead of on |
| // release like usual, because pressing the mouse while dragging sends out a |
| // ui::GESTURE_END_EVENT which may cause a bad state. |
| if (event->type() == ui::EventType::kMousePressed && |
| !overview_session_->CanProcessEvent()) { |
| OverviewController::Get()->EndOverview( |
| OverviewEndAction::kClickingOutsideWindowsInOverview); |
| event->StopPropagation(); |
| event->SetHandled(); |
| return; |
| } |
| |
| if (event->type() == ui::EventType::kMouseReleased) { |
| HandleClickOrTap(event); |
| } |
| } |
| |
| void OverviewGridEventHandler::OnGestureEvent(ui::GestureEvent* event) { |
| if (!overview_session_->CanProcessEvent()) { |
| event->StopPropagation(); |
| event->SetHandled(); |
| return; |
| } |
| |
| // TODO(crbug.com/1341128): Enable context menu via long-press in library page |
| // `SavedDeskLibraryView` will take over gesture event if it's active. When |
| // it's `EventType::kGestureTap`, here it does not set event to handled, and |
| // thus `HandleClickOrTap()` would be executed from |
| // `SavedDeskLibraryView::OnLocatedEvent()`. |
| if (grid_->IsShowingSavedDeskLibrary()) { |
| return; |
| } |
| |
| if (event->type() == ui::EventType::kGestureTap) { |
| HandleClickOrTap(event); |
| return; |
| } |
| |
| // The following events are for scrolling the overview scroll layout, which is |
| // tablet only. |
| if (!display::Screen::GetScreen()->InTabletMode()) { |
| return; |
| } |
| |
| switch (event->type()) { |
| case ui::EventType::kScrollFlingStart: { |
| HandleFlingScroll(event); |
| event->SetHandled(); |
| break; |
| } |
| case ui::EventType::kGestureScrollBegin: { |
| scroll_offset_x_cumulative_ = 0.f; |
| OnFlingEnd(); |
| grid_->StartScroll(); |
| event->SetHandled(); |
| break; |
| } |
| case ui::EventType::kGestureScrollUpdate: { |
| // Only forward the scrolls to grid once they have exceeded the threshold. |
| const float scroll_offset_x = event->details().scroll_x(); |
| scroll_offset_x_cumulative_ += scroll_offset_x; |
| if (std::abs(scroll_offset_x_cumulative_) > kScrollOffsetThresholdDp) { |
| grid_->UpdateScrollOffset(scroll_offset_x_cumulative_); |
| scroll_offset_x_cumulative_ = 0.f; |
| } |
| event->SetHandled(); |
| break; |
| } |
| case ui::EventType::kGestureScrollEnd: { |
| grid_->EndScroll(); |
| event->SetHandled(); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void OverviewGridEventHandler::HandleClickOrTap(ui::Event* event) { |
| CHECK_EQ(ui::EP_PRETARGET, event->phase()); |
| |
| // If the user is renaming a desk or saved desk, rather than closing overview |
| // the focused name view should lose focus. |
| if (grid_->IsDeskNameBeingModified() || |
| grid_->IsSavedDeskNameBeingModified()) { |
| grid_->CommitNameChanges(); |
| event->StopPropagation(); |
| return; |
| } |
| |
| if (display::Screen::GetScreen()->InTabletMode()) { |
| aura::Window* window = static_cast<views::View*>(event->target()) |
| ->GetWidget() |
| ->GetNativeWindow(); |
| |
| // In tablet mode, clicking on tapping on the wallpaper background will |
| // head back to home launcher screen if not in split view (in which case |
| // the event should be ignored). |
| if (!SplitViewController::Get(window)->InSplitViewMode()) { |
| int64_t display_id = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window).id(); |
| Shell::Get()->app_list_controller()->GoHome(display_id); |
| } |
| } else { |
| OverviewController::Get()->EndOverview( |
| OverviewEndAction::kClickingOutsideWindowsInOverview); |
| } |
| event->StopPropagation(); |
| } |
| |
| void OverviewGridEventHandler::HandleFlingScroll(ui::GestureEvent* event) { |
| const gfx::Vector2dF initial_fling_velocity(event->details().velocity_x(), |
| event->details().velocity_y()); |
| fling_handler_ = std::make_unique<WmFlingHandler>( |
| initial_fling_velocity, grid_->root_window(), |
| base::BindRepeating(&OverviewGridEventHandler::OnFlingStep, |
| base::Unretained(this)), |
| base::BindRepeating(&OverviewGridEventHandler::OnFlingEnd, |
| base::Unretained(this))); |
| } |
| |
| bool OverviewGridEventHandler::OnFlingStep(float offset) { |
| // Updates `grid_` based on `offset`. |
| DCHECK(fling_handler_); |
| return grid_->UpdateScrollOffset(offset); |
| } |
| |
| void OverviewGridEventHandler::OnFlingEnd() { |
| if (!fling_handler_) |
| return; |
| |
| fling_handler_.reset(); |
| grid_->EndScroll(); |
| } |
| |
| } // namespace ash |