blob: b0f118754be56ba92582851368aff71f56163acb [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_sequence_block.h"
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include <variant>
#include "base/check.h"
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_owner.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/views/animation/animation_builder.h"
#include "ui/views/animation/animation_key.h"
namespace views {
using PassKey = base::PassKey<AnimationSequenceBlock>;
AnimationSequenceBlock::AnimationSequenceBlock(
base::PassKey<AnimationBuilder> builder_key,
AnimationBuilder* owner,
base::TimeDelta start,
bool repeating)
: builder_key_(builder_key),
owner_(owner),
start_(start),
repeating_(repeating) {}
AnimationSequenceBlock::~AnimationSequenceBlock() {
if (!finalized_) {
TerminateBlock();
owner_->TerminateSequence(PassKey(), repeating_);
}
}
AnimationSequenceBlock& AnimationSequenceBlock::SetDuration(
base::TimeDelta duration) {
DCHECK(!finalized_) << "Do not access old blocks after creating new ones.";
DCHECK(!duration_.has_value()) << "Duration may be set at most once.";
duration_ = duration;
return *this;
}
AnimationSequenceBlock& AnimationSequenceBlock::SetBounds(
ui::Layer* target,
const gfx::Rect& bounds,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::BOUNDS},
Element(bounds, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetBounds(
ui::LayerOwner* target,
const gfx::Rect& bounds,
gfx::Tween::Type tween_type) {
return SetBounds(target->layer(), bounds, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetBrightness(
ui::Layer* target,
float brightness,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::BRIGHTNESS},
Element(brightness, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetBrightness(
ui::LayerOwner* target,
float brightness,
gfx::Tween::Type tween_type) {
return SetBrightness(target->layer(), brightness, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetClipRect(
ui::Layer* target,
const gfx::Rect& clip_rect,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::CLIP},
Element(clip_rect, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetClipRect(
ui::LayerOwner* target,
const gfx::Rect& clip_rect,
gfx::Tween::Type tween_type) {
return SetClipRect(target->layer(), clip_rect, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetColor(
ui::Layer* target,
SkColor4f color,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::COLOR},
Element(color, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetColor(
ui::LayerOwner* target,
SkColor4f color,
gfx::Tween::Type tween_type) {
return SetColor(target->layer(), color, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetGrayscale(
ui::Layer* target,
float grayscale,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::GRAYSCALE},
Element(grayscale, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetGrayscale(
ui::LayerOwner* target,
float grayscale,
gfx::Tween::Type tween_type) {
return SetGrayscale(target->layer(), grayscale, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetOpacity(
ui::Layer* target,
float opacity,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::OPACITY},
Element(opacity, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetOpacity(
ui::LayerOwner* target,
float opacity,
gfx::Tween::Type tween_type) {
return SetOpacity(target->layer(), opacity, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetTransform(
ui::Layer* target,
gfx::Transform transform,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::TRANSFORM},
Element(std::move(transform), tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetTransform(
ui::LayerOwner* target,
gfx::Transform transform,
gfx::Tween::Type tween_type) {
return SetTransform(target->layer(), std::move(transform), tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetRoundedCorners(
ui::Layer* target,
const gfx::RoundedCornersF& rounded_corners,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::ROUNDED_CORNERS},
Element(rounded_corners, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetRoundedCorners(
ui::LayerOwner* target,
const gfx::RoundedCornersF& rounded_corners,
gfx::Tween::Type tween_type) {
return SetRoundedCorners(target->layer(), rounded_corners, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetGradientMask(
ui::Layer* target,
const gfx::LinearGradient& gradient_mask,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::GRADIENT_MASK},
Element(gradient_mask, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetGradientMask(
ui::LayerOwner* target,
const gfx::LinearGradient& gradient_mask,
gfx::Tween::Type tween_type) {
return SetGradientMask(target->layer(), gradient_mask, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetVisibility(
ui::Layer* target,
bool visible,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::VISIBILITY},
Element(visible, tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetVisibility(
ui::LayerOwner* target,
bool visible,
gfx::Tween::Type tween_type) {
return SetVisibility(target->layer(), visible, tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::SetInterpolatedTransform(
ui::Layer* target,
std::unique_ptr<ui::InterpolatedTransform> interpolated_transform,
gfx::Tween::Type tween_type) {
return AddAnimation({target, ui::LayerAnimationElement::TRANSFORM},
Element(std::move(interpolated_transform), tween_type));
}
AnimationSequenceBlock& AnimationSequenceBlock::SetInterpolatedTransform(
ui::LayerOwner* target,
std::unique_ptr<ui::InterpolatedTransform> interpolated_transform,
gfx::Tween::Type tween_type) {
return SetInterpolatedTransform(
target->layer(), std::move(interpolated_transform), tween_type);
}
AnimationSequenceBlock& AnimationSequenceBlock::At(
base::TimeDelta since_sequence_start) {
// NOTE: at the end of this function, this object is destroyed.
DCHECK(!finalized_) << "Do not access old blocks after creating new ones.";
TerminateBlock();
finalized_ = true;
// NOTE: `old_sequence` is actually the sequence block itself. Do not destruct
// this object until the function end.
auto old_sequence = owner_->SwapCurrentSequence(
PassKey(), std::make_unique<AnimationSequenceBlock>(
builder_key_, owner_, since_sequence_start, repeating_));
return owner_->GetCurrentSequence();
}
AnimationSequenceBlock& AnimationSequenceBlock::Offset(
base::TimeDelta since_last_block_start) {
return At(start_ + since_last_block_start);
}
AnimationSequenceBlock& AnimationSequenceBlock::Then() {
return Offset(duration_.value_or(base::TimeDelta()));
}
AnimationSequenceBlock::Element::Element(AnimationValue animation_value,
gfx::Tween::Type tween_type)
: animation_value_(std::move(animation_value)), tween_type_(tween_type) {}
AnimationSequenceBlock::Element::~Element() = default;
AnimationSequenceBlock::Element::Element(Element&&) = default;
AnimationSequenceBlock::Element& AnimationSequenceBlock::Element::operator=(
Element&&) = default;
AnimationSequenceBlock& AnimationSequenceBlock::AddAnimation(AnimationKey key,
Element element) {
DCHECK(!finalized_) << "Do not access old blocks after creating new ones.";
DCHECK(key.target) << "Animation targets must paint to a layer.";
const auto result =
elements_.insert(std::make_pair(std::move(key), std::move(element)));
DCHECK(result.second) << "Animate (target, property) at most once per block.";
return *this;
}
void AnimationSequenceBlock::TerminateBlock() {
const auto duration = duration_.value_or(base::TimeDelta());
for (auto& pair : elements_) {
std::unique_ptr<ui::LayerAnimationElement> element;
switch (pair.first.property) {
case ui::LayerAnimationElement::TRANSFORM:
if (std::holds_alternative<std::unique_ptr<ui::InterpolatedTransform>>(
pair.second.animation_value_)) {
element =
ui::LayerAnimationElement::CreateInterpolatedTransformElement(
std::get<std::unique_ptr<ui::InterpolatedTransform>>(
std::move(pair.second.animation_value_)),
duration);
} else {
DCHECK(std::holds_alternative<gfx::Transform>(
pair.second.animation_value_));
element = ui::LayerAnimationElement::CreateTransformElement(
std::get<gfx::Transform>(std::move(pair.second.animation_value_)),
duration);
}
break;
case ui::LayerAnimationElement::BOUNDS:
element = ui::LayerAnimationElement::CreateBoundsElement(
std::get<gfx::Rect>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::OPACITY:
element = ui::LayerAnimationElement::CreateOpacityElement(
std::get<float>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::VISIBILITY:
element = ui::LayerAnimationElement::CreateVisibilityElement(
std::get<bool>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::BRIGHTNESS:
element = ui::LayerAnimationElement::CreateBrightnessElement(
std::get<float>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::GRAYSCALE:
element = ui::LayerAnimationElement::CreateGrayscaleElement(
std::get<float>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::COLOR:
element = ui::LayerAnimationElement::CreateColorElement(
std::get<SkColor4f>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::CLIP:
element = ui::LayerAnimationElement::CreateClipRectElement(
std::get<gfx::Rect>(pair.second.animation_value_), duration);
break;
case ui::LayerAnimationElement::ROUNDED_CORNERS:
element = ui::LayerAnimationElement::CreateRoundedCornersElement(
std::get<gfx::RoundedCornersF>(pair.second.animation_value_),
duration);
break;
case ui::LayerAnimationElement::GRADIENT_MASK:
element = ui::LayerAnimationElement::CreateGradientMaskElement(
std::get<gfx::LinearGradient>(pair.second.animation_value_),
duration);
break;
default:
NOTREACHED();
}
element->set_tween_type(pair.second.tween_type_);
owner_->AddLayerAnimationElement(PassKey(), pair.first, start_, duration,
std::move(element));
}
owner_->BlockEndedAt(PassKey(), start_ + duration);
elements_.clear();
}
} // namespace views