chromium/ash/wm/overview/scoped_overview_wallpaper_clipper.cc

// Copyright 2024 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/scoped_overview_wallpaper_clipper.h"

#include <optional>

#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/overview/overview_constants.h"
#include "ash/wm/overview/overview_grid.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/notreached.h"
#include "ui/compositor/layer.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/animation/animation_builder.h"
#include "ui/wm/core/coordinate_conversion.h"

namespace ash {

namespace {

struct AnimationSettings {
  base::TimeDelta duration;
  gfx::Tween::Type tween_type;
};

constexpr AnimationSettings kEnterInformedRestoreAnimationSettings{
    .duration = base::Milliseconds(800),
    .tween_type = gfx::Tween::EASE_IN_OUT_EMPHASIZED};

constexpr AnimationSettings kEnterOverviewAnimationSettings{
    .duration = base::Milliseconds(500),
    .tween_type = gfx::Tween::EASE_IN_OUT_EMPHASIZED};

constexpr AnimationSettings kShowBirchBarInOverviewAnimationSettings{
    .duration = base::Milliseconds(200),
    .tween_type = gfx::Tween::FAST_OUT_LINEAR_IN};

constexpr AnimationSettings kShowBirchBarByUserAnimationSettings{
    .duration = base::Milliseconds(250),
    .tween_type = gfx::Tween::ACCEL_LIN_DECEL_100_3};

constexpr AnimationSettings kHideBirchBarByUserAnimationSettings{
    .duration = base::Milliseconds(100),
    .tween_type = gfx::Tween::LINEAR};

constexpr AnimationSettings kRestoreAnimationSettings{
    .duration = base::Milliseconds(200),
    .tween_type = gfx::Tween::ACCEL_LIN_DECEL_100};

constexpr AnimationSettings kRelayoutBirchBarAnimationSettings{
    .duration = base::Milliseconds(100),
    .tween_type = gfx::Tween::LINEAR};

void RemoveWallpaperClipper(
    WallpaperWidgetController* wallpaper_widget_controller) {
  if (wallpaper_widget_controller->wallpaper_underlay_layer()) {
    wallpaper_widget_controller->wallpaper_underlay_layer()->SetVisible(false);
  }

  if (auto* wallpaper_view_layer =
          wallpaper_widget_controller->wallpaper_view()->layer()) {
    wallpaper_view_layer->SetClipRect(gfx::Rect());
  }
}

}  // namespace

ScopedOverviewWallpaperClipper::ScopedOverviewWallpaperClipper(
    OverviewGrid* overview_grid,
    AnimationType animation_type)
    : overview_grid_(overview_grid) {
  aura::Window* root_window = overview_grid_->root_window();
  WallpaperWidgetController* wallpaper_widget_controller =
      RootWindowController::ForWindow(root_window)
          ->wallpaper_widget_controller();

  // Stop animating the wallpaper view layer. There may be an ongoing clip
  // and/or rounded corner animation from the last overview exit. This ensures
  // the callback completes before we make changes to the wallpaper again.
  auto* wallpaper_view_layer =
      wallpaper_widget_controller->wallpaper_view()->layer();
  wallpaper_view_layer->GetAnimator()->StopAnimating();

  auto* wallpaper_underlay_layer =
      wallpaper_widget_controller->wallpaper_underlay_layer();
  // TODO(http://b/333952534): Remove this check once `wallpaper_underlay_layer`
  // is always created.
  CHECK(wallpaper_underlay_layer);
  wallpaper_underlay_layer->SetVisible(true);

  RefreshWallpaperClipBounds(animation_type, base::DoNothing());
}

ScopedOverviewWallpaperClipper::~ScopedOverviewWallpaperClipper() {
  // Switching to tablet mode will force mirroring displays which would destroy
  // non primary root windows. The wallpaper widget controller would already be
  // destroyed at this point.
  if (auto* wallpaper_widget_controller =
          RootWindowController::ForWindow(overview_grid_->root_window())
              ->wallpaper_widget_controller()) {
    RefreshWallpaperClipBounds(
        AnimationType::kRestore,
        base::BindOnce(&RemoveWallpaperClipper,
                       base::Unretained(wallpaper_widget_controller)));
  }
}

void ScopedOverviewWallpaperClipper::RefreshWallpaperClipBounds(
    AnimationType animation_type,
    base::OnceClosure animation_end_callback) {
  aura::Window* root_window = overview_grid_->root_window();
  WallpaperWidgetController* wallpaper_widget_controller =
      RootWindowController::ForWindow(root_window)
          ->wallpaper_widget_controller();
  auto* wallpaper_view_layer =
      wallpaper_widget_controller->wallpaper_view()->layer();

  gfx::Rect target_clip_rect = animation_type == AnimationType::kRestore
                                   ? display::Screen::GetScreen()
                                         ->GetDisplayNearestWindow(root_window)
                                         .bounds()
                                   : overview_grid_->GetWallpaperClipBounds();
  // Convert the clip bounds to the parent's coordinates, as layer bounds are
  // always relative to their parent.
  wm::ConvertRectFromScreen(root_window, &target_clip_rect);

  // If the animation type is none, directly set the clip rect and rounded
  // corners if the target properties are changed and run the callback.
  if (animation_type == AnimationType::kNone) {
    if (target_clip_rect != wallpaper_view_layer->GetTargetClipRect()) {
      wallpaper_view_layer->SetClipRect(target_clip_rect);
    }

    if (kWallpaperClipRoundedCornerRadii !=
        wallpaper_view_layer->GetTargetRoundedCornerRadius()) {
      wallpaper_view_layer->SetRoundedCornerRadius(
          kWallpaperClipRoundedCornerRadii);
    }

    if (animation_end_callback) {
      std::move(animation_end_callback).Run();
    }
    return;
  }

  // Otherwise, perform animation according to the given type.
  AnimationSettings animation_settings;
  std::optional<gfx::RoundedCornersF> rounded_corners;

  // Set animation settings according to animation type.
  switch (animation_type) {
    case AnimationType::kEnterInformedRestore:
      animation_settings = kEnterInformedRestoreAnimationSettings;
      rounded_corners = kWallpaperClipRoundedCornerRadii;
      break;
    case AnimationType::kEnterOverview:
      animation_settings = kEnterOverviewAnimationSettings;
      rounded_corners = kWallpaperClipRoundedCornerRadii;
      break;
    case AnimationType::kShowBirchBarInOverview:
      animation_settings = kShowBirchBarInOverviewAnimationSettings;
      break;
    case AnimationType::kShowBirchBarByUser:
      animation_settings = kShowBirchBarByUserAnimationSettings;
      break;
    case AnimationType::kHideBirchBarByUser:
      animation_settings = kHideBirchBarByUserAnimationSettings;
      break;
    case AnimationType::kRelayoutBirchBar:
      animation_settings = kRelayoutBirchBarAnimationSettings;
      break;
    case AnimationType::kRestore:
      animation_settings = kRestoreAnimationSettings;
      rounded_corners = gfx::RoundedCornersF();
      break;
    case AnimationType::kNone:
      NOTREACHED();
  }

  views::AnimationBuilder animation_builder;
  if (animation_end_callback) {
    animation_builder.OnEnded(std::move(animation_end_callback));
  }

  views::AnimationSequenceBlock& animation_sequence =
      animation_builder
          .SetPreemptionStrategy(
              ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
          .Once()
          .SetDuration(animation_settings.duration)
          .SetClipRect(wallpaper_view_layer, target_clip_rect,
                       animation_settings.tween_type);

  if (rounded_corners) {
    animation_sequence.SetRoundedCorners(wallpaper_view_layer, *rounded_corners,
                                         animation_settings.tween_type);
  }
}

}  // namespace ash