chromium/ash/app_list/views/ghost_image_view.cc

// 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/app_list/views/ghost_image_view.h"

#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/style/dark_light_mode_controller_impl.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"

namespace ash {

namespace {

constexpr int kGhostCircleStrokeWidth = 2;
constexpr int kGhostColorOpacity = 0x4D;  // 30% opacity.
constexpr base::TimeDelta kGhostFadeInOutLength = base::Milliseconds(180);
constexpr gfx::Tween::Type kGhostTween = gfx::Tween::FAST_OUT_SLOW_IN;

}  // namespace

GhostImageView::GhostImageView(GridIndex index)
    : is_hiding_(false), index_(index) {}

GhostImageView::~GhostImageView() {
  StopObservingImplicitAnimations();
}

void GhostImageView::Init(const gfx::Rect& drop_target_bounds,
                          int grid_focus_corner_radius) {
  corner_radius_ = grid_focus_corner_radius;

  SetPaintToLayer();
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetOpacity(0.0f);
  SetBoundsRect(drop_target_bounds);
}

void GhostImageView::FadeOut() {
  if (is_hiding_) {
    return;
  }
  is_hiding_ = true;
  DoAnimation(true /* fade out */);
}

void GhostImageView::FadeIn() {
  DoAnimation(false /* fade in */);
}

void GhostImageView::SetTransitionOffset(
    const gfx::Vector2d& transition_offset) {
  SetPosition(bounds().origin() + transition_offset);
}

void GhostImageView::DoAnimation(bool hide) {
  ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
  animation.SetTransitionDuration(kGhostFadeInOutLength);
  animation.SetTweenType(kGhostTween);

  if (hide) {
    animation.AddObserver(this);
    animation.SetPreemptionStrategy(
        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    layer()->SetOpacity(0.0f);
    return;
  }
  layer()->SetOpacity(1.0f);
}

void GhostImageView::OnPaint(gfx::Canvas* canvas) {
  cc::PaintFlags flags;
  flags.setAntiAlias(true);
  flags.setColor(GetColorProvider()->GetColor(cros_tokens::kCrosSysOutline));

  flags.setAlphaf(kGhostColorOpacity / 255.0f);
  flags.setStyle(cc::PaintFlags::kStroke_Style);
  flags.setStrokeWidth(kGhostCircleStrokeWidth);
  gfx::Rect bounds = GetContentsBounds();
  bounds.Inset(gfx::Insets(kGhostCircleStrokeWidth / 2));
  canvas->DrawRoundRect(gfx::RectF(bounds), corner_radius_, flags);
}

void GhostImageView::OnImplicitAnimationsCompleted() {
  // Delete this GhostImageView when the fade out animation is done.
  delete this;
}

BEGIN_METADATA(GhostImageView)
END_METADATA

}  // namespace ash