Commit d954b810 authored by flackr's avatar flackr Committed by Commit bot

Defer maximize mode bounds updates until after exiting overview.

When we enter overview the shelf is revealed which causes a workspace bounds
change. Normally the maximize mode window manager would update all active
windows to have the new bounds but this effectively undoes the transformation
applied to the window to show it in overview. As such, we defer updating the
bounds until we exit overview mode.

BUG=401664
TEST=WindowSelectorTest.FullscreenWindowMaximizeMode, WindowAnimationsTest.CrossFadeToBoundsFromTransform
TEST=Repro steps in http://crbug.com/401664

Review URL: https://codereview.chromium.org/588193003

Cr-Commit-Position: refs/heads/master@{#297455}
parent 38b0a899
......@@ -81,6 +81,7 @@ void MaximizeModeWindowManager::OnOverviewModeStarting() {
return;
EnableBackdropBehindTopWindowOnEachDisplay(false);
SetDeferBoundsUpdates(true);
backdrops_hidden_ = true;
}
......@@ -90,6 +91,7 @@ void MaximizeModeWindowManager::OnOverviewModeEnding() {
backdrops_hidden_ = false;
EnableBackdropBehindTopWindowOnEachDisplay(true);
SetDeferBoundsUpdates(false);
}
void MaximizeModeWindowManager::OnWindowDestroying(aura::Window* window) {
......@@ -194,6 +196,15 @@ void MaximizeModeWindowManager::RestoreAllWindows() {
ForgetWindow(window_state_map_.begin()->first);
}
void MaximizeModeWindowManager::SetDeferBoundsUpdates(
bool defer_bounds_updates) {
for (WindowToState::iterator it = window_state_map_.begin();
it != window_state_map_.end();
++it) {
it->second->SetDeferBoundsUpdates(defer_bounds_updates);
}
}
void MaximizeModeWindowManager::MaximizeAndTrackWindow(
aura::Window* window) {
if (!ShouldHandleWindow(window))
......
......@@ -86,6 +86,10 @@ class ASH_EXPORT MaximizeModeWindowManager : public aura::WindowObserver,
// Restore all windows to their previous state.
void RestoreAllWindows();
// Set whether to defer bounds updates on all tracked windows. When set to
// false bounds will be updated as they may be stale.
void SetDeferBoundsUpdates(bool defer_bounds_updates);
// If the given window should be handled by us, this function will maximize it
// and add it to the list of known windows (remembering the initial show
// state).
......
......@@ -20,6 +20,7 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
#include "ui/views/view_constants_aura.h"
......@@ -98,7 +99,8 @@ MaximizeModeWindowState::MaximizeModeWindowState(
aura::Window* window, MaximizeModeWindowManager* creator)
: window_(window),
creator_(creator),
current_state_type_(wm::GetWindowState(window)->GetStateType()) {
current_state_type_(wm::GetWindowState(window)->GetStateType()),
defer_bounds_updates_(false) {
old_state_.reset(
wm::GetWindowState(window)->SetStateObject(
scoped_ptr<State>(this).Pass()).release());
......@@ -114,6 +116,15 @@ void MaximizeModeWindowState::LeaveMaximizeMode(wm::WindowState* window_state) {
window_state->SetStateObject(old_state_.Pass());
}
void MaximizeModeWindowState::SetDeferBoundsUpdates(bool defer_bounds_updates) {
if (defer_bounds_updates_ == defer_bounds_updates)
return;
defer_bounds_updates_ = defer_bounds_updates;
if (!defer_bounds_updates_)
UpdateBounds(wm::GetWindowState(window_), true);
}
void MaximizeModeWindowState::OnWMEvent(wm::WindowState* window_state,
const wm::WMEvent* event) {
switch (event->type()) {
......@@ -280,6 +291,8 @@ wm::WindowStateType MaximizeModeWindowState::GetMaximizedOrCenteredWindowType(
void MaximizeModeWindowState::UpdateBounds(wm::WindowState* window_state,
bool animated) {
if (defer_bounds_updates_)
return;
gfx::Rect bounds_in_parent = GetBoundsInMaximizedMode(window_state);
// If we have a target bounds rectangle, we center it and set it
// accordingly.
......
......@@ -31,6 +31,10 @@ class MaximizeModeWindowState : public wm::WindowState::State {
// Leaves the maximize mode by reverting to previous state object.
void LeaveMaximizeMode(wm::WindowState* window_state);
// Sets whether to ignore bounds updates. If set to false, immediately does a
// bounds update as the current window bounds may no longer be correct.
void SetDeferBoundsUpdates(bool defer_bounds_updates);
// WindowState::State overrides:
virtual void OnWMEvent(wm::WindowState* window_state,
const wm::WMEvent* event) OVERRIDE;
......@@ -71,6 +75,9 @@ class MaximizeModeWindowState : public wm::WindowState::State {
// WM_STATE_TYPE{NORMAL, MINIMIZED, MAXIMIZED}.
wm::WindowStateType current_state_type_;
// If true, do not update bounds.
bool defer_bounds_updates_;
DISALLOW_COPY_AND_ASSIGN(MaximizeModeWindowState);
};
......
......@@ -17,6 +17,7 @@
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/maximize_mode/maximize_mode_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/window_grid.h"
#include "ash/wm/overview/window_selector.h"
......@@ -434,6 +435,46 @@ TEST_F(WindowSelectorTest, OverviewUndimsShelf) {
EXPECT_TRUE(shelf->GetDimsShelf());
}
// Tests that entering overview when a fullscreen window is active in maximized
// mode correctly applies the transformations to the window and correctly
// updates the window bounds on exiting overview mode: http://crbug.com/401664.
TEST_F(WindowSelectorTest, FullscreenWindowMaximizeMode) {
gfx::Rect bounds(0, 0, 400, 400);
scoped_ptr<aura::Window> window1(CreateWindow(bounds));
scoped_ptr<aura::Window> window2(CreateWindow(bounds));
Shell::GetInstance()->maximize_mode_controller()->
EnableMaximizeModeWindowManager(true);
wm::ActivateWindow(window2.get());
wm::ActivateWindow(window1.get());
gfx::Rect normal_window_bounds(window1->bounds());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window1.get())->OnWMEvent(&toggle_fullscreen_event);
gfx::Rect fullscreen_window_bounds(window1->bounds());
EXPECT_NE(normal_window_bounds.ToString(),
fullscreen_window_bounds.ToString());
EXPECT_EQ(fullscreen_window_bounds.ToString(),
window2->GetTargetBounds().ToString());
ToggleOverview();
// Window 2 would normally resize to normal window bounds on showing the shelf
// for overview but this is deferred until overview is exited.
EXPECT_EQ(fullscreen_window_bounds.ToString(),
window2->GetTargetBounds().ToString());
EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
ToggleOverview();
// Since the fullscreen window is still active, window2 will still have the
// larger bounds.
EXPECT_EQ(fullscreen_window_bounds.ToString(),
window2->GetTargetBounds().ToString());
// Enter overview again and select window 2. Selecting window 2 should show
// the shelf bringing window2 back to the normal bounds.
ToggleOverview();
ClickWindow(window2.get());
EXPECT_EQ(normal_window_bounds.ToString(),
window2->GetTargetBounds().ToString());
}
// Tests that beginning window selection hides the app list.
TEST_F(WindowSelectorTest, SelectingHidesAppList) {
gfx::Rect bounds(0, 0, 400, 400);
......
......@@ -67,12 +67,12 @@ int64 Round64(float f) {
}
base::TimeDelta GetCrossFadeDuration(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::RectF& old_bounds,
const gfx::Rect& new_bounds) {
if (::wm::WindowAnimationsDisabled(window))
return base::TimeDelta();
int old_area = old_bounds.width() * old_bounds.height();
int old_area = static_cast<int>(old_bounds.width() * old_bounds.height());
int new_area = new_bounds.width() * new_bounds.height();
int max_area = std::max(old_area, new_area);
// Avoid divide by zero.
......@@ -327,17 +327,25 @@ base::TimeDelta CrossFadeAnimation(
gfx::Tween::Type tween_type) {
DCHECK(old_layer_owner->root());
const gfx::Rect old_bounds(old_layer_owner->root()->bounds());
gfx::RectF old_transformed_bounds(old_bounds);
gfx::Transform old_transform(old_layer_owner->root()->transform());
gfx::Transform old_transform_in_root;
old_transform_in_root.Translate(old_bounds.x(), old_bounds.y());
old_transform_in_root.PreconcatTransform(old_transform);
old_transform_in_root.Translate(-old_bounds.x(), -old_bounds.y());
old_transform_in_root.TransformRect(&old_transformed_bounds);
const gfx::Rect new_bounds(window->bounds());
const bool old_on_top = (old_bounds.width() > new_bounds.width());
// Shorten the animation if there's not much visual movement.
const base::TimeDelta duration = GetCrossFadeDuration(window,
old_bounds, new_bounds);
old_transformed_bounds, new_bounds);
// Scale up the old layer while translating to new position.
{
ui::Layer* old_layer = old_layer_owner->root();
old_layer->GetAnimator()->StopAnimating();
old_layer->SetTransform(old_transform);
ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
// Animation observer owns the old layer and deletes itself.
......@@ -365,12 +373,12 @@ base::TimeDelta CrossFadeAnimation(
// Set the new layer's current transform, such that the user sees a scaled
// version of the window with the original bounds at the original position.
gfx::Transform in_transform;
const float scale_x = static_cast<float>(old_bounds.width()) /
const float scale_x = old_transformed_bounds.width() /
static_cast<float>(new_bounds.width());
const float scale_y = static_cast<float>(old_bounds.height()) /
const float scale_y = old_transformed_bounds.height() /
static_cast<float>(new_bounds.height());
in_transform.Translate(old_bounds.x() - new_bounds.x(),
old_bounds.y() - new_bounds.y());
in_transform.Translate(old_transformed_bounds.x() - new_bounds.x(),
old_transformed_bounds.y() - new_bounds.y());
in_transform.Scale(scale_x, scale_y);
window->layer()->SetTransform(in_transform);
if (!old_on_top) {
......
......@@ -168,6 +168,46 @@ TEST_F(WindowAnimationsTest, CrossFadeToBounds) {
base::TimeDelta::FromSeconds(1));
}
// Tests that when crossfading from a window which has a transform that the
// crossfade starts from this transformed size rather than snapping the window
// to an identity transform and crossfading from there.
TEST_F(WindowAnimationsTest, CrossFadeToBoundsFromTransform) {
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
scoped_ptr<Window> window(CreateTestWindowInShellWithId(0));
window->SetBounds(gfx::Rect(10, 10, 320, 240));
gfx::Transform half_size;
half_size.Translate(10, 10);
half_size.Scale(0.5f, 0.5f);
window->SetTransform(half_size);
window->Show();
Layer* old_layer = window->layer();
EXPECT_EQ(1.0f, old_layer->GetTargetOpacity());
// Cross fade to a larger size, as in a maximize animation.
GetWindowState(window.get())->SetBoundsDirectCrossFade(
gfx::Rect(0, 0, 640, 480));
// Window's layer has been replaced.
EXPECT_NE(old_layer, window->layer());
// Original layer stays opaque and stretches to new size.
EXPECT_EQ(1.0f, old_layer->GetTargetOpacity());
EXPECT_EQ("10,10 320x240", old_layer->bounds().ToString());
EXPECT_EQ(half_size, old_layer->transform());
// New layer animates in from the old window's transformed size to the
// identity transform.
EXPECT_EQ(1.0f, window->layer()->GetTargetOpacity());
// Set up the transform necessary to start at the old windows transformed
// position.
gfx::Transform quarter_size_shifted;
quarter_size_shifted.Translate(20, 20);
quarter_size_shifted.Scale(0.25f, 0.25f);
EXPECT_EQ(quarter_size_shifted, window->layer()->transform());
EXPECT_EQ(gfx::Transform(), window->layer()->GetTargetTransform());
}
} // namespace wm
TEST_F(WindowAnimationsTest, LockAnimationDuration) {
......
......@@ -308,6 +308,8 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
friend class ash::MaximizeModeWindowState;
friend ASH_EXPORT WindowState* GetWindowState(aura::Window*);
FRIEND_TEST_ALL_PREFIXES(WindowAnimationsTest, CrossFadeToBounds);
FRIEND_TEST_ALL_PREFIXES(WindowAnimationsTest,
CrossFadeToBoundsFromTransform);
explicit WindowState(aura::Window* window);
......
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