chromium/ash/app_list/views/apps_grid_view_test_api.cc

// Copyright 2012 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/app_list/views/apps_grid_view_test_api.h"

#include <memory>
#include <vector>

#include "ash/app_list/views/app_drag_icon_proxy.h"
#include "ash/app_list/views/app_list_item_view.h"
#include "ash/app_list/views/apps_grid_view.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "ui/aura/window.h"
#include "ui/compositor/test/layer_animation_stopped_waiter.h"
#include "ui/events/event.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"

namespace ash {
namespace test {

AppsGridViewTestApi::AppsGridViewTestApi(AppsGridView* view) : view_(view) {}

AppsGridViewTestApi::~AppsGridViewTestApi() = default;

views::View* AppsGridViewTestApi::GetViewAtModelIndex(int index) const {
  return view_->view_model_.view_at(index);
}

void AppsGridViewTestApi::LayoutToIdealBounds() {
  if (view_->reorder_timer_.IsRunning()) {
    view_->reorder_timer_.Stop();
    view_->OnReorderTimer();
  }
  view_->DeprecatedLayoutImmediately();
}

gfx::Rect AppsGridViewTestApi::GetItemTileRectOnCurrentPageAt(int row,
                                                              int col) const {
  int slot = row * (view_->cols()) + col;
  gfx::Rect bounds_in_ltr =
      view_->GetExpectedTileBounds(GridIndex(view_->GetSelectedPage(), slot));
  // `GetExpectedTileBounds()` returns expected bounds for item at provided grid
  // index in LTR UI. Make sure this method returns mirrored bounds in RTL UI.
  return view_->GetMirroredRect(bounds_in_ltr);
}

void AppsGridViewTestApi::PressItemAt(int index) {
  GetViewAtModelIndex(index)->OnKeyPressed(
      ui::KeyEvent(ui::EventType::kKeyPressed, ui::VKEY_RETURN, ui::EF_NONE));
}

size_t AppsGridViewTestApi::TilesPerPageInPagedGrid(int page) const {
  return *view_->TilesPerPage(page);
}

size_t AppsGridViewTestApi::TilesPerPageOr(int page,
                                           size_t default_value) const {
  return view_->TilesPerPage(page).value_or(default_value);
}

int AppsGridViewTestApi::AppsOnPage(int page) const {
  return view_->GetNumberOfItemsOnPage(page);
}

AppListItemView* AppsGridViewTestApi::GetViewAtIndex(GridIndex index) const {
  return view_->GetViewAtIndex(index);
}

AppListItemView* AppsGridViewTestApi::GetViewAtVisualIndex(int page,
                                                           int slot) const {
  return GetViewAtIndex(GridIndex(page, slot));
}

const std::string& AppsGridViewTestApi::GetNameAtVisualIndex(int page,
                                                             int slot) const {
  return GetViewAtVisualIndex(page, slot)->item()->name();
}

gfx::Rect AppsGridViewTestApi::GetItemTileRectAtVisualIndex(int page,
                                                            int slot) const {
  // `GetExpectedTileBounds()` returns expected bounds for item at provided grid
  // index in LTR UI. Make sure this method returns mirrored bounds in RTL UI.
  return view_->GetMirroredRect(
      view_->GetExpectedTileBounds(GridIndex(page, slot)));
}

void AppsGridViewTestApi::WaitForItemMoveAnimationDone() {
  ui::LayerAnimationStoppedWaiter animation_waiter;
  while (true) {
    bool found_animation = false;
    for (size_t i = 0; i < view_->view_model()->view_size(); i++) {
      auto* item_view = view_->view_model()->view_at(i);
      if (view_->IsAnimatingView(item_view)) {
        found_animation = true;
        animation_waiter.Wait(item_view->layer());
        break;
      }
    }

    if (!found_animation)
      break;
  }
}

void AppsGridViewTestApi::FireReorderTimerAndWaitForAnimationDone() {
  base::OneShotTimer* timer = &view_->reorder_timer_;
  if (timer->IsRunning())
    timer->FireNow();

  WaitForItemMoveAnimationDone();
}

void AppsGridViewTestApi::FireFolderItemReparentTimer() {
  view_->FireFolderItemReparentTimerForTest();
}

gfx::Rect AppsGridViewTestApi::GetDragIconBoundsInAppsGridView() {
  if (!view_->drag_icon_proxy_)
    return gfx::Rect();
  gfx::Rect icon_bounds_in_screen =
      view_->drag_icon_proxy_->GetBoundsInScreen();
  if (icon_bounds_in_screen.IsEmpty())
    return gfx::Rect();
  gfx::Point icon_origin = icon_bounds_in_screen.origin();
  views::View::ConvertPointFromScreen(view_, &icon_origin);
  return gfx::Rect(icon_origin, icon_bounds_in_screen.size());
}

ui::Layer* AppsGridViewTestApi::GetDragIconLayer() {
  if (!view_->drag_icon_proxy_)
    return nullptr;
  return view_->drag_icon_proxy_->GetImageLayerForTesting();
}

void AppsGridViewTestApi::ReorderItemByDragAndDrop(int source_index,
                                                   int target_index) {
  if (source_index == target_index)
    return;

  ui::test::EventGenerator event_generator(
      view_->GetWidget()->GetNativeView()->GetRootWindow());
  ash::AppListItemView* dragged_view =
      view_->view_model()->view_at(source_index);
  event_generator.MoveMouseTo(dragged_view->GetBoundsInScreen().CenterPoint());
  event_generator.PressLeftButton();
  dragged_view->FireMouseDragTimerForTest();

  // Calculate the move target location. If `source_index` is to the left of
  // `target_index`, the item should be moved to the right of the target slot
  // in order to trigger apps reorder; otherwise, the item should be moved to
  // the left.
  const gfx::Rect target_view_screen_bounds =
      view_->view_model()->view_at(target_index)->GetBoundsInScreen();
  constexpr int offset = 10;
  const int target_location_x =
      (source_index < target_index ? target_view_screen_bounds.right() + offset
                                   : target_view_screen_bounds.x() - offset);
  const gfx::Point target_move_location(
      target_location_x, target_view_screen_bounds.CenterPoint().y());
  event_generator.MoveMouseTo(target_move_location);
  FireReorderTimerAndWaitForAnimationDone();
  event_generator.ReleaseLeftButton();
}

}  // namespace test
}  // namespace ash