summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/ash.gyp2
-rw-r--r--ash/ash_switches.cc4
-rw-r--r--ash/ash_switches.h1
-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
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/prefs/pref_service_browsertest.cc3
-rw-r--r--chrome/browser/sessions/session_restore.cc12
-rw-r--r--chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc5
-rw-r--r--chrome/browser/ui/views/frame/browser_frame_aura.cc21
-rw-r--r--chrome/browser/ui/views/frame/browser_frame_aura.h3
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller.cc35
-rw-r--r--chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc29
-rw-r--r--chrome/browser/ui/window_sizer/window_sizer_ash.cc18
-rw-r--r--chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc4
26 files changed, 688 insertions, 44 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 866ff28..e8f97b0 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -383,6 +383,8 @@
'wm/window_util.h',
'wm/workspace_controller.cc',
'wm/workspace_controller.h',
+ 'wm/workspace/auto_window_management.cc',
+ 'wm/workspace/auto_window_management.h',
'wm/workspace/colored_window_controller.cc',
'wm/workspace/colored_window_controller.h',
'wm/workspace/desktop_background_fade_controller.cc',
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 7ae43636..f9980e8 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -19,6 +19,10 @@ const char kAshConstrainPointerToRoot[] = "ash-constrain-pointer-to-root";
// Enable keyboard shortcuts useful for debugging.
const char kAshDebugShortcuts[] = "ash-debug-shortcuts";
+// Disable support for auto window placement.
+const char kAshDisableAutoWindowPlacement[] =
+ "ash-enable-auto-window-placement";
+
// Disables boot animation v2, go back to v1.
const char kAshDisableBootAnimation2[] = "ash-disable-boot-animation2";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 306219d..240bdc5 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -19,6 +19,7 @@ ASH_EXPORT extern const char kAshBootAnimationFunction2[];
ASH_EXPORT extern const char kAshBootAnimationFunction3[];
ASH_EXPORT extern const char kAshConstrainPointerToRoot[];
ASH_EXPORT extern const char kAshDebugShortcuts[];
+ASH_EXPORT extern const char kAshDisableAutoWindowPlacement[];
ASH_EXPORT extern const char kAshDisablePanelFitting[];
ASH_EXPORT extern const char kAshDisableBootAnimation2[];
ASH_EXPORT extern const char kAshEnableAdvancedGestures[];
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 55513af..58546a6 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"
@@ -61,11 +62,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));
}
@@ -73,17 +74,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;
}
@@ -141,5 +142,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 849f587..ae0023b 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -36,22 +36,22 @@ ASH_EXPORT bool CanActivateWindow(aura::Window* window);
ASH_EXPORT aura::Window* GetActivatableWindow(aura::Window* window);
// 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);
@@ -83,6 +83,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();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 812db92..46eb02a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6361,6 +6361,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_AURA_GOOGLE_DIALOG_FRAMES_DESCRIPTION" desc="Description for the flag to enable Google-style dialog frames.">
Enable Google-style dialog frames.
</message>
+ <message name="IDS_FLAGS_ASH_AUTO_WINDOW_PLACEMENT_NAME" desc="Name for the option to enable/disable the auto window placement functionality.">
+ Automatic window placement.
+ </message>
+ <message name="IDS_FLAGS_ASH_AUTO_WINDOW_PLACEMENT_DESCRIPTION" desc="Description for the option to enable/disable the auto window placement functionality.">
+ Disable automatic window placement for one and two browser / app windows.
+ </message>
<message name="IDS_FLAGS_AURA_DISABLE_HOLD_MOUSE_MOVES_NAME" desc="Title for the flag to disable throttling the rate of window resize.">
Throttle the rate of window resize.
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3a5f798..1f0c82f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -620,6 +620,13 @@ const Experiment kExperiments[] = {
kOsWin | kOsLinux | kOsCrOS,
SINGLE_VALUE_TYPE(ash::switches::kAuraGoogleDialogFrames)
},
+ {
+ "ash-disable-auto-window-placement",
+ IDS_FLAGS_ASH_AUTO_WINDOW_PLACEMENT_NAME,
+ IDS_FLAGS_ASH_AUTO_WINDOW_PLACEMENT_DESCRIPTION,
+ kOsWin | kOsLinux | kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshDisableAutoWindowPlacement)
+ },
#endif
{
"per-tile-painting",
diff --git a/chrome/browser/prefs/pref_service_browsertest.cc b/chrome/browser/prefs/pref_service_browsertest.cc
index da3556a..0054fa5 100644
--- a/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chrome/browser/prefs/pref_service_browsertest.cc
@@ -36,9 +36,8 @@ IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, PRE_Test) {
IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, Test) {
gfx::Rect bounds = browser()->window()->GetBounds();
-
gfx::Rect expected_bounds(gfx::Rect(20, 30, 400, 500));
- ASSERT_EQ(expected_bounds, bounds);
+ ASSERT_EQ(expected_bounds.ToString(), bounds.ToString());
}
#endif // defined(TOOLKIT_GTK)
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 40ebe27..4a134601 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -57,6 +57,9 @@
#include "base/win/metro.h"
#endif
+#if defined(USE_ASH)
+#include "ash/wm/window_util.h"
+#endif
using content::NavigationController;
using content::RenderWidgetHost;
using content::WebContents;
@@ -1020,7 +1023,16 @@ class SessionRestoreImpl : public content::NotificationObserver {
if (browser_ == browser)
return;
+#if defined(USE_ASH)
+ // Prevent the auto window management for this window on show.
+ ash::wm::SetUserHasChangedWindowPositionOrSize(
+ browser->window()->GetNativeWindow(), true);
+#endif
browser->window()->Show();
+#if defined(USE_ASH)
+ ash::wm::SetUserHasChangedWindowPositionOrSize(
+ browser->window()->GetNativeWindow(), false);
+#endif
browser->set_is_session_restore(false);
// TODO(jcampan): http://crbug.com/8123 we should not need to set the
diff --git a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
index 1af7f4c..ed01b8b 100644
--- a/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
+++ b/chrome/browser/ui/views/constrained_web_dialog_delegate_views.cc
@@ -129,6 +129,11 @@ class ConstrainedWebDialogDelegateViewViews
virtual views::View* GetContentsView() OVERRIDE {
return this;
}
+ virtual void OnWidgetMove() OVERRIDE {
+ GetWidget()->CenterWindow(
+ GetWidget()->non_client_view()->GetPreferredSize());
+ views::WidgetDelegate::OnWidgetMove();
+ }
// views::WebView overrides.
virtual bool AcceleratorPressed(
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.cc b/chrome/browser/ui/views/frame/browser_frame_aura.cc
index db9af3f..afc7c08 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.cc
@@ -23,6 +23,7 @@
#if defined(USE_ASH)
#include "ash/wm/property_util.h"
+#include "ash/wm/window_util.h"
#endif
#if !defined(OS_CHROMEOS)
@@ -109,6 +110,11 @@ BrowserFrameAura::BrowserFrameAura(BrowserFrame* browser_frame,
GetNativeWindow(),
ash::WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_NO);
}
+ // Turn on auto window management if we don't need an explicit bounds.
+ // This way the requested bounds are honored.
+ if (!browser_view->browser()->bounds_overridden() &&
+ !browser_view->browser()->is_session_restore())
+ SetWindowAutoManaged();
#endif
}
@@ -160,8 +166,13 @@ void BrowserFrameAura::OnWindowTargetVisibilityChanged(bool visible) {
// RestoreFocus() when we become visible, which results in the web contents
// being asked to focus, which places focus either in the web contents or in
// the location bar as appropriate.
- if (visible)
+ if (visible) {
+ // Once the window has been shown we know the requested bounds
+ // (if provided) have been honored and we can switch on window management.
+ SetWindowAutoManaged();
+
browser_view_->RestoreFocus();
+ }
views::NativeWidgetAura::OnWindowTargetVisibilityChanged(visible);
}
@@ -219,3 +230,11 @@ NativeBrowserFrame* NativeBrowserFrame::CreateNativeBrowserFrame(
BrowserFrameAura::~BrowserFrameAura() {
}
+
+void BrowserFrameAura::SetWindowAutoManaged() {
+#if defined(USE_ASH)
+ if (browser_view_->browser()->type() != Browser::TYPE_POPUP ||
+ browser_view_->browser()->is_app())
+ ash::wm::SetWindowPositionManaged(GetNativeWindow(), true);
+#endif
+}
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.h b/chrome/browser/ui/views/frame/browser_frame_aura.h
index d4e0dbd..98ee955 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.h
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.h
@@ -53,6 +53,9 @@ class BrowserFrameAura : public views::ContextMenuController,
virtual ~BrowserFrameAura();
+ // Set the window into the auto managed mode.
+ void SetWindowAutoManaged();
+
// The BrowserView is our ClientView. This is a pointer to it.
BrowserView* browser_view_;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 0b48e71..7fff1e9 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -47,6 +47,7 @@
#if defined(USE_ASH)
#include "ash/shell.h"
#include "ash/wm/property_util.h"
+#include "ash/wm/window_util.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window.h"
#include "ui/base/gestures/gesture_recognizer.h"
@@ -191,6 +192,12 @@ void SetTrackedByWorkspace(gfx::NativeWindow window, bool value) {
#endif
}
+void SetWindowPositionManaged(gfx::NativeWindow window, bool value) {
+#if defined(USE_ASH)
+ ash::wm::SetWindowPositionManaged(window, value);
+#endif
+}
+
bool ShouldDetachIntoNewBrowser() {
#if defined(USE_AURA)
return true;
@@ -211,6 +218,19 @@ bool DoesRectContainVerticalPointExpanded(
return y >= lower_threshold && y <= upper_threshold;
}
+// WidgetObserver implementation that resets the window position managed
+// property on Show.
+// We're forced to do this here since BrowserFrameAura resets the 'window
+// position managed' property during a show and we need the property set to
+// false before WorkspaceLayoutManager2 sees the visibility change.
+class WindowPositionManagedUpdater : public views::WidgetObserver {
+ public:
+ virtual void OnWidgetVisibilityChanged(views::Widget* widget,
+ bool visible) OVERRIDE {
+ SetWindowPositionManaged(widget->GetNativeView(), false);
+ }
+};
+
} // namespace
///////////////////////////////////////////////////////////////////////////////
@@ -373,6 +393,7 @@ TabDragController::~TabDragController() {
if (move_loop_widget_) {
move_loop_widget_->RemoveObserver(this);
SetTrackedByWorkspace(move_loop_widget_->GetNativeView(), true);
+ SetWindowPositionManaged(move_loop_widget_->GetNativeView(), true);
}
if (source_tabstrip_ && detach_into_browser_)
@@ -829,6 +850,10 @@ TabDragController::DragBrowserToNewTabStrip(
#else
target_tabstrip->GetWidget()->SetCapture(attached_tabstrip_);
#endif
+ // The window is going away. Since the drag is still on going we don't want
+ // that to effect the position of any windows.
+ SetWindowPositionManaged(browser_widget->GetNativeView(), false);
+
// EndMoveLoop is going to snap the window back to its original location.
// Hide it so users don't see this.
browser_widget->Hide();
@@ -1299,7 +1324,11 @@ void TabDragController::DetachIntoNewBrowserAndRunMoveLoop(
// TODO: come up with a cleaner way to do this.
attached_tabstrip_->SetTabBoundsForDrag(drag_bounds);
+ WindowPositionManagedUpdater updater;
+ dragged_browser_view->GetWidget()->AddObserver(&updater);
browser->window()->Show();
+ dragged_browser_view->GetWidget()->RemoveObserver(&updater);
+
browser->window()->Activate();
dragged_browser_view->GetWidget()->SetVisibilityChangedAnimationsEnabled(
true);
@@ -1556,8 +1585,11 @@ void TabDragController::EndDragImpl(EndDragType type) {
// happens we ignore it.
waiting_for_run_loop_to_exit_ = true;
- if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1))
+ if (type == NORMAL || (type == TAB_DESTROYED && drag_data_.size() > 1)) {
SetTrackedByWorkspace(GetAttachedBrowserWidget()->GetNativeView(), true);
+ SetWindowPositionManaged(GetAttachedBrowserWidget()->GetNativeView(),
+ true);
+ }
// End the nested drag loop.
GetAttachedBrowserWidget()->EndMoveLoop();
@@ -1970,6 +2002,7 @@ Browser* TabDragController::CreateBrowserForDrag(
create_params.initial_bounds = new_bounds;
Browser* browser = new Browser(create_params);
SetTrackedByWorkspace(browser->window()->GetNativeWindow(), false);
+ SetWindowPositionManaged(browser->window()->GetNativeWindow(), false);
// If the window is created maximized then the bounds we supplied are ignored.
// We need to reset them again so they are honored.
browser->window()->SetBounds(new_bounds);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index b59d82c..8ee2ea3 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -38,6 +38,7 @@
#include "ash/shell.h"
#include "ash/test/cursor_manager_test_api.h"
#include "ash/wm/cursor_manager.h"
+#include "ash/wm/window_util.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/root_window.h"
#endif
@@ -399,6 +400,22 @@ void DetachToOwnWindowStep2(DetachToBrowserTabDragControllerTest* test) {
ASSERT_TRUE(test->ReleaseInput());
}
+#if defined(USE_ASH)
+bool IsWindowPositionManaged(aura::Window* window) {
+ return ash::wm::IsWindowPositionManaged(window);
+}
+bool HasUserChangedWindowPositionOrSize(aura::Window* window) {
+ return ash::wm::HasUserChangedWindowPositionOrSize(window);
+}
+#else
+bool IsWindowPositionManaged(gfx::NativeWindow window) {
+ return true;
+}
+bool HasUserChangedWindowPositionOrSize(gfx::NativeWindow window) {
+ return false;
+}
+#endif
+
} // namespace
// Drags from browser to separate window and releases mouse.
@@ -441,6 +458,10 @@ IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
EXPECT_TRUE(GetTrackedByWorkspace(browser()));
EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
+ // After this both windows should still be managable.
+ EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
+ EXPECT_TRUE(IsWindowPositionManaged(
+ new_browser->window()->GetNativeWindow()));
}
// Deletes a tab being dragged before the user moved enough to start a drag.
@@ -856,6 +877,7 @@ IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
// Create another browser.
Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+ const gfx::Rect initial_bounds(browser2->window()->GetBounds());
// Move to the first tab and drag it enough so that it detaches, but not
// enough that it attaches to browser2.
@@ -879,6 +901,13 @@ IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
EXPECT_TRUE(GetTrackedByWorkspace(browser2));
+ // Make sure that the window is still managed and not user moved.
+ EXPECT_TRUE(IsWindowPositionManaged(browser2->window()->GetNativeWindow()));
+ EXPECT_FALSE(HasUserChangedWindowPositionOrSize(
+ browser2->window()->GetNativeWindow()));
+ // Also make sure that the drag to window position has not changed.
+ EXPECT_EQ(initial_bounds.ToString(),
+ browser2->window()->GetBounds().ToString());
}
namespace {
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash.cc b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
index 30093e6..519680e 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
@@ -156,22 +156,14 @@ bool WindowSizer::GetBoundsOverrideAsh(const gfx::Rect& specified_bounds,
if (maximized)
return true;
- gfx::Rect other_bounds_in_screen = top_window->GetBoundsInScreen();
- bool move_right =
- other_bounds_in_screen.CenterPoint().x() < work_area.CenterPoint().x();
-
- // In case we have only one window, we move the other window fully to the
- // "other side" - making room for this new window.
- if (count == 1) {
- gfx::Display display = ash::Shell::GetScreen()->GetDisplayMatching(
- top_window->GetRootWindow()->GetBoundsInScreen());
- if (MoveRect(work_area, other_bounds_in_screen, !move_right))
- top_window->SetBoundsInScreen(other_bounds_in_screen, display);
- }
// Use the size of the other window, and mirror the location to the
// opposite side. Then make sure that it is inside our work area
// (if possible).
- *bounds_in_screen = other_bounds_in_screen;
+ *bounds_in_screen = top_window->GetBoundsInScreen();
+
+ bool move_right =
+ bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x();
+
MoveRect(work_area, *bounds_in_screen, move_right);
if (bounds_in_screen->bottom() > work_area.bottom())
bounds_in_screen->set_y(std::max(work_area.y(),
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
index 0fcbdb0..3fbafb2 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
@@ -495,8 +495,6 @@ TEST_F(WindowSizerTestWithBrowser, PlaceNewWindows) {
PERSISTED, browser.get(), gfx::Rect(), &window_bounds);
// The position should be right flush.
EXPECT_EQ("384,32 640x320", window_bounds.ToString());
- // In addition the other window should have moved left flush.
- EXPECT_EQ("0,32 640x320", window->bounds().ToString());
}
{ // With the window shown - but more on the right side then on the left
@@ -509,8 +507,6 @@ TEST_F(WindowSizerTestWithBrowser, PlaceNewWindows) {
PERSISTED, browser.get(), gfx::Rect(), &window_bounds);
// The position should be left & bottom flush.
EXPECT_EQ("0,448 640x320", window_bounds.ToString());
- // In addition the other window should have moved right flush.
- EXPECT_EQ("384,600 640x320", window->bounds().ToString());
}
{ // If the second windows right side is already over the right side of the