Commit c641d129 authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

wm: Animate window when dragging to unmaximize and dropping window.

Spec wants a cross fade animation that is linear and 120ms. The target
bounds are often changing as well since the window is in a drag.

Modify current cross fade animation to take in different tween and
times. Since the animation happens while the window is in a drag and
the bounds are being updated, add a new animation observer. The new
created layer animates towards identity transform so changing the
bounds is ok. The old layer is a bit trickier to deal with so on a
animation tick, update the transform so that the old layer is always
aligned with the new layer.

Follow up will add metrics so we can have a perf test.

Test: manual
Bug: 1081986
Change-Id: I46882c0a11b7ea2c97b1c8802a9c65184b384c1e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2204813Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#770881}
parent ae59c72b
This diff is collapsed.
...@@ -23,17 +23,24 @@ class LayerTreeOwner; ...@@ -23,17 +23,24 @@ class LayerTreeOwner;
// with desktop Chrome, see ui/views/corewm/window_animations.h. // with desktop Chrome, see ui/views/corewm/window_animations.h.
namespace ash { namespace ash {
// Amount of time for the cross fade animation.
constexpr base::TimeDelta kCrossFadeDuration =
base::TimeDelta::FromMilliseconds(200);
// Implementation of cross fading. Window is the window being cross faded. It // Implementation of cross fading. Window is the window being cross faded. It
// should be at the target bounds. |old_layer_owner| contains the previous layer // should be at the target bounds. |old_layer_owner| contains the previous layer
// from |window|. // from |window|.
ASH_EXPORT base::TimeDelta CrossFadeAnimation( ASH_EXPORT void CrossFadeAnimation(
aura::Window* window, aura::Window* window,
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner); std::unique_ptr<ui::LayerTreeOwner> old_layer_owner);
// Implementation of cross fading which only animates the new layer. The old
// layer will be owned by an observer which will update the transform as the new
// layer's transform and bounds change. This is used by the
// WorkspaceWindowResizer which needs to animate a window which has its bounds
// updated throughout the course of the animation.
ASH_EXPORT void CrossFadeAnimationAnimateNewLayerOnly(
aura::Window* window,
const gfx::Rect& target_bounds,
base::TimeDelta duration,
gfx::Tween::Type tween_type);
ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window,
bool visible); bool visible);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ui/compositor/layer_animator.h" #include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
using aura::Window; using aura::Window;
using ui::Layer; using ui::Layer;
...@@ -391,4 +392,74 @@ TEST_F(WindowAnimationsTest, ResetAnimationAfterDismissingArcPip) { ...@@ -391,4 +392,74 @@ TEST_F(WindowAnimationsTest, ResetAnimationAfterDismissingArcPip) {
window->layer()->GetTargetBounds()); window->layer()->GetTargetBounds());
} }
// A unique test class for testing certain cross fade animations as those rely
// on observing compositor animations which require a mock time task
// environment.
class CrossFadeAnimateNewLayerOnlyTest : public AshTestBase {
public:
CrossFadeAnimateNewLayerOnlyTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
CrossFadeAnimateNewLayerOnlyTest(const CrossFadeAnimateNewLayerOnlyTest&) =
delete;
CrossFadeAnimateNewLayerOnlyTest& operator=(
const CrossFadeAnimateNewLayerOnlyTest&) = delete;
~CrossFadeAnimateNewLayerOnlyTest() override = default;
};
// Tests a version of the cross fade animation which animates the transform and
// opacity of the new layer, but only the opacity of the old layer. The old
// layer transform is updated manually when the animation ticks so that it
// has the same visible bounds as the new layer.
TEST_F(CrossFadeAnimateNewLayerOnlyTest, CrossFadeAnimateNewLayerOnly) {
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
std::unique_ptr<Window> window(CreateTestWindowInShellWithId(0));
window->SetBounds(gfx::Rect(10, 10, 200, 200));
window->Show();
window->layer()->GetAnimator()->StopAnimating();
ui::DrawWaiterForTest::WaitForCompositingStarted(
window->GetRootWindow()->layer()->GetCompositor());
Layer* old_layer = window->layer();
EXPECT_EQ(1.f, old_layer->GetTargetOpacity());
const gfx::Rect target_bounds(40, 40, 400, 400);
CrossFadeAnimationAnimateNewLayerOnly(window.get(), target_bounds,
base::TimeDelta::FromMilliseconds(200),
gfx::Tween::LINEAR);
// Window's layer has been replaced.
EXPECT_NE(old_layer, window->layer());
// Original layer fades away. Transform is updated as the animation steps.
EXPECT_EQ(0.f, old_layer->GetTargetOpacity());
EXPECT_EQ(gfx::Rect(10, 10, 200, 200), old_layer->bounds());
EXPECT_EQ(gfx::Transform(), old_layer->GetTargetTransform());
// New layer animates in to the identity transform.
EXPECT_EQ(1.0f, window->layer()->GetTargetOpacity());
EXPECT_EQ(gfx::Transform(), window->layer()->GetTargetTransform());
// Start the animations, then set the bounds of the new window during the
// animation.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(10));
// Set the bounds halfway through the animation. The bounds of the old layer
// remain the same, but the transform has updated to match the bounds of the
// new layer.
window->SetBounds(gfx::Rect(80, 80, 200, 200));
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(gfx::Rect(10, 10, 200, 200), old_layer->bounds());
EXPECT_NE(gfx::Transform(), old_layer->GetTargetTransform());
// New layer targets remain the same.
EXPECT_EQ(1.0f, window->layer()->GetTargetOpacity());
EXPECT_EQ(gfx::Transform(), window->layer()->GetTargetTransform());
task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
}
} // namespace ash } // namespace ash
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h" #include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h"
#include "ash/wm/tablet_mode/tablet_mode_window_resizer.h" #include "ash/wm/tablet_mode/tablet_mode_window_resizer.h"
#include "ash/wm/window_animations.h"
#include "ash/wm/window_positioning_utils.h" #include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
#include "ash/wm/window_util.h" #include "ash/wm/window_util.h"
...@@ -84,6 +85,11 @@ constexpr char kTabDraggingInClamshellModeMaxLatencyHistogram[] = ...@@ -84,6 +85,11 @@ constexpr char kTabDraggingInClamshellModeMaxLatencyHistogram[] =
"Ash.WorkspaceWindowResizer.TabDragging.PresentationTime.MaxLatency." "Ash.WorkspaceWindowResizer.TabDragging.PresentationTime.MaxLatency."
"ClamshellMode"; "ClamshellMode";
// Duration of the cross fade animation used when dragging to unmaximize or
// dragging to snap maximize.
constexpr base::TimeDelta kCrossFadeDuration =
base::TimeDelta::FromMilliseconds(120);
// Current instance for use by the WorkspaceWindowResizerTest. // Current instance for use by the WorkspaceWindowResizerTest.
WorkspaceWindowResizer* instance = nullptr; WorkspaceWindowResizer* instance = nullptr;
...@@ -343,6 +349,11 @@ WorkspaceWindowResizer::SnapType GetSnapType( ...@@ -343,6 +349,11 @@ WorkspaceWindowResizer::SnapType GetSnapType(
return WorkspaceWindowResizer::SnapType::kNone; return WorkspaceWindowResizer::SnapType::kNone;
} }
void CrossFadeAnimation(aura::Window* window, const gfx::Rect& target_bounds) {
CrossFadeAnimationAnimateNewLayerOnly(window, target_bounds,
kCrossFadeDuration, gfx::Tween::LINEAR);
}
} // namespace } // namespace
std::unique_ptr<WindowResizer> CreateWindowResizer( std::unique_ptr<WindowResizer> CreateWindowResizer(
...@@ -527,6 +538,7 @@ void WorkspaceWindowResizer::Drag(const gfx::PointF& location_in_parent, ...@@ -527,6 +538,7 @@ void WorkspaceWindowResizer::Drag(const gfx::PointF& location_in_parent,
// restored (i.e. update the caption buttons and height of the browser // restored (i.e. update the caption buttons and height of the browser
// frame). // frame).
window_state()->window()->SetProperty(kFrameRestoreLookKey, true); window_state()->window()->SetProperty(kFrameRestoreLookKey, true);
CrossFadeAnimation(window_state()->window(), bounds);
} }
} }
RestackWindows(); RestackWindows();
...@@ -624,8 +636,8 @@ void WorkspaceWindowResizer::CompleteDrag() { ...@@ -624,8 +636,8 @@ void WorkspaceWindowResizer::CompleteDrag() {
// no-op, so reset the bounds manually here. // no-op, so reset the bounds manually here.
if (window_state()->IsMaximized()) { if (window_state()->IsMaximized()) {
aura::Window* window = window_state()->window(); aura::Window* window = window_state()->window();
window->SetBounds( CrossFadeAnimation(
screen_util::GetMaximizedWindowBoundsInParent(window)); window, screen_util::GetMaximizedWindowBoundsInParent(window));
} }
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment