| // 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 |