Chromium Code Reviews| Index: content/browser/web_contents/aura/overscroll_window_animation.cc |
| diff --git a/content/browser/web_contents/aura/overscroll_window_animation.cc b/content/browser/web_contents/aura/overscroll_window_animation.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ff04dc92aa3e2d7296813f8827759aab9cbbdb2b |
| --- /dev/null |
| +++ b/content/browser/web_contents/aura/overscroll_window_animation.cc |
| @@ -0,0 +1,303 @@ |
| +// Copyright (c) 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 "base/auto_reset.h" |
| +#include "content/browser/web_contents/aura/overscroll_window_delegate.h" |
| +#include "content/browser/web_contents/aura/shadow_layer_delegate.h" |
| +#include "content/browser/web_contents/web_contents_impl.h" |
| +#include "content/browser/web_contents/web_contents_view_aura.h" |
| +#include "content/public/browser/render_widget_host_view.h" |
| +#include "ui/aura/window.h" |
| +#include "ui/compositor/layer_animation_observer.h" |
| +#include "ui/compositor/scoped_layer_animation_settings.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +// Responsible for fading out and deleting the layer of the overlay window. |
| +class OverlayDismissAnimator : public ui::LayerAnimationObserver { |
| + public: |
| + // Takes ownership of the layer. |
| + explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer) |
| + : layer_(layer.Pass()) { |
| + CHECK(layer_.get()); |
| + } |
| + |
| + // Starts the fadeout animation on the layer. When the animation finishes, |
| + // the object deletes itself along with the layer. |
| + void Animate() { |
| + DCHECK(layer_.get()); |
| + ui::LayerAnimator* animator = layer_->GetAnimator(); |
| + // This makes SetOpacity() animate with default duration (which could be |
| + // zero, e.g. when running tests). |
| + ui::ScopedLayerAnimationSettings settings(animator); |
| + animator->AddObserver(this); |
| + LOG(ERROR) << "Setting opacity to 0"; |
| + layer_->SetOpacity(0); |
| + } |
| + |
| + // Overridden from ui::LayerAnimationObserver |
| + void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override { |
| + LOG(ERROR) << "Deleting dismiss layer"; |
| + delete this; |
| + } |
| + |
| + void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override { |
| + LOG(ERROR) << "Animation aborted!!!"; |
| + delete this; |
| + } |
| + |
| + void OnLayerAnimationScheduled( |
| + ui::LayerAnimationSequence* sequence) override {} |
| + |
| + private: |
| + ~OverlayDismissAnimator() override {} |
| + |
| + scoped_ptr<ui::Layer> layer_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator); |
| +}; |
| + |
| +} // namespace |
| + |
| +OverscrollWindowAnimation::OverscrollWindowAnimation( |
| + WebContentsImpl* web_contents, |
| + OverscrollNavigationOverlay* ono, |
| + aura::Window* web_contents_window) |
| + : web_contents_(web_contents), |
| + ono_(ono), |
| + direction_(OverscrollNavigationOverlay::NONE), |
| + web_contents_window_(web_contents_window), |
| + slide_layer_(nullptr), |
| + fade_out_(false), |
| + animation_cancelled_(false), |
| + gesture_completed_(false) { |
| +} |
| + |
| +OverscrollWindowAnimation::~OverscrollWindowAnimation() { |
| + LOG(ERROR) << "OWA destructor"; |
| +} |
| + |
| +void OverscrollWindowAnimation::CancelAnimation() { |
| + LOG(ERROR) << "Cancelling animation"; |
| + animation_cancelled_ = true; |
| + fade_out_ = false; |
| + gesture_completed_ = true; |
| + aura::Window* animate_window = GetWindowToAnimateForOverscroll(); |
| + if (!animate_window) |
| + return; |
| + ui::ScopedLayerAnimationSettings settings( |
| + animate_window->layer()->GetAnimator()); |
| + settings.SetPreemptionStrategy( |
| + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| + settings.SetTweenType(gfx::Tween::EASE_OUT); |
| + settings.AddObserver(this); |
| + animate_window->SetTransform(gfx::Transform()); |
| + direction_ = OverscrollNavigationOverlay::NONE; |
| +} |
| + |
| +// We should be able to delete this function. |
| +void OverscrollWindowAnimation::AbortAllAnimations() { |
| + aura::Window* target = GetWindowToAnimateForOverscroll(); |
| + if (target) |
| + target->layer()->GetAnimator()->AbortAllAnimations(); |
| +} |
| + |
| +void OverscrollWindowAnimation::DismissOverlay() { |
| + LOG(ERROR) << "Dismissing overlay"; |
| + if (gesture_completed_) { |
| + FadeOutOverscrollWindow(); |
| + return; |
| + } |
| + fade_out_ = true; |
| +} |
| + |
| +void OverscrollWindowAnimation::OnImplicitAnimationsCompleted() { |
| + LOG(ERROR) << "On implicit animations completed"; |
| + if (!overscroll_window_) { |
| + LOG(ERROR) << "Overscroll window already destroyed, returning"; |
| + return; |
| + } |
| + gesture_completed_ = true; |
| + aura::Window* contents = web_contents_->GetContentNativeView(); |
| + contents->parent()->StackChildBelow(contents, overscroll_window_.get()); |
|
mfomitchev
2015/02/13 22:08:48
Similar to the previous comments - this logic shou
|
| + contents->SetTransform(gfx::Transform()); |
| + if (fade_out_) { |
| + LOG(ERROR) << "With fade_out_ == true"; |
| + FadeOutOverscrollWindow(); |
| + return; |
| + } |
| + if (animation_cancelled_) { |
| + LOG(ERROR) << "With animation_cancelled_ == true"; |
| + overscroll_window_.reset(); |
| + overscroll_shadow_.reset(); |
| + } |
| +} |
| + |
| +gfx::Vector2dF OverscrollWindowAnimation::GetTranslationForOverscroll( |
| + float delta_x) { |
| + const float bounds_width = |
| + static_cast<float>(web_contents_window_->bounds().width()); |
| + if (direction_ == OverscrollNavigationOverlay::FORWARD) |
| + return gfx::Vector2dF(std::max(-bounds_width, delta_x), 0); |
| + else |
| + return gfx::Vector2dF(std::min(bounds_width, delta_x), 0); |
| +} |
| + |
| +gfx::Rect OverscrollWindowAnimation::GetVisibleBounds() const { |
| + return web_contents_window_->bounds(); |
| +} |
| + |
| +bool OverscrollWindowAnimation::OnOverscrollUpdate(float delta_x, |
| + float delta_y) { |
| + if (direction_ == OverscrollNavigationOverlay::NONE) |
| + return false; |
| + LOG(ERROR) << "OWA: OnOverscrollUpdate"; |
| + gfx::Vector2dF translate = GetTranslationForOverscroll(delta_x); |
| + if (translate.IsZero()) |
| + return false; |
| + |
| + gfx::Transform transform; |
| + transform.Translate(translate.x(), translate.y()); |
| + aura::Window* animate_window = GetWindowToAnimateForOverscroll(); |
| + if (animate_window) { |
| + animate_window->SetTransform(transform); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void OverscrollWindowAnimation::OnOverscrollModeChange( |
| + OverscrollMode old_mode, |
| + OverscrollMode new_mode) { |
| + LOG(ERROR) << "OWA: OnOverscrollModeChange"; |
| + animation_cancelled_ = false; |
| + AbortAllAnimations(); |
| + |
| + direction_ = |
| + ono_->GetNavigationDirection(web_contents_->GetController(), new_mode); |
| + // TODO(nsatragno): in this case, show a feedback animation. |
| + if (direction_ == OverscrollNavigationOverlay::NONE) { |
| + if (overscroll_window_) |
| + CancelAnimation(); |
| + return; |
| + } |
| + if (overscroll_window_) { |
| + LOG(ERROR) << "OWA: SHOULD CREATE LAYER"; |
| + AddNewLayer(); |
| + return; |
| + } |
| + |
| + StartAnimating(); |
| +} |
| + |
| +void OverscrollWindowAnimation::StartAnimating() { |
| + LOG(ERROR) << "OWA: StartAnimating"; |
| + gesture_completed_ = false; |
| + OverscrollWindowDelegate* overscroll_delegate = new OverscrollWindowDelegate( |
|
mfomitchev
2015/02/13 22:08:48
I am not sure this setup code belongs in OWA eithe
|
| + this, ono_->GetImageForDirection(direction_)); |
| + overscroll_window_.reset(new aura::Window(overscroll_delegate)); |
| + overscroll_window_->SetType(ui::wm::WINDOW_TYPE_CONTROL); |
| + overscroll_window_->SetTransparent(true); |
| + overscroll_window_->Init(aura::WINDOW_LAYER_TEXTURED); |
| + overscroll_window_->layer()->SetMasksToBounds(false); |
| + overscroll_window_->SetName("OverscrollOverlay"); |
| + |
| + aura::Window* animate_window = GetWindowToAnimateForOverscroll(); |
| + web_contents_window_->AddChild(overscroll_window_.get()); |
| + |
| + if (animate_window == overscroll_window_) { |
| + LOG(ERROR) << "animate_window == overscroll_window_"; |
| + web_contents_window_->StackChildAbove( |
| + overscroll_window_.get(), web_contents_->GetContentNativeView()); |
| + } else { |
| + LOG(ERROR) << "animate_window != overscroll_window_"; |
| + web_contents_window_->StackChildBelow( |
| + overscroll_window_.get(), web_contents_->GetContentNativeView()); |
| + } |
| + |
| + overscroll_window_->SetBounds(GetStarterBounds()); |
| + overscroll_window_->Show(); |
| + |
| + if (!animate_window) |
| + LOG(ERROR) << "ERROR: ANIMATE_WINDOW_ IS NULL"; |
| + |
| + overscroll_shadow_.reset(new ShadowLayerDelegate(animate_window->layer())); |
| +} |
| + |
| +void OverscrollWindowAnimation::AddNewLayer() { |
| + slide_layer_ = ono_->CreateLayerForDirection(direction_); |
| + ui::Layer* parent = overscroll_window_->layer()->parent(); |
|
mfomitchev
2015/02/13 22:08:48
Same here
|
| + parent->Add(slide_layer_.get()); |
| + // TODO stack apropriately. |
| + if (direction_ == OverscrollNavigationOverlay::FORWARD) |
| + parent->StackAbove(slide_layer_.get(), overscroll_window_->layer()); |
| + else |
| + parent->StackBelow(slide_layer_.get(), overscroll_window_->layer()); |
| + slide_layer_->SetBounds(GetStarterBounds()); |
| +} |
| + |
| +void OverscrollWindowAnimation::FadeOutOverscrollWindow() { |
| + fade_out_ = false; |
| + LOG(ERROR) << "FadeOutOverscrollWindow"; |
| + if (!overscroll_window_) |
| + return; |
| + aura::Window* contents = web_contents_->GetContentNativeView(); |
| + contents->layer()->SetLayerBrightness(1.f); |
| + { |
| + ui::ScopedLayerAnimationSettings settings(contents->layer()->GetAnimator()); |
| + settings.SetPreemptionStrategy( |
| + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| + settings.SetTweenType(gfx::Tween::EASE_OUT); |
| + contents->layer()->SetLayerBrightness(0.f); |
| + } |
| + scoped_ptr<ui::Layer> dismiss_layer = overscroll_window_->AcquireLayer(); |
| + overscroll_window_.reset(); |
| + (new OverlayDismissAnimator(dismiss_layer.Pass()))->Animate(); |
| + // TODO delete shadow when the dismiss animation finishes? |
| + overscroll_shadow_.reset(); |
| +} |
| + |
| +gfx::Rect OverscrollWindowAnimation::GetStarterBounds() const { |
| + gfx::Rect bounds = gfx::Rect(web_contents_window_->bounds().size()); |
| + if (direction_ == OverscrollNavigationOverlay::FORWARD) { |
| + // The overlay will be sliding in from the right edge towards the left in |
| + // non-RTL, or sliding in from the left edge towards the right in RTL. |
| + // So position the overlay window accordingly. |
| + bounds.Offset(base::i18n::IsRTL() ? -bounds.width() : bounds.width(), 0); |
| + } |
| + return bounds; |
| +} |
| + |
| +void OverscrollWindowAnimation::OnOverscrollComplete( |
| + OverscrollMode overscroll_mode) { |
| + LOG(ERROR) << "OWA: OnOverscrollComplete"; |
| + aura::Window* target = GetWindowToAnimateForOverscroll(); |
| + ui::ScopedLayerAnimationSettings settings(target->layer()->GetAnimator()); |
| + settings.SetPreemptionStrategy( |
| + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| + settings.SetTweenType(gfx::Tween::EASE_OUT); |
| + settings.AddObserver(this); |
| + gfx::Transform transform; |
| + int content_width = |
| + web_contents_->GetRenderWidgetHostView()->GetViewBounds().width(); |
| + float translate_x = static_cast<float>( |
| + direction_ == OverscrollNavigationOverlay::FORWARD ? -content_width |
| + : content_width); |
| + transform.Translate(translate_x, 0); |
| + target->SetTransform(transform); |
| + direction_ = OverscrollNavigationOverlay::NONE; |
| +} |
| + |
| +aura::Window* OverscrollWindowAnimation::GetWindowToAnimateForOverscroll() |
| + const { |
| + return direction_ == OverscrollNavigationOverlay::FORWARD |
| + ? overscroll_window_.get() |
| + : web_contents_->GetContentNativeView(); |
| +} |
| + |
| +} // namespace content |