diff options
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 |