blob: 4b7eca9413b0fde6f1b5e5d380c93141aa6850ba [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 "ui/views/animation/animation_builder.h"
#include <algorithm>
#include <tuple>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "base/time/time.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/layer_owner.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/views/animation/animation_abort_handle.h"
#include "ui/views/animation/animation_key.h"
#include "ui/views/animation/animation_sequence_block.h"
namespace views {
AnimationBuilder::Observer::Observer() = default;
AnimationBuilder::Observer::~Observer() {
DCHECK(attached_to_sequence_)
<< "You must register callbacks and get abort handle before "
<< "creating a sequence block.";
if (abort_handle_)
abort_handle_->OnObserverDeleted();
base::RepeatingClosure& on_observer_deleted =
AnimationBuilder::GetObserverDeletedCallback();
if (on_observer_deleted)
on_observer_deleted.Run();
}
void AnimationBuilder::Observer::SetOnStarted(base::OnceClosure callback) {
DCHECK(!on_started_);
on_started_ = std::move(callback);
}
void AnimationBuilder::Observer::SetOnEnded(base::OnceClosure callback) {
DCHECK(!on_ended_);
on_ended_ = std::move(callback);
}
void AnimationBuilder::Observer::SetOnWillRepeat(
base::RepeatingClosure callback) {
DCHECK(!on_will_repeat_);
on_will_repeat_ = std::move(callback);
}
void AnimationBuilder::Observer::SetOnAborted(base::OnceClosure callback) {
DCHECK(!on_aborted_);
on_aborted_ = std::move(callback);
}
void AnimationBuilder::Observer::SetOnScheduled(base::OnceClosure callback) {
DCHECK(!on_scheduled_);
on_scheduled_ = std::move(callback);
}
void AnimationBuilder::Observer::OnLayerAnimationStarted(
ui::LayerAnimationSequence* sequence) {
if (abort_handle_ && abort_handle_->animation_state() ==
AnimationAbortHandle::AnimationState::kNotStarted) {
abort_handle_->OnAnimationStarted();
}
if (on_started_)
std::move(on_started_).Run();
}
void AnimationBuilder::Observer::SetAbortHandle(
AnimationAbortHandle* abort_handle) {
abort_handle_ = abort_handle;
}
void AnimationBuilder::Observer::OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) {
if (--sequences_to_run_ == 0) {
if (on_ended_)
std::move(on_ended_).Run();
if (abort_handle_ && abort_handle_->animation_state() ==
AnimationAbortHandle::AnimationState::kRunning)
abort_handle_->OnAnimationEnded();
}
}
void AnimationBuilder::Observer::OnLayerAnimationWillRepeat(
ui::LayerAnimationSequence* sequence) {
if (!on_will_repeat_)
return;
// First time through, initialize the repeat_map_ with the sequences.
if (repeat_map_.empty()) {
for (auto* seq : attached_sequences())
repeat_map_[seq] = 0;
}
// Only trigger the repeat callback on the last LayerAnimationSequence on
// which this observer is attached.
const int next_cycle = ++repeat_map_[sequence];
if (base::ranges::none_of(
repeat_map_, [next_cycle](int count) { return count < next_cycle; },
&RepeatMap::value_type::second)) {
on_will_repeat_.Run();
}
}
void AnimationBuilder::Observer::OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) {
if (on_aborted_)
std::move(on_aborted_).Run();
if (abort_handle_ && abort_handle_->animation_state() ==
AnimationAbortHandle::AnimationState::kRunning)
abort_handle_->OnAnimationEnded();
}
void AnimationBuilder::Observer::OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) {
if (on_scheduled_)
std::move(on_scheduled_).Run();
}
void AnimationBuilder::Observer::OnAttachedToSequence(
ui::LayerAnimationSequence* sequence) {
ui::LayerAnimationObserver::OnAttachedToSequence(sequence);
attached_to_sequence_ = true;
sequences_to_run_++;
}
void AnimationBuilder::Observer::OnDetachedFromSequence(
ui::LayerAnimationSequence* sequence) {
if (attached_sequences().empty())
delete this;
}
bool AnimationBuilder::Observer::RequiresNotificationWhenAnimatorDestroyed()
const {
return true;
}
struct AnimationBuilder::Value {
base::TimeDelta start;
// Save the original duration because the duration on the element can be a
// scaled version. The scale can potentially be zero.
base::TimeDelta original_duration;
std::unique_ptr<ui::LayerAnimationElement> element;
bool operator<(const Value& key) const {
// Animations with zero duration need to be ordered before animations with
// nonzero of the same start time to prevent the DCHECK from happening in
// TerminateSequence(). These animations don't count as overlapping
// properties.
auto element_properties = element->properties();
auto key_element_properties = key.element->properties();
return std::tie(start, original_duration, element_properties) <
std::tie(key.start, key.original_duration, key_element_properties);
}
};
AnimationBuilder::AnimationBuilder() = default;
AnimationBuilder::AnimationBuilder(AnimationBuilder&& rhs) = default;
AnimationBuilder& AnimationBuilder::operator=(AnimationBuilder&& rhs) = default;
AnimationBuilder::~AnimationBuilder() {
// Terminate `current_sequence_` to complete layer animation configuration.
current_sequence_.reset();
DCHECK(!next_animation_observer_)
<< "Callbacks were scheduled without creating a sequence block "
"afterwards. There are no animations to run these callbacks on.";
// The observer needs to outlive the AnimationBuilder and will manage its own
// lifetime. GetAttachedToSequence should not return false here. This is
// DCHECKed in the observer’s destructor.
if (animation_observer_ && animation_observer_->GetAttachedToSequence())
animation_observer_.release();
for (auto it = layer_animation_sequences_.begin();
it != layer_animation_sequences_.end();) {
auto* const target = it->first;
auto end_it = layer_animation_sequences_.upper_bound(target);
if (abort_handle_)
abort_handle_->AddLayer(target);
ui::ScopedLayerAnimationSettings settings(target->GetAnimator());
if (preemption_strategy_)
settings.SetPreemptionStrategy(preemption_strategy_.value());
std::vector<ui::LayerAnimationSequence*> sequences;
std::transform(it, end_it, std::back_inserter(sequences),
[](auto& it) { return it.second.release(); });
target->GetAnimator()->StartTogether(std::move(sequences));
it = end_it;
}
}
AnimationBuilder& AnimationBuilder::SetPreemptionStrategy(
ui::LayerAnimator::PreemptionStrategy preemption_strategy) {
preemption_strategy_ = preemption_strategy;
return *this;
}
AnimationBuilder& AnimationBuilder::OnStarted(base::OnceClosure callback) {
GetObserver()->SetOnStarted(std::move(callback));
return *this;
}
AnimationBuilder& AnimationBuilder::OnEnded(base::OnceClosure callback) {
GetObserver()->SetOnEnded(std::move(callback));
return *this;
}
AnimationBuilder& AnimationBuilder::OnWillRepeat(
base::RepeatingClosure callback) {
GetObserver()->SetOnWillRepeat(std::move(callback));
return *this;
}
AnimationBuilder& AnimationBuilder::OnAborted(base::OnceClosure callback) {
GetObserver()->SetOnAborted(std::move(callback));
return *this;
}
AnimationBuilder& AnimationBuilder::OnScheduled(base::OnceClosure callback) {
GetObserver()->SetOnScheduled(std::move(callback));
return *this;
}
AnimationSequenceBlock& AnimationBuilder::Once() {
return NewSequence(false);
}
AnimationSequenceBlock& AnimationBuilder::Repeatedly() {
return NewSequence(true);
}
void AnimationBuilder::AddLayerAnimationElement(
base::PassKey<AnimationSequenceBlock>,
AnimationKey key,
base::TimeDelta start,
base::TimeDelta original_duration,
std::unique_ptr<ui::LayerAnimationElement> element) {
auto& values = values_[key];
Value value = {start, original_duration, std::move(element)};
auto it = base::ranges::upper_bound(values, value);
values.insert(it, std::move(value));
}
std::unique_ptr<AnimationSequenceBlock> AnimationBuilder::SwapCurrentSequence(
base::PassKey<AnimationSequenceBlock>,
std::unique_ptr<AnimationSequenceBlock> new_sequence) {
auto old_sequence = std::move(current_sequence_);
current_sequence_ = std::move(new_sequence);
return old_sequence;
}
void AnimationBuilder::BlockEndedAt(base::PassKey<AnimationSequenceBlock>,
base::TimeDelta end) {
end_ = std::max(end_, end);
}
void AnimationBuilder::TerminateSequence(base::PassKey<AnimationSequenceBlock>,
bool repeating) {
for (auto& pair : values_) {
auto sequence = std::make_unique<ui::LayerAnimationSequence>();
sequence->set_is_repeating(repeating);
if (animation_observer_)
sequence->AddObserver(animation_observer_.get());
base::TimeDelta start;
ui::LayerAnimationElement::AnimatableProperties properties =
ui::LayerAnimationElement::UNKNOWN;
for (auto& value : pair.second) {
DCHECK_GE(value.start, start)
<< "Do not overlap animations of the same property on the same view.";
properties = value.element->properties();
if (value.start > start) {
sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
properties, value.start - start));
start = value.start;
}
start += value.original_duration;
sequence->AddElement(std::move(value.element));
}
if (start < end_) {
sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
properties, end_ - start));
}
layer_animation_sequences_.insert({pair.first.target, std::move(sequence)});
}
values_.clear();
}
AnimationSequenceBlock& AnimationBuilder::GetCurrentSequence() {
DCHECK(current_sequence_);
return *current_sequence_;
}
std::unique_ptr<AnimationAbortHandle> AnimationBuilder::GetAbortHandle() {
DCHECK(!abort_handle_) << "An abort handle is already created.";
abort_handle_ = new AnimationAbortHandle(GetObserver());
return base::WrapUnique(abort_handle_.get());
}
AnimationBuilder::Observer* AnimationBuilder::GetObserver() {
if (!next_animation_observer_)
next_animation_observer_ = std::make_unique<Observer>();
return next_animation_observer_.get();
}
// static
void AnimationBuilder::SetObserverDeletedCallbackForTesting(
base::RepeatingClosure deleted_closure) {
GetObserverDeletedCallback() = std::move(deleted_closure);
}
AnimationSequenceBlock& AnimationBuilder::NewSequence(bool repeating) {
// Each sequence should have its own observer.
// Ensure to terminate the current sequence block before touching the
// animation sequence observer so that the sequence observer is attached to
// the layer animation sequence.
if (current_sequence_)
current_sequence_.reset();
// The observer needs to outlive the AnimationBuilder and will manage its own
// lifetime. GetAttachedToSequence should not return false here. This is
// DCHECKed in the observer’s destructor.
if (animation_observer_ && animation_observer_->GetAttachedToSequence())
animation_observer_.release();
if (next_animation_observer_) {
animation_observer_ = std::move(next_animation_observer_);
next_animation_observer_.reset();
}
end_ = base::TimeDelta();
current_sequence_ = std::make_unique<AnimationSequenceBlock>(
base::PassKey<AnimationBuilder>(), this, base::TimeDelta(), repeating);
return *current_sequence_;
}
// static
base::RepeatingClosure& AnimationBuilder::GetObserverDeletedCallback() {
static base::NoDestructor<base::RepeatingClosure> on_observer_deleted;
return *on_observer_deleted;
}
} // namespace views