blob: 00d17e9e86bfc2f92d559e716027380c10662c24 [file] [log] [blame]
// Copyright 2021 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/utility/layer_copy_animator.h"
#include "ash/utility/layer_util.h"
#include "base/functional/bind.h"
#include "ui/aura/window.h"
#include "ui/base/class_property.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
DEFINE_UI_CLASS_PROPERTY_TYPE(ash::LayerCopyAnimator*)
namespace ash {
namespace {
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(LayerCopyAnimator,
kLayerCopyAnimatorKey,
nullptr)
// CopyOutputRequest's callback may be called on the different thread during
// shutdown, which results in the DCHECK failure in the weak ptr when
// referenced.
void MaybeLayerCopied(base::WeakPtr<LayerCopyAnimator> swc,
std::unique_ptr<ui::Layer> new_layer) {
if (!swc.MaybeValid())
return;
swc->OnLayerCopied(std::move(new_layer));
}
} // namespace
// static
LayerCopyAnimator* LayerCopyAnimator::Get(aura::Window* window) {
return window->GetProperty(kLayerCopyAnimatorKey);
}
LayerCopyAnimator::LayerCopyAnimator(aura::Window* window) : window_(window) {
window->SetProperty(kLayerCopyAnimatorKey, this);
observation_.Observe(window);
// Copy request will not copy NOT_DRAWN and the result may be smaller than
// requested layer. Create a transparent layer to cover the entire layer.
if (window_->layer()->type() == ui::LAYER_NOT_DRAWN) {
full_layer_.SetColor(SK_ColorTRANSPARENT);
full_layer_.SetBounds(gfx::Rect(window_->bounds().size()));
window_->layer()->Add(&full_layer_);
window_->layer()->StackAtBottom(&full_layer_);
}
window_->layer()->GetAnimator()->StopAnimating();
window_->layer()->SetOpacity(1.f);
CopyLayerContentToNewLayer(
window_->layer(),
base::BindOnce(&MaybeLayerCopied, weak_ptr_factory_.GetWeakPtr()));
}
LayerCopyAnimator::~LayerCopyAnimator() {
window_->layer()->SetOpacity(1.0f);
if (fake_sequence_)
NotifyWithFakeSequence(/*abort=*/true);
DCHECK(!observer_);
}
void LayerCopyAnimator::MaybeStartAnimation(
ui::LayerAnimationObserver* observer,
AnimationCallback callback) {
DCHECK(!animation_requested_);
observer_ = observer;
animation_callback_ = std::move(callback);
animation_requested_ = true;
if (fail_) {
FinishAndDelete(/*abort=*/true);
return;
}
if (copied_layer_) {
RunAnimation();
return;
}
if (observer_)
EnsureFakeSequence();
}
void LayerCopyAnimator::OnLayerCopied(std::unique_ptr<ui::Layer> new_layer) {
if (fail_)
return;
if (!new_layer)
fail_ = true;
copied_layer_ = std::move(new_layer);
if (animation_requested_ && !fail_)
RunAnimation();
}
void LayerCopyAnimator::OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) {
if (last_sequence_ == sequence)
window_->ClearProperty(kLayerCopyAnimatorKey);
}
void LayerCopyAnimator::OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) {
window_->ClearProperty(kLayerCopyAnimatorKey);
}
void LayerCopyAnimator::OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
fail_ = true;
if (copied_layer_)
copied_layer_->GetAnimator()->StopAnimating();
}
void LayerCopyAnimator::RunAnimation() {
copied_layer_->SetFillsBoundsOpaquely(false);
auto* parent_layer = window_->layer()->parent();
parent_layer->Add(copied_layer_.get());
parent_layer->StackAbove(copied_layer_.get(), window_->layer());
window_->layer()->SetOpacity(0.f);
std::move(animation_callback_).Run(copied_layer_.get(), observer_.get());
// Callback may not run animations, in which case, just end immediately.
if (!copied_layer_->GetAnimator()->is_animating()) {
FinishAndDelete(/*abort=*/false);
return;
}
if (fake_sequence_)
NotifyWithFakeSequence(/*abort=*/false);
observer_ = nullptr;
last_sequence_ = new ui::LayerAnimationSequence(
ui::LayerAnimationElement::CreateOpacityElement(1.0, base::TimeDelta()));
copied_layer_->GetAnimator()->ScheduleAnimation(last_sequence_);
copied_layer_->GetAnimator()->AddObserver(this);
}
void LayerCopyAnimator::FinishAndDelete(bool abort) {
if (observer_) {
EnsureFakeSequence();
NotifyWithFakeSequence(abort);
}
window_->ClearProperty(kLayerCopyAnimatorKey);
}
void LayerCopyAnimator::NotifyWithFakeSequence(bool abort) {
DCHECK(fake_sequence_);
if (abort)
observer_->OnLayerAnimationAborted(fake_sequence_.get());
else
observer_->OnLayerAnimationEnded(fake_sequence_.get());
fake_sequence_.reset();
observer_ = nullptr;
}
void LayerCopyAnimator::EnsureFakeSequence() {
if (fake_sequence_)
return;
fake_sequence_ = std::make_unique<ui::LayerAnimationSequence>(
ui::LayerAnimationElement::CreateOpacityElement(0.0, base::TimeDelta()));
fake_sequence_->AddObserver(observer_);
}
} // namespace ash