blob: 0cca4cd8d72e6615b2b745fd5b57e8c90bd40b28 [file] [log] [blame]
// Copyright 2015 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/ink_drop_animation_controller_impl.h"
#include "base/auto_reset.h"
#include "base/timer/timer.h"
#include "ui/compositor/layer.h"
#include "ui/views/animation/ink_drop_host.h"
#include "ui/views/animation/ink_drop_hover.h"
#include "ui/views/animation/square_ink_drop_animation.h"
namespace views {
namespace {
// The duration, in milliseconds, of the hover state fade in animation when it
// is triggered by user input.
const int kHoverFadeInFromUserInputDurationInMs = 250;
// The duration, in milliseconds, of the hover state fade out animation when it
// is triggered by user input.
const int kHoverFadeOutFromUserInputDurationInMs = 250;
// The duration, in milliseconds, of the hover state fade in animation when it
// is triggered by an ink drop ripple animation ending.
const int kHoverFadeInAfterAnimationDurationInMs = 250;
// The duration, in milliseconds, of the hover state fade out animation when it
// is triggered by an ink drop ripple animation starting.
const int kHoverFadeOutBeforeAnimationDurationInMs = 120;
// The amount of time in milliseconds that |hover_| should delay after a ripple
// animation before fading in.
const int kHoverFadeInAfterAnimationDelayInMs = 1000;
// Returns true if an ink drop with the given |ink_drop_state| should
// automatically transition to the InkDropState::HIDDEN state.
bool ShouldAnimateToHidden(InkDropState ink_drop_state) {
switch (ink_drop_state) {
case views::InkDropState::ACTION_TRIGGERED:
case views::InkDropState::ALTERNATE_ACTION_TRIGGERED:
case views::InkDropState::DEACTIVATED:
return true;
default:
return false;
}
}
} // namespace
InkDropAnimationControllerImpl::InkDropAnimationControllerImpl(
InkDropHost* ink_drop_host)
: ink_drop_host_(ink_drop_host),
root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
is_hovered_(false),
hover_after_animation_timer_(nullptr) {
root_layer_->set_name("InkDropAnimationControllerImpl:RootLayer");
ink_drop_host_->AddInkDropLayer(root_layer_.get());
}
InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() {
// Explicitly destroy the InkDropAnimation so that this still exists if
// views::InkDropAnimationObserver methods are called on this.
DestroyInkDropAnimation();
ink_drop_host_->RemoveInkDropLayer(root_layer_.get());
}
InkDropState InkDropAnimationControllerImpl::GetTargetInkDropState() const {
if (!ink_drop_animation_)
return InkDropState::HIDDEN;
return ink_drop_animation_->target_ink_drop_state();
}
bool InkDropAnimationControllerImpl::IsVisible() const {
return ink_drop_animation_ && ink_drop_animation_->IsVisible();
}
void InkDropAnimationControllerImpl::AnimateToState(
InkDropState ink_drop_state) {
DestroyHiddenTargetedAnimations();
if (!ink_drop_animation_)
CreateInkDropAnimation();
if (ink_drop_state != views::InkDropState::HIDDEN) {
SetHoveredInternal(false, base::TimeDelta::FromMilliseconds(
kHoverFadeOutBeforeAnimationDurationInMs),
true);
}
ink_drop_animation_->AnimateToState(ink_drop_state);
}
void InkDropAnimationControllerImpl::SnapToActivated() {
DestroyHiddenTargetedAnimations();
if (!ink_drop_animation_)
CreateInkDropAnimation();
SetHoveredInternal(false, base::TimeDelta(), false);
ink_drop_animation_->SnapToActivated();
}
void InkDropAnimationControllerImpl::SetHovered(bool is_hovered) {
is_hovered_ = is_hovered;
SetHoveredInternal(is_hovered,
is_hovered ? base::TimeDelta::FromMilliseconds(
kHoverFadeInFromUserInputDurationInMs)
: base::TimeDelta::FromMilliseconds(
kHoverFadeOutFromUserInputDurationInMs),
false);
}
void InkDropAnimationControllerImpl::DestroyHiddenTargetedAnimations() {
if (ink_drop_animation_ &&
(ink_drop_animation_->target_ink_drop_state() == InkDropState::HIDDEN ||
ShouldAnimateToHidden(ink_drop_animation_->target_ink_drop_state()))) {
DestroyInkDropAnimation();
}
}
void InkDropAnimationControllerImpl::CreateInkDropAnimation() {
DestroyInkDropAnimation();
ink_drop_animation_ = ink_drop_host_->CreateInkDropAnimation();
ink_drop_animation_->set_observer(this);
root_layer_->Add(ink_drop_animation_->GetRootLayer());
}
void InkDropAnimationControllerImpl::DestroyInkDropAnimation() {
if (!ink_drop_animation_)
return;
root_layer_->Remove(ink_drop_animation_->GetRootLayer());
ink_drop_animation_.reset();
}
void InkDropAnimationControllerImpl::CreateInkDropHover() {
DestroyInkDropHover();
hover_ = ink_drop_host_->CreateInkDropHover();
if (!hover_)
return;
root_layer_->Add(hover_->layer());
}
void InkDropAnimationControllerImpl::DestroyInkDropHover() {
if (!hover_)
return;
root_layer_->Remove(hover_->layer());
hover_.reset();
}
bool InkDropAnimationControllerImpl::IsHoverFadingInOrVisible() const {
return hover_ && hover_->IsFadingInOrVisible();
}
void InkDropAnimationControllerImpl::AnimationStarted(
InkDropState ink_drop_state) {}
void InkDropAnimationControllerImpl::AnimationEnded(
InkDropState ink_drop_state,
InkDropAnimationEndedReason reason) {
if (reason != InkDropAnimationEndedReason::SUCCESS)
return;
if (ShouldAnimateToHidden(ink_drop_state)) {
ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN);
} else if (ink_drop_state == views::InkDropState::HIDDEN) {
if (is_hovered_)
StartHoverAfterAnimationTimer();
// TODO(bruthig): Investigate whether creating and destroying
// InkDropAnimations is expensive and consider creating an
// InkDropAnimationPool. See www.crbug.com/522175.
DestroyInkDropAnimation();
}
}
void InkDropAnimationControllerImpl::SetHoveredInternal(
bool is_hovered,
base::TimeDelta animation_duration,
bool explode) {
StopHoverAfterAnimationTimer();
if (IsHoverFadingInOrVisible() == is_hovered)
return;
if (is_hovered) {
CreateInkDropHover();
if (hover_ && !IsVisible())
hover_->FadeIn(animation_duration);
} else {
hover_->FadeOut(animation_duration, explode);
}
}
void InkDropAnimationControllerImpl::StartHoverAfterAnimationTimer() {
StopHoverAfterAnimationTimer();
if (!hover_after_animation_timer_)
hover_after_animation_timer_.reset(new base::OneShotTimer);
hover_after_animation_timer_->Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kHoverFadeInAfterAnimationDelayInMs),
base::Bind(&InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired,
base::Unretained(this)));
}
void InkDropAnimationControllerImpl::StopHoverAfterAnimationTimer() {
if (hover_after_animation_timer_)
hover_after_animation_timer_.reset();
}
void InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired() {
SetHoveredInternal(true, base::TimeDelta::FromMilliseconds(
kHoverFadeInAfterAnimationDurationInMs),
true);
hover_after_animation_timer_.reset();
}
} // namespace views