summaryrefslogtreecommitdiffstats
path: root/ash/wm
diff options
context:
space:
mode:
Diffstat (limited to 'ash/wm')
-rw-r--r--ash/wm/property_util.cc4
-rw-r--r--ash/wm/property_util.h4
-rw-r--r--ash/wm/window_properties.cc2
-rw-r--r--ash/wm/window_properties.h10
-rw-r--r--ash/wm/window_util.cc29
-rw-r--r--ash/wm/window_util.h25
-rw-r--r--ash/wm/workspace/auto_window_management.cc178
-rw-r--r--ash/wm/workspace/auto_window_management.h20
-rw-r--r--ash/wm/workspace/workspace_layout_manager2_unittest.cc7
-rw-r--r--ash/wm/workspace/workspace_manager2.cc17
-rw-r--r--ash/wm/workspace/workspace_manager2_unittest.cc285
-rw-r--r--ash/wm/workspace/workspace_window_resizer.cc1
12 files changed, 559 insertions, 23 deletions
diff --git a/ash/wm/property_util.cc b/ash/wm/property_util.cc
index d52b196..6352797 100644
--- a/ash/wm/property_util.cc
+++ b/ash/wm/property_util.cc
@@ -48,7 +48,7 @@ void SetTrackedByWorkspace(aura::Window* window, bool value) {
window->SetProperty(internal::kWindowTrackedByWorkspaceKey, value);
}
-bool GetTrackedByWorkspace(aura::Window* window) {
+bool GetTrackedByWorkspace(const aura::Window* window) {
return window->GetProperty(internal::kWindowTrackedByWorkspaceKey);
}
@@ -56,7 +56,7 @@ void SetIgnoredByShelf(aura::Window* window, bool value) {
window->SetProperty(internal::kIgnoredByShelfKey, value);
}
-bool GetIgnoredByShelf(aura::Window* window) {
+bool GetIgnoredByShelf(const aura::Window* window) {
return window->GetProperty(internal::kIgnoredByShelfKey);
}
diff --git a/ash/wm/property_util.h b/ash/wm/property_util.h
index dde0335..48f6e53 100644
--- a/ash/wm/property_util.h
+++ b/ash/wm/property_util.h
@@ -50,14 +50,14 @@ enum WindowPersistsAcrossAllWorkspacesType {
// Sets whether |window| is ignored when determining whether the shelf should
// be darkened when overlapped.
ASH_EXPORT void SetIgnoredByShelf(aura::Window* window, bool value);
-ASH_EXPORT bool GetIgnoredByShelf(aura::Window* window);
+ASH_EXPORT bool GetIgnoredByShelf(const aura::Window* window);
// Sets whether the specified window is tracked by workspace code. Default is
// true. If set to false the workspace does not switch the current workspace,
// nor does it attempt to impose constraints on the bounds of the window. This
// is intended for tab dragging.
ASH_EXPORT void SetTrackedByWorkspace(aura::Window* window, bool value);
-ASH_EXPORT bool GetTrackedByWorkspace(aura::Window* window);
+ASH_EXPORT bool GetTrackedByWorkspace(const aura::Window* window);
// Makes |window| persist across all workspaces. The default is controlled
// by SetDefaultPersistsAcrossAllWorkspaces().
diff --git a/ash/wm/window_properties.cc b/ash/wm/window_properties.cc
index 6aad28a..560d14f 100644
--- a/ash/wm/window_properties.cc
+++ b/ash/wm/window_properties.cc
@@ -40,9 +40,11 @@ DEFINE_OWNED_WINDOW_PROPERTY_KEY(ui_controls::UIControlsAura,
kUIControlsKey,
NULL);
DEFINE_WINDOW_PROPERTY_KEY(bool, kUsesScreenCoordinatesKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kUserChangedWindowPositionOrSizeKey, false);
DEFINE_WINDOW_PROPERTY_KEY(ash::WindowPersistsAcrossAllWorkspacesType,
kWindowPersistsAcrossAllWorkspacesKey,
WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_DEFAULT);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kWindowPositionManagedKey, false);
DEFINE_WINDOW_PROPERTY_KEY(bool, kWindowTrackedByWorkspaceKey, true);
} // namespace internal
diff --git a/ash/wm/window_properties.h b/ash/wm/window_properties.h
index 8677359..ae5a9c9 100644
--- a/ash/wm/window_properties.h
+++ b/ash/wm/window_properties.h
@@ -67,12 +67,22 @@ ASH_EXPORT extern const aura::WindowProperty<bool>* const
extern const aura::WindowProperty<ui_controls::UIControlsAura*>* const
kUIControlsKey;
+// A property key to remember if a windows position or size was changed by a
+// user.
+ASH_EXPORT extern const aura::WindowProperty<bool>* const
+ kUserChangedWindowPositionOrSizeKey;
+
// Property to tell if the container uses the screen coordinates.
extern const aura::WindowProperty<bool>* const kUsesScreenCoordinatesKey;
extern const aura::WindowProperty<WindowPersistsAcrossAllWorkspacesType>* const
kWindowPersistsAcrossAllWorkspacesKey;
+// A property key to remember if a windows position can be managed by the
+// workspace manager or not.
+ASH_EXPORT extern const aura::WindowProperty<bool>* const
+ kWindowPositionManagedKey;
+
// True if the window is controlled by the workspace manager.
extern const aura::WindowProperty<bool>* const
kWindowTrackedByWorkspaceKey;
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index b3a9b5d..97e2761 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "ash/ash_constants.h"
#include "ash/shell.h"
#include "ash/wm/activation_controller.h"
#include "ash/wm/window_properties.h"
@@ -73,11 +74,11 @@ bool CanActivateWindow(aura::Window* window) {
return client && client->CanActivateWindow(window);
}
-bool CanMaximizeWindow(aura::Window* window) {
+bool CanMaximizeWindow(const aura::Window* window) {
return window->GetProperty(aura::client::kCanMaximizeKey);
}
-bool IsWindowNormal(aura::Window* window) {
+bool IsWindowNormal(const aura::Window* window) {
return IsWindowStateNormal(window->GetProperty(aura::client::kShowStateKey));
}
@@ -85,17 +86,17 @@ bool IsWindowStateNormal(ui::WindowShowState state) {
return state == ui::SHOW_STATE_NORMAL || state == ui::SHOW_STATE_DEFAULT;
}
-bool IsWindowMaximized(aura::Window* window) {
+bool IsWindowMaximized(const aura::Window* window) {
return window->GetProperty(aura::client::kShowStateKey) ==
ui::SHOW_STATE_MAXIMIZED;
}
-bool IsWindowMinimized(aura::Window* window) {
+bool IsWindowMinimized(const aura::Window* window) {
return window->GetProperty(aura::client::kShowStateKey) ==
ui::SHOW_STATE_MINIMIZED;
}
-bool IsWindowFullscreen(aura::Window* window) {
+bool IsWindowFullscreen(const aura::Window* window) {
return window->GetProperty(aura::client::kShowStateKey) ==
ui::SHOW_STATE_FULLSCREEN;
}
@@ -153,5 +154,23 @@ void DeepDeleteLayers(ui::Layer* layer) {
delete layer;
}
+bool IsWindowPositionManaged(const aura::Window* window) {
+ return window->GetProperty(ash::internal::kWindowPositionManagedKey);
+}
+
+void SetWindowPositionManaged(aura::Window* window, bool managed) {
+ window->SetProperty(ash::internal::kWindowPositionManagedKey, managed);
+}
+
+bool HasUserChangedWindowPositionOrSize(const aura::Window* window) {
+ return window->GetProperty(
+ ash::internal::kUserChangedWindowPositionOrSizeKey);
+}
+
+void SetUserHasChangedWindowPositionOrSize(aura::Window* window, bool changed) {
+ window->SetProperty(ash::internal::kUserChangedWindowPositionOrSizeKey,
+ changed);
+}
+
} // namespace wm
} // namespace ash
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index 081a40e..7a9e3f0 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -39,22 +39,22 @@ ASH_EXPORT aura::Window* GetActivatableWindow(aura::Window* window);
ASH_EXPORT bool IsActiveWindowFullscreen();
// Returns true if |window| can be maximized.
-ASH_EXPORT bool CanMaximizeWindow(aura::Window* window);
+ASH_EXPORT bool CanMaximizeWindow(const aura::Window* window);
// Returns true if |window| is normal or default.
-ASH_EXPORT bool IsWindowNormal(aura::Window* window);
+ASH_EXPORT bool IsWindowNormal(const aura::Window* window);
// Returns true if |state| is normal or default.
-ASH_EXPORT bool IsWindowStateNormal(ui::WindowShowState state);
+ASH_EXPORT bool IsWindowStateNormal(const ui::WindowShowState state);
// Returns true if |window| is in the maximized state.
-ASH_EXPORT bool IsWindowMaximized(aura::Window* window);
+ASH_EXPORT bool IsWindowMaximized(const aura::Window* window);
// Returns true if |window| is minimized.
-ASH_EXPORT bool IsWindowMinimized(aura::Window* window);
+ASH_EXPORT bool IsWindowMinimized(const aura::Window* window);
// Returns true if |window| is in the fullscreen state.
-ASH_EXPORT bool IsWindowFullscreen(aura::Window* window);
+ASH_EXPORT bool IsWindowFullscreen(const aura::Window* window);
// Maximizes |window|, which must not be NULL.
ASH_EXPORT void MaximizeWindow(aura::Window* window);
@@ -86,6 +86,19 @@ ASH_EXPORT ui::Layer* RecreateWindowLayers(aura::Window* window,
// Deletes |layer| and all its child layers.
ASH_EXPORT void DeepDeleteLayers(ui::Layer* layer);
+// Returns true if |window|'s position can automatically be managed.
+ASH_EXPORT bool IsWindowPositionManaged(const aura::Window* window);
+
+// Change the |window|'s position manageability to |managed|.
+ASH_EXPORT void SetWindowPositionManaged(aura::Window* window, bool managed);
+
+// Returns true if the user has changed the |window|'s position or size.
+ASH_EXPORT bool HasUserChangedWindowPositionOrSize(const aura::Window* window);
+
+// Marks a |window|'s coordinates to be changed by a user.
+ASH_EXPORT void SetUserHasChangedWindowPositionOrSize(aura::Window* window,
+ bool changed);
+
} // namespace wm
} // namespace ash
diff --git a/ash/wm/workspace/auto_window_management.cc b/ash/wm/workspace/auto_window_management.cc
new file mode 100644
index 0000000..ea2f89f
--- /dev/null
+++ b/ash/wm/workspace/auto_window_management.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 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/workspace/auto_window_management.h"
+
+#include "ash/ash_switches.h"
+#include "ash/shell.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/window_animations.h"
+#include "ash/wm/window_util.h"
+#include "base/command_line.h"
+#include "ui/aura/window.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/screen.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+// The time in milliseconds which should be used to visually move a window
+// through an automatic "intelligent" window management option.
+const int kWindowAutoMoveDurationMS = 125;
+
+// Check if any management should be performed (with a given |window|).
+bool UseAutoWindowMagerForWindow(const aura::Window* window) {
+ return !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshDisableAutoWindowPlacement) &&
+ GetTrackedByWorkspace(window) &&
+ wm::IsWindowPositionManaged(window);
+}
+
+// Check if a given |window| can be managed. This includes that it's state is
+// not minimized/maximized/the user has changed it's size by hand already.
+// It furthermore checks for the WindowIsManaged status.
+bool WindowPositionCanBeManaged(const aura::Window* window) {
+ return (wm::IsWindowPositionManaged(window) &&
+ !wm::IsWindowMinimized(window) &&
+ !wm::IsWindowMaximized(window) &&
+ !wm::HasUserChangedWindowPositionOrSize(window));
+}
+
+// Given a |window|, return the only |other_window| which has an impact on
+// the automated windows location management. If there is more then one window,
+// false is returned, but the |other_window| will be set to the first one
+// found.
+// If the return value is true a single window was found.
+bool GetOtherVisibleAndManageableWindow(const aura::Window* window,
+ aura::Window** other_window) {
+ *other_window = NULL;
+ const aura::Window::Windows& windows = window->parent()->children();
+ // Find a single open managed window.
+ for (size_t i = 0; i < windows.size(); i++) {
+ aura::Window* iterated_window = windows[i];
+ if (window != iterated_window &&
+ iterated_window->type() == aura::client::WINDOW_TYPE_NORMAL &&
+ iterated_window->TargetVisibility() &&
+ wm::IsWindowPositionManaged(iterated_window)) {
+ // Bail if we find a second usable window.
+ if (*other_window)
+ return false;
+ *other_window = iterated_window;
+ }
+ }
+ return *other_window != NULL;
+}
+
+// Get the work area for a given |window|.
+gfx::Rect GetWorkAreaForWindow(const aura::Window* window) {
+ gfx::Rect work_area = gfx::Rect(window->parent()->bounds().size());
+ work_area.Inset(Shell::GetScreen()->GetDisplayMatching(
+ work_area).GetWorkAreaInsets());
+ return work_area;
+}
+
+// Move the given |bounds| on the available |parent_width| to the
+// direction. If |move_right| is true, the rectangle gets moved to the right
+// corner, otherwise to the left one.
+bool MoveRectToOneSide(int parent_width, bool move_right, gfx::Rect* bounds) {
+ if (move_right) {
+ if (parent_width > bounds->right()) {
+ bounds->set_x(parent_width - bounds->width());
+ return true;
+ }
+ } else {
+ if (0 < bounds->x()) {
+ bounds->set_x(0);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Move a |window| to a new |bound|. Animate if desired by user.
+// Note: The function will do nothing if the bounds did not change.
+void SetBoundsAnimated(aura::Window* window, const gfx::Rect& bounds) {
+ if (bounds == window->GetTargetBounds())
+ return;
+
+ if (window->GetProperty(aura::client::kAnimationsDisabledKey) ||
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ ash::switches::kAshWindowAnimationsDisabled)) {
+ window->SetBounds(bounds);
+ return;
+ }
+
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS));
+ window->SetBounds(bounds);
+}
+
+} // namespace
+
+void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) {
+ if (!UseAutoWindowMagerForWindow(removed_window))
+ return;
+ // Find a single open browser window.
+ aura::Window* other_shown_window = NULL;
+ if (!GetOtherVisibleAndManageableWindow(removed_window,
+ &other_shown_window) ||
+ !WindowPositionCanBeManaged(other_shown_window))
+ return;
+ // Center the window (only in x).
+ gfx::Rect work_area = GetWorkAreaForWindow(removed_window);
+ gfx::Rect bounds = other_shown_window->bounds();
+ bounds.set_x((work_area.width() - bounds.width()) / 2);
+ SetBoundsAnimated(other_shown_window, bounds);
+}
+
+void RearrangeVisibleWindowOnShow(aura::Window* added_window) {
+ if (!UseAutoWindowMagerForWindow(added_window) ||
+ wm::HasUserChangedWindowPositionOrSize(added_window) ||
+ !added_window->TargetVisibility())
+ return;
+ // Find a single open managed window.
+ aura::Window* other_shown_window = NULL;
+ if (!GetOtherVisibleAndManageableWindow(added_window,
+ &other_shown_window)) {
+ // It could be that this window is the first window joining the workspace.
+ if (!WindowPositionCanBeManaged(added_window) || other_shown_window)
+ return;
+
+ // If so we have to make sure it is centered.
+ gfx::Rect work_area = GetWorkAreaForWindow(added_window);
+ gfx::Rect bounds = added_window->bounds();
+ bounds.set_x((work_area.width() - bounds.width()) / 2);
+ added_window->SetBounds(bounds);
+ return;
+ }
+
+ // When going from one to two windows both windows loose their "positioned
+ // by user" flags.
+ ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false);
+ ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false);
+
+ if (WindowPositionCanBeManaged(other_shown_window)) {
+ gfx::Rect work_area = GetWorkAreaForWindow(added_window);
+
+ // Push away the other window.
+ gfx::Rect other_bounds = other_shown_window->bounds();
+ bool move_right = other_bounds.CenterPoint().x() < work_area.width() / 2;
+ if (MoveRectToOneSide(work_area.width(), move_right, &other_bounds))
+ SetBoundsAnimated(other_shown_window, other_bounds);
+
+ // Push the new window also to the opposite location (if needed).
+ // Since it is just coming into view, we do not need to animate it.
+ gfx::Rect added_bounds = added_window->bounds();
+ if (MoveRectToOneSide(work_area.width(), !move_right, &added_bounds))
+ added_window->SetBounds(added_bounds);
+ }
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/workspace/auto_window_management.h b/ash/wm/workspace/auto_window_management.h
new file mode 100644
index 0000000..138cb54
--- /dev/null
+++ b/ash/wm/workspace/auto_window_management.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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 "ui/aura/window.h"
+
+namespace ash {
+namespace internal {
+
+// Check if after removal or hide of the given |removed_window| an automated
+// desktop location management can be performed and rearrange accordingly.
+void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window);
+
+// Check if after insertion or showing of the given |added_window| an automated
+// desktop location management can be performed and rearrange accordingly.
+void RearrangeVisibleWindowOnShow(aura::Window* added_window);
+
+} // namespace internal
+} // namespace ash
+
diff --git a/ash/wm/workspace/workspace_layout_manager2_unittest.cc b/ash/wm/workspace/workspace_layout_manager2_unittest.cc
index b559a3c..13496f0 100644
--- a/ash/wm/workspace/workspace_layout_manager2_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager2_unittest.cc
@@ -13,9 +13,7 @@
#include "ui/aura/root_window.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
-#include "ui/gfx/display.h"
#include "ui/gfx/insets.h"
-#include "ui/gfx/screen.h"
namespace ash {
@@ -147,15 +145,14 @@ TEST_F(WorkspaceLayoutManager2Test, SizeToWorkArea) {
100, 101, work_area.width() + 1, work_area.height() + 2);
scoped_ptr<aura::Window> window(CreateTestWindow(window_bounds));
EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
- window->bounds().ToString());
+ window->bounds().ToString());
// Directly setting the bounds triggers a slightly different code path. Verify
// that too.
window->SetBounds(window_bounds);
EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
- window->bounds().ToString());
+ window->bounds().ToString());
}
} // namespace
-
} // namespace ash
diff --git a/ash/wm/workspace/workspace_manager2.cc b/ash/wm/workspace/workspace_manager2.cc
index 68250c3..4a46cb72 100644
--- a/ash/wm/workspace/workspace_manager2.cc
+++ b/ash/wm/workspace/workspace_manager2.cc
@@ -17,6 +17,7 @@
#include "ash/wm/window_animations.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/auto_window_management.h"
#include "ash/wm/workspace/desktop_background_fade_controller.h"
#include "ash/wm/workspace/workspace_animations.h"
#include "ash/wm/workspace/workspace_layout_manager2.h"
@@ -593,10 +594,14 @@ void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace,
// to the workspace.
if (workspace == active_workspace_)
UpdateShelfVisibility();
+
+ RearrangeVisibleWindowOnShow(child);
}
void WorkspaceManager2::OnWillRemoveWindowFromWorkspace(Workspace2* workspace,
Window* child) {
+ if (child->TargetVisibility())
+ RearrangeVisibleWindowOnHideOrRemove(child);
child->ClearProperty(kWorkspaceKey);
}
@@ -609,10 +614,16 @@ void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace,
void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged(
Workspace2* workspace,
Window* child) {
- if (workspace->ShouldMoveToPending())
+ if (workspace->ShouldMoveToPending()) {
MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_VISIBILITY_CHANGED);
- else if (workspace == active_workspace_)
- UpdateShelfVisibility();
+ } else {
+ if (child->TargetVisibility())
+ RearrangeVisibleWindowOnShow(child);
+ else
+ RearrangeVisibleWindowOnHideOrRemove(child);
+ if (workspace == active_workspace_)
+ UpdateShelfVisibility();
+ }
}
void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged(
diff --git a/ash/wm/workspace/workspace_manager2_unittest.cc b/ash/wm/workspace/workspace_manager2_unittest.cc
index 70a39e6..29e9232 100644
--- a/ash/wm/workspace/workspace_manager2_unittest.cc
+++ b/ash/wm/workspace/workspace_manager2_unittest.cc
@@ -1107,5 +1107,290 @@ TEST_F(WorkspaceManager2Test, DeactivateDropsToDesktop) {
ASSERT_EQ("1 M1 active=1", StateString());
}
+// Test the basic auto placement of one and or two windows in a "simulated
+// session" of sequential window operations.
+TEST_F(WorkspaceManager2Test, BasicAutoPlacing) {
+ // Test 1: In case there is no manageable window, no window should shift.
+
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ gfx::Rect desktop_area = window1->parent()->bounds();
+
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ // Trigger the auto window placement function by making it visible.
+ // Note that the bounds are getting changed while it is invisible.
+ window2->Hide();
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+ window2->Show();
+
+ // Check the initial position of the windows is unchanged.
+ EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
+
+ // Remove the second window and make sure that the first window
+ // does NOT get centered.
+ window2.reset();
+ EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
+
+ // Test 2: Set up two managed windows and check their auto positioning.
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ scoped_ptr<aura::Window> window3(
+ aura::test::CreateTestWindowWithId(2, NULL));
+ ash::wm::SetWindowPositionManaged(window3.get(), true);
+ // To avoid any auto window manager changes due to SetBounds, the window
+ // gets first hidden and then shown again.
+ window3->Hide();
+ window3->SetBounds(gfx::Rect(32, 48, 256, 512));
+ window3->Show();
+ // |window1| should be flush right and |window3| flush left.
+ EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window3->bounds().width()) +
+ ",48 256x512", window3->bounds().ToString());
+
+ // After removing |window3|, |window1| should be centered again.
+ window3.reset();
+ EXPECT_EQ(
+ base::IntToString(
+ (desktop_area.width() - window1->bounds().width()) / 2) +
+ ",32 640x320", window1->bounds().ToString());
+
+ // Test 3: Set up a manageable and a non manageable window and check
+ // positioning.
+ scoped_ptr<aura::Window> window4(
+ aura::test::CreateTestWindowWithId(3, NULL));
+ // To avoid any auto window manager changes due to SetBounds, the window
+ // gets first hidden and then shown again.
+ window1->Hide();
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ window4->SetBounds(gfx::Rect(32, 48, 256, 512));
+ window1->Show();
+ // |window1| should be centered and |window4| untouched.
+ EXPECT_EQ(
+ base::IntToString(
+ (desktop_area.width() - window1->bounds().width()) / 2) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
+
+ // Test4: A single manageable window should get centered.
+ window4.reset();
+ ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), false);
+ // Trigger the auto window placement function by showing (and hiding) it.
+ window1->Hide();
+ window1->Show();
+ // |window1| should be centered.
+ EXPECT_EQ(
+ base::IntToString(
+ (desktop_area.width() - window1->bounds().width()) / 2) +
+ ",32 640x320", window1->bounds().ToString());
+}
+
+// Test the proper usage of user window movement interaction.
+TEST_F(WorkspaceManager2Test, TestUserMovedWindowRepositioning) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ gfx::Rect desktop_area = window1->parent()->bounds();
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+ window1->Hide();
+ window2->Hide();
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
+ EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
+
+ // Check that the current location gets preserved if the user has
+ // positioned it previously.
+ ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
+ window1->Show();
+ EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
+ // Flag should be still set.
+ EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
+ EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
+
+ // Turn on the second window and make sure that both windows are now
+ // positionable again (user movement cleared).
+ window2->Show();
+
+ // |window1| should be flush right and |window3| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+ // FLag should now be reset.
+ EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
+ EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
+
+ // Going back to one shown window should keep the state.
+ ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
+ window2->Hide();
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
+}
+
+// Test that a window from normal to minimize will repos the remaining.
+TEST_F(WorkspaceManager2Test, ToMinimizeRepositionsRemaining) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ gfx::Rect desktop_area = window1->parent()->bounds();
+
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+
+ ash::wm::MinimizeWindow(window1.get());
+
+ // |window2| should be centered now.
+ EXPECT_TRUE(window2->IsVisible());
+ EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
+ EXPECT_EQ(base::IntToString(
+ (desktop_area.width() - window2->bounds().width()) / 2) +
+ ",48 256x512", window2->bounds().ToString());
+
+ ash::wm::RestoreWindow(window1.get());
+ // |window1| should be flush right and |window3| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+}
+
+// Test that minimizing an initially maximized window will repos the remaining.
+TEST_F(WorkspaceManager2Test, MaxToMinRepositionsRemaining) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ gfx::Rect desktop_area = window1->parent()->bounds();
+
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+
+ ash::wm::MaximizeWindow(window1.get());
+ ash::wm::MinimizeWindow(window1.get());
+
+ // |window2| should be centered now.
+ EXPECT_TRUE(window2->IsVisible());
+ EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
+ EXPECT_EQ(base::IntToString(
+ (desktop_area.width() - window2->bounds().width()) / 2) +
+ ",48 256x512", window2->bounds().ToString());
+}
+
+// Test that nomral, maximize, minimizing will repos the remaining.
+TEST_F(WorkspaceManager2Test, NormToMaxToMinRepositionsRemaining) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ gfx::Rect desktop_area = window1->parent()->bounds();
+
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+
+ // Trigger the auto window placement function by showing (and hiding) it.
+ window1->Hide();
+ window1->Show();
+
+ // |window1| should be flush right and |window3| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+
+ ash::wm::MaximizeWindow(window1.get());
+ ash::wm::MinimizeWindow(window1.get());
+
+ // |window2| should be centered now.
+ EXPECT_TRUE(window2->IsVisible());
+ EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
+ EXPECT_EQ(base::IntToString(
+ (desktop_area.width() - window2->bounds().width()) / 2) +
+ ",48 256x512", window2->bounds().ToString());
+}
+
+// Test that nomral, maximize, normal will repos the remaining.
+TEST_F(WorkspaceManager2Test, NormToMaxToNormRepositionsRemaining) {
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ gfx::Rect desktop_area = window1->parent()->bounds();
+
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+
+ // Trigger the auto window placement function by showing (and hiding) it.
+ window1->Hide();
+ window1->Show();
+
+ // |window1| should be flush right and |window3| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+
+ ash::wm::MaximizeWindow(window1.get());
+ ash::wm::RestoreWindow(window1.get());
+
+ // |window1| should be flush right and |window2| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+}
+
+// Test that animations are triggered.
+TEST_F(WorkspaceManager2Test, AnimatedNormToMaxToNormRepositionsRemaining) {
+ ui::LayerAnimator::set_disable_animations_for_test(false);
+ scoped_ptr<aura::Window> window1(
+ aura::test::CreateTestWindowWithId(0, NULL));
+ window1->Hide();
+ window1->SetBounds(gfx::Rect(16, 32, 640, 320));
+ gfx::Rect desktop_area = window1->parent()->bounds();
+ scoped_ptr<aura::Window> window2(
+ aura::test::CreateTestWindowWithId(1, NULL));
+ window2->Hide();
+ window2->SetBounds(gfx::Rect(32, 48, 256, 512));
+
+ ash::wm::SetWindowPositionManaged(window1.get(), true);
+ ash::wm::SetWindowPositionManaged(window2.get(), true);
+ // Make sure nothing is animating.
+ window1->layer()->GetAnimator()->StopAnimating();
+ window2->layer()->GetAnimator()->StopAnimating();
+ window2->Show();
+
+ // The second window should now animate.
+ EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
+ EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
+ window2->layer()->GetAnimator()->StopAnimating();
+
+ window1->Show();
+ EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
+ EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
+
+ window1->layer()->GetAnimator()->StopAnimating();
+ window2->layer()->GetAnimator()->StopAnimating();
+ // |window1| should be flush right and |window2| flush left.
+ EXPECT_EQ(base::IntToString(
+ desktop_area.width() - window1->bounds().width()) +
+ ",32 640x320", window1->bounds().ToString());
+ EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 4185c442..4a7e9cc 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -313,6 +313,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent,
}
void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
+ wm::SetUserHasChangedWindowPositionOrSize(details_.window, true);
window()->layer()->SetOpacity(details_.initial_opacity);
drag_phantom_window_controller_.reset();
snap_phantom_window_controller_.reset();