blob: 9af7c9e381755b7335a3ce470a2c297bc73bdc4f [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/animation/square_ink_drop_ripple.h"
#include <algorithm>
#include <utility>
#include "base/logging.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/animation.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/view.h"
namespace views {
namespace {
// The minimum scale factor to use when scaling rectangle layers. Smaller values
// were causing visual anomalies.
constexpr float kMinimumRectScale = 0.0001f;
// The minimum scale factor to use when scaling circle layers. Smaller values
// were causing visual anomalies.
constexpr float kMinimumCircleScale = 0.001f;
// All the sub animations that are used to animate each of the InkDropStates.
// These are used to get time durations with
// GetAnimationDuration(InkDropSubAnimations). Note that in general a sub
// animation defines the duration for either a transformation animation or an
// opacity animation but there are some exceptions where an entire InkDropState
// animation consists of only 1 sub animation and it defines the duration for
// both the transformation and opacity animations.
enum InkDropSubAnimations {
// HIDDEN sub animations.
// The HIDDEN sub animation that is fading out to a hidden opacity.
HIDDEN_FADE_OUT,
// The HIDDEN sub animation that transforms the shape to a |small_size_|
// circle.
HIDDEN_TRANSFORM,
// ACTION_PENDING sub animations.
// The ACTION_PENDING sub animation that fades in to the visible opacity.
ACTION_PENDING_FADE_IN,
// The ACTION_PENDING sub animation that transforms the shape to a
// |large_size_| circle.
ACTION_PENDING_TRANSFORM,
// ACTION_TRIGGERED sub animations.
// The ACTION_TRIGGERED sub animation that is fading out to a hidden opacity.
ACTION_TRIGGERED_FADE_OUT,
// The ACTION_TRIGGERED sub animation that transforms the shape to a
// |large_size_|
// circle.
ACTION_TRIGGERED_TRANSFORM,
// ALTERNATE_ACTION_PENDING sub animations.
// The ALTERNATE_ACTION_PENDING animation has only one sub animation which
// animates
// to a |small_size_| rounded rectangle at visible opacity.
ALTERNATE_ACTION_PENDING,
// ALTERNATE_ACTION_TRIGGERED sub animations.
// The ALTERNATE_ACTION_TRIGGERED sub animation that is fading out to a hidden
// opacity.
ALTERNATE_ACTION_TRIGGERED_FADE_OUT,
// The ALTERNATE_ACTION_TRIGGERED sub animation that transforms the shape to a
// |large_size_|
// rounded rectangle.
ALTERNATE_ACTION_TRIGGERED_TRANSFORM,
// ACTIVATED sub animations.
// The ACTIVATED sub animation that transforms the shape to a |large_size_|
// circle. This is used when the ink drop is in a HIDDEN state prior to
// animating to the ACTIVATED state.
ACTIVATED_CIRCLE_TRANSFORM,
// The ACTIVATED sub animation that transforms the shape to a |small_size_|
// rounded rectangle.
ACTIVATED_RECT_TRANSFORM,
// DEACTIVATED sub animations.
// The DEACTIVATED sub animation that is fading out to a hidden opacity.
DEACTIVATED_FADE_OUT,
// The DEACTIVATED sub animation that transforms the shape to a |large_size_|
// rounded rectangle.
DEACTIVATED_TRANSFORM,
};
// The scale factor used to burst the ACTION_TRIGGERED bubble as it fades out.
constexpr float kQuickActionBurstScale = 1.3f;
// Returns the InkDropState sub animation duration for the given |state|.
base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) {
if (!PlatformStyle::kUseRipples ||
!gfx::Animation::ShouldRenderRichAnimation()) {
return base::TimeDelta();
}
// Duration constants for InkDropStateSubAnimations. See the
// InkDropStateSubAnimations enum documentation for more info.
constexpr base::TimeDelta kAnimationDuration[] = {
base::TimeDelta::FromMilliseconds(150), // HIDDEN_FADE_OUT
base::TimeDelta::FromMilliseconds(200), // HIDDEN_TRANSFORM
base::TimeDelta(), // ACTION_PENDING_FADE_IN
base::TimeDelta::FromMilliseconds(160), // ACTION_PENDING_TRANSFORM
base::TimeDelta::FromMilliseconds(150), // ACTION_TRIGGERED_FADE_OUT
base::TimeDelta::FromMilliseconds(160), // ACTION_TRIGGERED_TRANSFORM
base::TimeDelta::FromMilliseconds(200), // ALTERNATE_ACTION_PENDING
base::TimeDelta::FromMilliseconds(150), // ALTERNAT..._TRIGGERED_FADE_OUT
base::TimeDelta::FromMilliseconds(200), // ALTERNA..._TRIGGERED_TRANSFORM
base::TimeDelta::FromMilliseconds(200), // ACTIVATED_CIRCLE_TRANSFORM
base::TimeDelta::FromMilliseconds(160), // ACTIVATED_RECT_TRANSFORM
base::TimeDelta::FromMilliseconds(150), // DEACTIVATED_FADE_OUT
base::TimeDelta::FromMilliseconds(200), // DEACTIVATED_TRANSFORM
};
return kAnimationDuration[state];
}
} // namespace
SquareInkDropRipple::SquareInkDropRipple(const gfx::Size& large_size,
int large_corner_radius,
const gfx::Size& small_size,
int small_corner_radius,
const gfx::Point& center_point,
SkColor color,
float visible_opacity)
: activated_shape_(ActivatedShape::kRoundedRect),
visible_opacity_(visible_opacity),
large_size_(large_size),
large_corner_radius_(large_corner_radius),
small_size_(small_size),
small_corner_radius_(small_corner_radius),
center_point_(center_point),
circle_layer_delegate_(new CircleLayerDelegate(
color,
std::min(large_size_.width(), large_size_.height()) / 2)),
rect_layer_delegate_(
new RectangleLayerDelegate(color, gfx::SizeF(large_size_))),
root_layer_(ui::LAYER_NOT_DRAWN) {
root_layer_.SetName("SquareInkDropRipple:ROOT_LAYER");
for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
AddPaintLayer(static_cast<PaintedShape>(i));
root_layer_.SetMasksToBounds(false);
root_layer_.SetBounds(gfx::Rect(large_size_));
SetStateToHidden();
}
SquareInkDropRipple::~SquareInkDropRipple() {
// Explicitly aborting all the animations ensures all callbacks are invoked
// while this instance still exists.
AbortAllAnimations();
}
void SquareInkDropRipple::SnapToActivated() {
InkDropRipple::SnapToActivated();
SetOpacity(visible_opacity_);
InkDropTransforms transforms;
GetActivatedTargetTransforms(&transforms);
SetTransforms(transforms);
}
ui::Layer* SquareInkDropRipple::GetRootLayer() {
return &root_layer_;
}
float SquareInkDropRipple::GetCurrentOpacity() const {
return root_layer_.opacity();
}
std::string SquareInkDropRipple::ToLayerName(PaintedShape painted_shape) {
switch (painted_shape) {
case TOP_LEFT_CIRCLE:
return "TOP_LEFT_CIRCLE";
case TOP_RIGHT_CIRCLE:
return "TOP_RIGHT_CIRCLE";
case BOTTOM_RIGHT_CIRCLE:
return "BOTTOM_RIGHT_CIRCLE";
case BOTTOM_LEFT_CIRCLE:
return "BOTTOM_LEFT_CIRCLE";
case HORIZONTAL_RECT:
return "HORIZONTAL_RECT";
case VERTICAL_RECT:
return "VERTICAL_RECT";
case PAINTED_SHAPE_COUNT:
NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used.";
return "PAINTED_SHAPE_COUNT";
}
return "UNKNOWN";
}
void SquareInkDropRipple::AnimateStateChange(
InkDropState old_ink_drop_state,
InkDropState new_ink_drop_state,
ui::LayerAnimationObserver* animation_observer) {
InkDropTransforms transforms;
switch (new_ink_drop_state) {
case InkDropState::HIDDEN:
if (!IsVisible()) {
SetStateToHidden();
break;
} else {
AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(HIDDEN_FADE_OUT),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
CalculateCircleTransforms(small_size_, &transforms);
AnimateToTransforms(
transforms, GetAnimationDuration(HIDDEN_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
}
break;
case InkDropState::ACTION_PENDING:
if (old_ink_drop_state == new_ink_drop_state)
return;
DLOG_IF(WARNING, InkDropState::HIDDEN != old_ink_drop_state)
<< "Invalid InkDropState transition. old_ink_drop_state="
<< ToString(old_ink_drop_state)
<< " new_ink_drop_state=" << ToString(new_ink_drop_state);
AnimateToOpacity(visible_opacity_,
GetAnimationDuration(ACTION_PENDING_FADE_IN),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN, animation_observer);
AnimateToOpacity(visible_opacity_,
GetAnimationDuration(ACTION_PENDING_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN, animation_observer);
CalculateCircleTransforms(large_size_, &transforms);
AnimateToTransforms(transforms,
GetAnimationDuration(ACTION_PENDING_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
case InkDropState::ACTION_TRIGGERED: {
DLOG_IF(WARNING, old_ink_drop_state != InkDropState::HIDDEN &&
old_ink_drop_state != InkDropState::ACTION_PENDING)
<< "Invalid InkDropState transition. old_ink_drop_state="
<< ToString(old_ink_drop_state)
<< " new_ink_drop_state=" << ToString(new_ink_drop_state);
if (old_ink_drop_state == InkDropState::HIDDEN) {
AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING,
animation_observer);
}
AnimateToOpacity(kHiddenOpacity,
GetAnimationDuration(ACTION_TRIGGERED_FADE_OUT),
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
gfx::Tween::EASE_IN_OUT, animation_observer);
gfx::Size s = ScaleToRoundedSize(large_size_, kQuickActionBurstScale);
CalculateCircleTransforms(s, &transforms);
AnimateToTransforms(transforms,
GetAnimationDuration(ACTION_TRIGGERED_TRANSFORM),
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
}
case InkDropState::ALTERNATE_ACTION_PENDING:
DLOG_IF(WARNING, InkDropState::ACTION_PENDING != old_ink_drop_state)
<< "Invalid InkDropState transition. old_ink_drop_state="
<< ToString(old_ink_drop_state)
<< " new_ink_drop_state=" << ToString(new_ink_drop_state);
AnimateToOpacity(visible_opacity_,
GetAnimationDuration(ALTERNATE_ACTION_PENDING),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN, animation_observer);
CalculateRectTransforms(small_size_, small_corner_radius_, &transforms);
AnimateToTransforms(transforms,
GetAnimationDuration(ALTERNATE_ACTION_PENDING),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
case InkDropState::ALTERNATE_ACTION_TRIGGERED: {
DLOG_IF(WARNING,
InkDropState::ALTERNATE_ACTION_PENDING != old_ink_drop_state)
<< "Invalid InkDropState transition. old_ink_drop_state="
<< ToString(old_ink_drop_state)
<< " new_ink_drop_state=" << ToString(new_ink_drop_state);
base::TimeDelta visible_duration =
GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM) -
GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_FADE_OUT);
AnimateToOpacity(visible_opacity_, visible_duration,
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
AnimateToOpacity(
kHiddenOpacity,
GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_FADE_OUT),
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, gfx::Tween::EASE_IN_OUT,
animation_observer);
CalculateRectTransforms(large_size_, large_corner_radius_, &transforms);
AnimateToTransforms(
transforms,
GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
}
case InkDropState::ACTIVATED: {
// Animate the opacity so that it cancels any opacity animations already
// in progress.
AnimateToOpacity(visible_opacity_, base::TimeDelta(),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
ui::LayerAnimator::PreemptionStrategy rect_transform_preemption_strategy =
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET;
if (old_ink_drop_state == InkDropState::HIDDEN) {
rect_transform_preemption_strategy =
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
CalculateCircleTransforms(large_size_, &transforms);
AnimateToTransforms(
transforms, GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
} else if (old_ink_drop_state == InkDropState::ACTION_PENDING) {
rect_transform_preemption_strategy =
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
}
GetActivatedTargetTransforms(&transforms);
AnimateToTransforms(transforms,
GetAnimationDuration(ACTIVATED_RECT_TRANSFORM),
rect_transform_preemption_strategy,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
}
case InkDropState::DEACTIVATED: {
base::TimeDelta visible_duration =
GetAnimationDuration(DEACTIVATED_TRANSFORM) -
GetAnimationDuration(DEACTIVATED_FADE_OUT);
AnimateToOpacity(visible_opacity_, visible_duration,
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
AnimateToOpacity(kHiddenOpacity,
GetAnimationDuration(DEACTIVATED_FADE_OUT),
ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
gfx::Tween::EASE_IN_OUT, animation_observer);
GetDeactivatedTargetTransforms(&transforms);
AnimateToTransforms(transforms,
GetAnimationDuration(DEACTIVATED_TRANSFORM),
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
gfx::Tween::EASE_IN_OUT, animation_observer);
break;
}
}
}
void SquareInkDropRipple::SetStateToHidden() {
InkDropTransforms transforms;
// Use non-zero size to avoid visual anomalies.
CalculateCircleTransforms(gfx::Size(1, 1), &transforms);
SetTransforms(transforms);
root_layer_.SetOpacity(InkDropRipple::kHiddenOpacity);
root_layer_.SetVisible(false);
}
void SquareInkDropRipple::AbortAllAnimations() {
root_layer_.GetAnimator()->AbortAllAnimations();
for (auto& painted_layer : painted_layers_)
painted_layer->GetAnimator()->AbortAllAnimations();
}
void SquareInkDropRipple::AnimateToTransforms(
const InkDropTransforms transforms,
base::TimeDelta duration,
ui::LayerAnimator::PreemptionStrategy preemption_strategy,
gfx::Tween::Type tween,
ui::LayerAnimationObserver* animation_observer) {
for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) {
ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator();
ui::ScopedLayerAnimationSettings animation(animator);
animation.SetPreemptionStrategy(preemption_strategy);
animation.SetTweenType(tween);
std::unique_ptr<ui::LayerAnimationElement> element =
ui::LayerAnimationElement::CreateTransformElement(transforms[i],
duration);
ui::LayerAnimationSequence* sequence =
new ui::LayerAnimationSequence(std::move(element));
if (animation_observer)
sequence->AddObserver(animation_observer);
animator->StartAnimation(sequence);
}
}
void SquareInkDropRipple::SetTransforms(const InkDropTransforms transforms) {
for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
painted_layers_[i]->SetTransform(transforms[i]);
}
void SquareInkDropRipple::SetOpacity(float opacity) {
root_layer_.SetOpacity(opacity);
}
void SquareInkDropRipple::AnimateToOpacity(
float opacity,
base::TimeDelta duration,
ui::LayerAnimator::PreemptionStrategy preemption_strategy,
gfx::Tween::Type tween,
ui::LayerAnimationObserver* animation_observer) {
ui::LayerAnimator* animator = root_layer_.GetAnimator();
ui::ScopedLayerAnimationSettings animation_settings(animator);
animation_settings.SetPreemptionStrategy(preemption_strategy);
animation_settings.SetTweenType(tween);
std::unique_ptr<ui::LayerAnimationElement> animation_element =
ui::LayerAnimationElement::CreateOpacityElement(opacity, duration);
ui::LayerAnimationSequence* animation_sequence =
new ui::LayerAnimationSequence(std::move(animation_element));
if (animation_observer)
animation_sequence->AddObserver(animation_observer);
animator->StartAnimation(animation_sequence);
}
void SquareInkDropRipple::CalculateCircleTransforms(
const gfx::Size& size,
InkDropTransforms* transforms_out) const {
CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f,
transforms_out);
}
void SquareInkDropRipple::CalculateRectTransforms(
const gfx::Size& desired_size,
float corner_radius,
InkDropTransforms* transforms_out) const {
DCHECK_GE(desired_size.width() / 2.0f, corner_radius)
<< "The circle's diameter should not be greater than the total width.";
DCHECK_GE(desired_size.height() / 2.0f, corner_radius)
<< "The circle's diameter should not be greater than the total height.";
gfx::SizeF size(desired_size);
// This function can be called before the layer's been added to a view,
// either at construction time or in tests.
if (root_layer_.GetCompositor()) {
// Modify |desired_size| so that the ripple aligns to pixel bounds.
const float dsf = root_layer_.GetCompositor()->device_scale_factor();
gfx::RectF ripple_bounds((gfx::PointF(center_point_)), gfx::SizeF());
ripple_bounds.Inset(-gfx::InsetsF(desired_size.height() / 2.0f,
desired_size.width() / 2.0f));
ripple_bounds.Scale(dsf);
ripple_bounds = gfx::RectF(gfx::ToEnclosingRect(ripple_bounds));
ripple_bounds.Scale(1.0f / dsf);
size = ripple_bounds.size();
}
// The shapes are drawn such that their center points are not at the origin.
// Thus we use the CalculateCircleTransform() and CalculateRectTransform()
// methods to calculate the complex Transforms.
const float circle_scale = std::max(
kMinimumCircleScale,
corner_radius / static_cast<float>(circle_layer_delegate_->radius()));
const float circle_target_x_offset = size.width() / 2.0f - corner_radius;
const float circle_target_y_offset = size.height() / 2.0f - corner_radius;
(*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform(
circle_scale, -circle_target_x_offset, -circle_target_y_offset);
(*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform(
circle_scale, circle_target_x_offset, -circle_target_y_offset);
(*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform(
circle_scale, circle_target_x_offset, circle_target_y_offset);
(*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform(
circle_scale, -circle_target_x_offset, circle_target_y_offset);
const float rect_delegate_width = rect_layer_delegate_->size().width();
const float rect_delegate_height = rect_layer_delegate_->size().height();
(*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform(
std::max(kMinimumRectScale, size.width() / rect_delegate_width),
std::max(kMinimumRectScale,
(size.height() - 2.0f * corner_radius) / rect_delegate_height));
(*transforms_out)[VERTICAL_RECT] = CalculateRectTransform(
std::max(kMinimumRectScale,
(size.width() - 2.0f * corner_radius) / rect_delegate_width),
std::max(kMinimumRectScale, size.height() / rect_delegate_height));
}
gfx::Transform SquareInkDropRipple::CalculateCircleTransform(
float scale,
float target_center_x,
float target_center_y) const {
gfx::Transform transform;
// Offset for the center point of the ripple.
transform.Translate(center_point_.x(), center_point_.y());
// Move circle to target.
transform.Translate(target_center_x, target_center_y);
transform.Scale(scale, scale);
// Align center point of the painted circle.
const gfx::Vector2dF circle_center_offset =
circle_layer_delegate_->GetCenteringOffset();
transform.Translate(-circle_center_offset.x(), -circle_center_offset.y());
return transform;
}
gfx::Transform SquareInkDropRipple::CalculateRectTransform(
float x_scale,
float y_scale) const {
gfx::Transform transform;
transform.Translate(center_point_.x(), center_point_.y());
transform.Scale(x_scale, y_scale);
const gfx::Vector2dF rect_center_offset =
rect_layer_delegate_->GetCenteringOffset();
transform.Translate(-rect_center_offset.x(), -rect_center_offset.y());
return transform;
}
void SquareInkDropRipple::GetCurrentTransforms(
InkDropTransforms* transforms_out) const {
for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
(*transforms_out)[i] = painted_layers_[i]->transform();
}
void SquareInkDropRipple::GetActivatedTargetTransforms(
InkDropTransforms* transforms_out) const {
switch (activated_shape_) {
case ActivatedShape::kCircle:
CalculateCircleTransforms(small_size_, transforms_out);
break;
case ActivatedShape::kRoundedRect:
CalculateRectTransforms(small_size_, small_corner_radius_,
transforms_out);
break;
}
}
void SquareInkDropRipple::GetDeactivatedTargetTransforms(
InkDropTransforms* transforms_out) const {
switch (activated_shape_) {
case ActivatedShape::kCircle:
CalculateCircleTransforms(large_size_, transforms_out);
break;
case ActivatedShape::kRoundedRect:
CalculateRectTransforms(large_size_, small_corner_radius_,
transforms_out);
break;
}
}
void SquareInkDropRipple::AddPaintLayer(PaintedShape painted_shape) {
ui::LayerDelegate* delegate = nullptr;
switch (painted_shape) {
case TOP_LEFT_CIRCLE:
case TOP_RIGHT_CIRCLE:
case BOTTOM_RIGHT_CIRCLE:
case BOTTOM_LEFT_CIRCLE:
delegate = circle_layer_delegate_.get();
break;
case HORIZONTAL_RECT:
case VERTICAL_RECT:
delegate = rect_layer_delegate_.get();
break;
case PAINTED_SHAPE_COUNT:
NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type.";
break;
}
ui::Layer* layer = new ui::Layer();
root_layer_.Add(layer);
layer->SetBounds(gfx::Rect(large_size_));
layer->SetFillsBoundsOpaquely(false);
layer->set_delegate(delegate);
layer->SetVisible(true);
layer->SetOpacity(1.0);
layer->SetMasksToBounds(false);
layer->SetName("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape));
painted_layers_[painted_shape].reset(layer);
}
} // namespace views