blob: 67b1422f16b41fa6f3772c212764a650af190ef1 [file] [log] [blame]
// Copyright 2019 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 "ash/wm/desks/desks_animations.h"
#include <memory>
#include <utility>
#include "ash/shell.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/window_transient_descendant_iterator.h"
#include "base/time/time.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace desks_animations {
namespace {
// Returns the transform that can translate |window| offscreen in the direction
// indicated by |going_left|.
gfx::Transform GetWindowEndTransform(aura::Window* window, bool going_left) {
gfx::Transform transform;
auto* root = window->GetRootWindow();
const auto root_bounds = root->bounds();
const auto window_bounds = window->GetBoundsInRootWindow();
const int x_translation = going_left
? -window_bounds.right()
: (root_bounds.right() - window_bounds.x());
transform.Translate(x_translation, 0);
return transform;
}
// A self-deleting object, which recreates the layer tree of the given |window|,
// and animates the old layer tree offscreen towards the direction of the
// target desk indicated by |going_left|. When the animation is over, this
// object deletes itself and the window's old layer tree.
class WindowMoveToDeskAnimation : public ui::ImplicitAnimationObserver {
public:
WindowMoveToDeskAnimation(aura::Window* window, bool going_left)
: old_window_layer_tree_(::wm::RecreateLayers(window)) {
ui::Layer* layer = old_window_layer_tree_->root();
ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
constexpr base::TimeDelta kDuration =
base::TimeDelta::FromMilliseconds(200);
settings.SetTransitionDuration(kDuration);
settings.SetTweenType(gfx::Tween::EASE_IN);
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.AddObserver(this);
layer->SetTransform(GetWindowEndTransform(window, going_left));
}
~WindowMoveToDeskAnimation() override = default;
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override { delete this; }
private:
std::unique_ptr<ui::LayerTreeOwner> old_window_layer_tree_;
DISALLOW_COPY_AND_ASSIGN(WindowMoveToDeskAnimation);
};
} // namespace
void PerformHitTheWallAnimation(aura::Window* root, bool going_left) {
DCHECK(root->IsRootWindow());
// Start and end the animation using the root layer's target transform, since
// the layer might have a different transform other than identity due to, for
// example, display rotation.
ui::Layer* layer = root->layer();
const gfx::Transform end_transform = layer->GetTargetTransform();
gfx::Transform begin_transform = end_transform;
// |root| will be translated out horizontally by 4% of its width and then
// translated back to its original transform.
const float displacement_factor = 0.04f * (going_left ? 1 : -1);
begin_transform.Translate(displacement_factor * root->bounds().width(), 0);
// Prepare two animation elements, one for the outgoing translation:
// | |
// |<----|
// | |
// and another for the incoming translation:
// | |
// |---->|
// | |
constexpr base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(150);
auto outgoing_transition = ui::LayerAnimationElement::CreateTransformElement(
begin_transform, kDuration);
outgoing_transition->set_tween_type(gfx::Tween::EASE_OUT);
auto sequence = std::make_unique<ui::LayerAnimationSequence>();
sequence->AddElement(std::move(outgoing_transition));
auto incoming_transition = ui::LayerAnimationElement::CreateTransformElement(
end_transform, kDuration);
incoming_transition->set_tween_type(gfx::Tween::EASE_IN);
sequence->AddElement(std::move(incoming_transition));
// Use `REPLACE_QUEUED_ANIMATIONS` since the user may press the shortcut many
// times repeatedly, and we don't want to keep animating the root layer
// endlessly.
ui::ScopedLayerAnimationSettings settings{layer->GetAnimator()};
settings.SetPreemptionStrategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
layer->GetAnimator()->StartAnimation(sequence.release());
}
void PerformWindowMoveToDeskAnimation(aura::Window* window, bool going_left) {
DCHECK(!Shell::Get()->overview_controller()->InOverviewSession());
// The entire transient window tree should appear to animate together towards
// the target desk.
for (auto* transient_window : GetTransientTreeIterator(window)) {
// This is a self-deleting object.
new WindowMoveToDeskAnimation(transient_window, going_left);
}
}
} // namespace desks_animations
} // namespace ash