blob: cd36565d0c710eaa12ed404a39578b7122543b92 [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 "content/browser/web_contents/aura/overscroll_window_animation.h"
#include <algorithm>
#include <utility>
#include "base/i18n/rtl.h"
#include "content/browser/web_contents/aura/shadow_layer_delegate.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
namespace content {
namespace {
OverscrollWindowAnimation::Direction GetDirectionForMode(OverscrollMode mode) {
if (mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST))
return OverscrollWindowAnimation::SLIDE_FRONT;
if (mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST))
return OverscrollWindowAnimation::SLIDE_BACK;
return OverscrollWindowAnimation::SLIDE_NONE;
}
} // namespace
OverscrollWindowAnimation::OverscrollWindowAnimation(Delegate* delegate)
: delegate_(delegate),
direction_(SLIDE_NONE),
overscroll_cancelled_(false) {
DCHECK(delegate_);
}
OverscrollWindowAnimation::~OverscrollWindowAnimation() {}
void OverscrollWindowAnimation::CancelSlide() {
overscroll_cancelled_ = true;
// Listen to the animation of the main window.
bool main_window_is_front = direction_ == SLIDE_BACK;
AnimateTranslation(GetBackLayer(), 0, !main_window_is_front);
AnimateTranslation(GetFrontLayer(), 0, main_window_is_front);
}
float OverscrollWindowAnimation::GetTranslationForOverscroll(float delta_x) {
DCHECK(direction_ != SLIDE_NONE);
const float bounds_width = GetContentSize().width();
if (direction_ == SLIDE_FRONT)
return std::max(-bounds_width, delta_x);
else
return std::min(bounds_width, delta_x);
}
gfx::Size OverscrollWindowAnimation::GetDisplaySize() const {
return display::Screen::GetScreen()
->GetDisplayNearestView(delegate_->GetMainWindow())
.size();
}
void OverscrollWindowAnimation::OnOverscrollBehaviorUpdate(
cc::OverscrollBehavior overscroll_behavior) {
overscroll_behavior_ = overscroll_behavior;
}
bool OverscrollWindowAnimation::OnOverscrollUpdate(float delta_x,
float delta_y) {
if (direction_ == SLIDE_NONE)
return false;
gfx::Transform front_transform;
gfx::Transform back_transform;
float translate_x = GetTranslationForOverscroll(delta_x);
front_transform.Translate(translate_x, 0);
back_transform.Translate(translate_x / 2, 0);
GetFrontLayer()->SetTransform(front_transform);
GetBackLayer()->SetTransform(back_transform);
return true;
}
void OverscrollWindowAnimation::OnImplicitAnimationsCompleted() {
if (overscroll_cancelled_) {
slide_window_.reset();
delegate_->OnOverscrollCancelled();
overscroll_cancelled_ = false;
} else {
delegate_->OnOverscrollCompleted(std::move(slide_window_));
}
overscroll_source_ = OverscrollSource::NONE;
direction_ = SLIDE_NONE;
}
void OverscrollWindowAnimation::OnOverscrollModeChange(
OverscrollMode old_mode,
OverscrollMode new_mode,
OverscrollSource source) {
DCHECK_NE(old_mode, new_mode);
Direction new_direction = GetDirectionForMode(new_mode);
if (new_direction == SLIDE_NONE ||
overscroll_behavior_.x != cc::OverscrollBehavior::OverscrollBehaviorType::
kOverscrollBehaviorTypeAuto) {
// The user cancelled the in progress animation.
if (is_active())
CancelSlide();
return;
}
if (is_active()) {
slide_window_->layer()->GetAnimator()->StopAnimating();
delegate_->GetMainWindow()->layer()->GetAnimator()->StopAnimating();
}
gfx::Rect slide_window_bounds(GetContentSize());
if (new_direction == SLIDE_FRONT) {
slide_window_bounds.Offset(base::i18n::IsRTL()
? -slide_window_bounds.width()
: slide_window_bounds.width(),
0);
} else {
slide_window_bounds.Offset(base::i18n::IsRTL()
? slide_window_bounds.width() / 2
: -slide_window_bounds.width() / 2,
0);
}
overscroll_source_ = source;
slide_window_ = new_direction == SLIDE_FRONT
? delegate_->CreateFrontWindow(slide_window_bounds)
: delegate_->CreateBackWindow(slide_window_bounds);
if (!slide_window_) {
// Cannot navigate, do not start an overscroll gesture.
direction_ = SLIDE_NONE;
overscroll_source_ = OverscrollSource::NONE;
return;
}
overscroll_cancelled_ = false;
direction_ = new_direction;
shadow_.reset(new ShadowLayerDelegate(GetFrontLayer()));
}
base::Optional<float> OverscrollWindowAnimation::GetMaxOverscrollDelta() const {
return base::nullopt;
}
void OverscrollWindowAnimation::OnOverscrollComplete(
OverscrollMode overscroll_mode) {
if (!is_active())
return;
delegate_->OnOverscrollCompleting();
int content_width = GetContentSize().width();
float translate_x;
if ((base::i18n::IsRTL() && direction_ == SLIDE_FRONT) ||
(!base::i18n::IsRTL() && direction_ == SLIDE_BACK)) {
translate_x = content_width;
} else {
translate_x = -content_width;
}
// Listen to the animation of the main window.
bool main_window_is_front = direction_ == SLIDE_BACK;
AnimateTranslation(GetBackLayer(), translate_x / 2, !main_window_is_front);
AnimateTranslation(GetFrontLayer(), translate_x, main_window_is_front);
}
void OverscrollWindowAnimation::AnimateTranslation(ui::Layer* layer,
float translate_x,
bool listen_for_completion) {
gfx::Transform transform;
transform.Translate(translate_x, 0);
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTweenType(gfx::Tween::EASE_OUT);
if (listen_for_completion)
settings.AddObserver(this);
layer->SetTransform(transform);
}
ui::Layer* OverscrollWindowAnimation::GetFrontLayer() const {
DCHECK(direction_ != SLIDE_NONE);
if (direction_ == SLIDE_FRONT) {
DCHECK(slide_window_);
return slide_window_->layer();
}
return delegate_->GetMainWindow()->layer();
}
ui::Layer* OverscrollWindowAnimation::GetBackLayer() const {
DCHECK(direction_ != SLIDE_NONE);
if (direction_ == SLIDE_BACK) {
DCHECK(slide_window_);
return slide_window_->layer();
}
return delegate_->GetMainWindow()->layer();
}
gfx::Size OverscrollWindowAnimation::GetContentSize() const {
return delegate_->GetMainWindow()->bounds().size();
}
} // namespace content