diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-30 20:31:40 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-30 20:31:40 +0000 |
commit | cda278dd146961f4ecf8bb7771d22d7eeeab467b (patch) | |
tree | dd12972bb15e9a47ff2aadcbcc5b458091fc90ae /ash | |
parent | eeac662cd531cfd3482e9784107d93b061909981 (diff) | |
download | chromium_src-cda278dd146961f4ecf8bb7771d22d7eeeab467b.zip chromium_src-cda278dd146961f4ecf8bb7771d22d7eeeab467b.tar.gz chromium_src-cda278dd146961f4ecf8bb7771d22d7eeeab467b.tar.bz2 |
Re-vamped the entire window auto positioning logic
As long as the user did not move / resize the window:
A single visible window (browser / app) on the screen will always be centered.
When a second visible (browser / app) window joins in, both will be pushed into opposite corners.
When more windows get piled up, they go every time into the opposite side of the currently active window.
When the user has resized / moved a window, it will be treated as a visible (browser / app) window, but it will not get moved - the others will however.
Addressing bug: Moving last remaining tabbed window back into screen center
BUG=153431, 150879, 153302
TEST=unit-tests & visual
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=164352
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=164652
Review URL: https://chromiumcodereview.appspot.com/11085053
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164993 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 2 | ||||
-rw-r--r-- | ash/ash_switches.cc | 4 | ||||
-rw-r--r-- | ash/ash_switches.h | 1 | ||||
-rw-r--r-- | ash/wm/property_util.cc | 4 | ||||
-rw-r--r-- | ash/wm/property_util.h | 4 | ||||
-rw-r--r-- | ash/wm/window_properties.cc | 2 | ||||
-rw-r--r-- | ash/wm/window_properties.h | 10 | ||||
-rw-r--r-- | ash/wm/window_util.cc | 29 | ||||
-rw-r--r-- | ash/wm/window_util.h | 25 | ||||
-rw-r--r-- | ash/wm/workspace/auto_window_management.cc | 178 | ||||
-rw-r--r-- | ash/wm/workspace/auto_window_management.h | 20 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_layout_manager2_unittest.cc | 7 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_manager2.cc | 17 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_manager2_unittest.cc | 285 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.cc | 1 |
15 files changed, 566 insertions, 23 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index 00df8b6..b0bb95a 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -370,6 +370,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 836d5f7..6094cf0 100644 --- a/ash/ash_switches.cc +++ b/ash/ash_switches.cc @@ -25,6 +25,10 @@ const char kAshCopyHostBackgroundAtBoot[] = "ash-copy-host-background-at-boot"; // 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 b3e1e24..5ed521f 100644 --- a/ash/ash_switches.h +++ b/ash/ash_switches.h @@ -20,6 +20,7 @@ ASH_EXPORT extern const char kAshBootAnimationFunction3[]; ASH_EXPORT extern const char kAshConstrainPointerToRoot[]; ASH_EXPORT extern const char kAshCopyHostBackgroundAtBoot[]; 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 24e0ef4..50e7bf0 100644 --- a/ash/wm/window_properties.cc +++ b/ash/wm/window_properties.cc @@ -38,9 +38,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 c7b0eff..0d2bff3 100644 --- a/ash/wm/window_properties.h +++ b/ash/wm/window_properties.h @@ -66,12 +66,22 @@ ASH_EXPORT extern const aura::WindowProperty<bool>* const extern const aura::WindowProperty<ui_controls::UIControlsAura*>* const kUIControlsKey; +// A property key to remember if a windows position or size was changed by a +// user. +ASH_EXPORT extern const aura::WindowProperty<bool>* const + kUserChangedWindowPositionOrSizeKey; + // Property to tell if the container uses the screen coordinates. extern const aura::WindowProperty<bool>* const kUsesScreenCoordinatesKey; extern const aura::WindowProperty<WindowPersistsAcrossAllWorkspacesType>* const kWindowPersistsAcrossAllWorkspacesKey; +// A property key to remember if a windows position can be managed by the +// workspace manager or not. +ASH_EXPORT extern const aura::WindowProperty<bool>* const + kWindowPositionManagedKey; + // True if the window is controlled by the workspace manager. extern const aura::WindowProperty<bool>* const kWindowTrackedByWorkspaceKey; diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index b3a9b5d..97e2761 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc @@ -6,6 +6,7 @@ #include <vector> +#include "ash/ash_constants.h" #include "ash/shell.h" #include "ash/wm/activation_controller.h" #include "ash/wm/window_properties.h" @@ -73,11 +74,11 @@ bool CanActivateWindow(aura::Window* window) { return client && client->CanActivateWindow(window); } -bool CanMaximizeWindow(aura::Window* window) { +bool CanMaximizeWindow(const aura::Window* window) { return window->GetProperty(aura::client::kCanMaximizeKey); } -bool IsWindowNormal(aura::Window* window) { +bool IsWindowNormal(const aura::Window* window) { return IsWindowStateNormal(window->GetProperty(aura::client::kShowStateKey)); } @@ -85,17 +86,17 @@ bool IsWindowStateNormal(ui::WindowShowState state) { return state == ui::SHOW_STATE_NORMAL || state == ui::SHOW_STATE_DEFAULT; } -bool IsWindowMaximized(aura::Window* window) { +bool IsWindowMaximized(const aura::Window* window) { return window->GetProperty(aura::client::kShowStateKey) == ui::SHOW_STATE_MAXIMIZED; } -bool IsWindowMinimized(aura::Window* window) { +bool IsWindowMinimized(const aura::Window* window) { return window->GetProperty(aura::client::kShowStateKey) == ui::SHOW_STATE_MINIMIZED; } -bool IsWindowFullscreen(aura::Window* window) { +bool IsWindowFullscreen(const aura::Window* window) { return window->GetProperty(aura::client::kShowStateKey) == ui::SHOW_STATE_FULLSCREEN; } @@ -153,5 +154,23 @@ void DeepDeleteLayers(ui::Layer* layer) { delete layer; } +bool IsWindowPositionManaged(const aura::Window* window) { + return window->GetProperty(ash::internal::kWindowPositionManagedKey); +} + +void SetWindowPositionManaged(aura::Window* window, bool managed) { + window->SetProperty(ash::internal::kWindowPositionManagedKey, managed); +} + +bool HasUserChangedWindowPositionOrSize(const aura::Window* window) { + return window->GetProperty( + ash::internal::kUserChangedWindowPositionOrSizeKey); +} + +void SetUserHasChangedWindowPositionOrSize(aura::Window* window, bool changed) { + window->SetProperty(ash::internal::kUserChangedWindowPositionOrSizeKey, + changed); +} + } // namespace wm } // namespace ash diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h index 081a40e..7a9e3f0 100644 --- a/ash/wm/window_util.h +++ b/ash/wm/window_util.h @@ -39,22 +39,22 @@ ASH_EXPORT aura::Window* GetActivatableWindow(aura::Window* window); ASH_EXPORT bool IsActiveWindowFullscreen(); // Returns true if |window| can be maximized. -ASH_EXPORT bool CanMaximizeWindow(aura::Window* window); +ASH_EXPORT bool CanMaximizeWindow(const aura::Window* window); // Returns true if |window| is normal or default. -ASH_EXPORT bool IsWindowNormal(aura::Window* window); +ASH_EXPORT bool IsWindowNormal(const aura::Window* window); // Returns true if |state| is normal or default. -ASH_EXPORT bool IsWindowStateNormal(ui::WindowShowState state); +ASH_EXPORT bool IsWindowStateNormal(const ui::WindowShowState state); // Returns true if |window| is in the maximized state. -ASH_EXPORT bool IsWindowMaximized(aura::Window* window); +ASH_EXPORT bool IsWindowMaximized(const aura::Window* window); // Returns true if |window| is minimized. -ASH_EXPORT bool IsWindowMinimized(aura::Window* window); +ASH_EXPORT bool IsWindowMinimized(const aura::Window* window); // Returns true if |window| is in the fullscreen state. -ASH_EXPORT bool IsWindowFullscreen(aura::Window* window); +ASH_EXPORT bool IsWindowFullscreen(const aura::Window* window); // Maximizes |window|, which must not be NULL. ASH_EXPORT void MaximizeWindow(aura::Window* window); @@ -86,6 +86,19 @@ ASH_EXPORT ui::Layer* RecreateWindowLayers(aura::Window* window, // Deletes |layer| and all its child layers. ASH_EXPORT void DeepDeleteLayers(ui::Layer* layer); +// Returns true if |window|'s position can automatically be managed. +ASH_EXPORT bool IsWindowPositionManaged(const aura::Window* window); + +// Change the |window|'s position manageability to |managed|. +ASH_EXPORT void SetWindowPositionManaged(aura::Window* window, bool managed); + +// Returns true if the user has changed the |window|'s position or size. +ASH_EXPORT bool HasUserChangedWindowPositionOrSize(const aura::Window* window); + +// Marks a |window|'s coordinates to be changed by a user. +ASH_EXPORT void SetUserHasChangedWindowPositionOrSize(aura::Window* window, + bool changed); + } // namespace wm } // namespace ash diff --git a/ash/wm/workspace/auto_window_management.cc b/ash/wm/workspace/auto_window_management.cc new file mode 100644 index 0000000..ea2f89f --- /dev/null +++ b/ash/wm/workspace/auto_window_management.cc @@ -0,0 +1,178 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/workspace/auto_window_management.h" + +#include "ash/ash_switches.h" +#include "ash/shell.h" +#include "ash/wm/property_util.h" +#include "ash/wm/window_animations.h" +#include "ash/wm/window_util.h" +#include "base/command_line.h" +#include "ui/aura/window.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/compositor/layer_animator.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/screen.h" + +namespace ash { +namespace internal { + +namespace { + +// The time in milliseconds which should be used to visually move a window +// through an automatic "intelligent" window management option. +const int kWindowAutoMoveDurationMS = 125; + +// Check if any management should be performed (with a given |window|). +bool UseAutoWindowMagerForWindow(const aura::Window* window) { + return !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAshDisableAutoWindowPlacement) && + GetTrackedByWorkspace(window) && + wm::IsWindowPositionManaged(window); +} + +// Check if a given |window| can be managed. This includes that it's state is +// not minimized/maximized/the user has changed it's size by hand already. +// It furthermore checks for the WindowIsManaged status. +bool WindowPositionCanBeManaged(const aura::Window* window) { + return (wm::IsWindowPositionManaged(window) && + !wm::IsWindowMinimized(window) && + !wm::IsWindowMaximized(window) && + !wm::HasUserChangedWindowPositionOrSize(window)); +} + +// Given a |window|, return the only |other_window| which has an impact on +// the automated windows location management. If there is more then one window, +// false is returned, but the |other_window| will be set to the first one +// found. +// If the return value is true a single window was found. +bool GetOtherVisibleAndManageableWindow(const aura::Window* window, + aura::Window** other_window) { + *other_window = NULL; + const aura::Window::Windows& windows = window->parent()->children(); + // Find a single open managed window. + for (size_t i = 0; i < windows.size(); i++) { + aura::Window* iterated_window = windows[i]; + if (window != iterated_window && + iterated_window->type() == aura::client::WINDOW_TYPE_NORMAL && + iterated_window->TargetVisibility() && + wm::IsWindowPositionManaged(iterated_window)) { + // Bail if we find a second usable window. + if (*other_window) + return false; + *other_window = iterated_window; + } + } + return *other_window != NULL; +} + +// Get the work area for a given |window|. +gfx::Rect GetWorkAreaForWindow(const aura::Window* window) { + gfx::Rect work_area = gfx::Rect(window->parent()->bounds().size()); + work_area.Inset(Shell::GetScreen()->GetDisplayMatching( + work_area).GetWorkAreaInsets()); + return work_area; +} + +// Move the given |bounds| on the available |parent_width| to the +// direction. If |move_right| is true, the rectangle gets moved to the right +// corner, otherwise to the left one. +bool MoveRectToOneSide(int parent_width, bool move_right, gfx::Rect* bounds) { + if (move_right) { + if (parent_width > bounds->right()) { + bounds->set_x(parent_width - bounds->width()); + return true; + } + } else { + if (0 < bounds->x()) { + bounds->set_x(0); + return true; + } + } + return false; +} + +// Move a |window| to a new |bound|. Animate if desired by user. +// Note: The function will do nothing if the bounds did not change. +void SetBoundsAnimated(aura::Window* window, const gfx::Rect& bounds) { + if (bounds == window->GetTargetBounds()) + return; + + if (window->GetProperty(aura::client::kAnimationsDisabledKey) || + CommandLine::ForCurrentProcess()->HasSwitch( + ash::switches::kAshWindowAnimationsDisabled)) { + window->SetBounds(bounds); + return; + } + + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS)); + window->SetBounds(bounds); +} + +} // namespace + +void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) { + if (!UseAutoWindowMagerForWindow(removed_window)) + return; + // Find a single open browser window. + aura::Window* other_shown_window = NULL; + if (!GetOtherVisibleAndManageableWindow(removed_window, + &other_shown_window) || + !WindowPositionCanBeManaged(other_shown_window)) + return; + // Center the window (only in x). + gfx::Rect work_area = GetWorkAreaForWindow(removed_window); + gfx::Rect bounds = other_shown_window->bounds(); + bounds.set_x((work_area.width() - bounds.width()) / 2); + SetBoundsAnimated(other_shown_window, bounds); +} + +void RearrangeVisibleWindowOnShow(aura::Window* added_window) { + if (!UseAutoWindowMagerForWindow(added_window) || + wm::HasUserChangedWindowPositionOrSize(added_window) || + !added_window->TargetVisibility()) + return; + // Find a single open managed window. + aura::Window* other_shown_window = NULL; + if (!GetOtherVisibleAndManageableWindow(added_window, + &other_shown_window)) { + // It could be that this window is the first window joining the workspace. + if (!WindowPositionCanBeManaged(added_window) || other_shown_window) + return; + + // If so we have to make sure it is centered. + gfx::Rect work_area = GetWorkAreaForWindow(added_window); + gfx::Rect bounds = added_window->bounds(); + bounds.set_x((work_area.width() - bounds.width()) / 2); + added_window->SetBounds(bounds); + return; + } + + // When going from one to two windows both windows loose their "positioned + // by user" flags. + ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false); + ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false); + + if (WindowPositionCanBeManaged(other_shown_window)) { + gfx::Rect work_area = GetWorkAreaForWindow(added_window); + + // Push away the other window. + gfx::Rect other_bounds = other_shown_window->bounds(); + bool move_right = other_bounds.CenterPoint().x() < work_area.width() / 2; + if (MoveRectToOneSide(work_area.width(), move_right, &other_bounds)) + SetBoundsAnimated(other_shown_window, other_bounds); + + // Push the new window also to the opposite location (if needed). + // Since it is just coming into view, we do not need to animate it. + gfx::Rect added_bounds = added_window->bounds(); + if (MoveRectToOneSide(work_area.width(), !move_right, &added_bounds)) + added_window->SetBounds(added_bounds); + } +} + +} // namespace internal +} // namespace ash diff --git a/ash/wm/workspace/auto_window_management.h b/ash/wm/workspace/auto_window_management.h new file mode 100644 index 0000000..138cb54 --- /dev/null +++ b/ash/wm/workspace/auto_window_management.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/aura/window.h" + +namespace ash { +namespace internal { + +// Check if after removal or hide of the given |removed_window| an automated +// desktop location management can be performed and rearrange accordingly. +void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window); + +// Check if after insertion or showing of the given |added_window| an automated +// desktop location management can be performed and rearrange accordingly. +void RearrangeVisibleWindowOnShow(aura::Window* added_window); + +} // namespace internal +} // namespace ash + diff --git a/ash/wm/workspace/workspace_layout_manager2_unittest.cc b/ash/wm/workspace/workspace_layout_manager2_unittest.cc index b559a3c..13496f0 100644 --- a/ash/wm/workspace/workspace_layout_manager2_unittest.cc +++ b/ash/wm/workspace/workspace_layout_manager2_unittest.cc @@ -13,9 +13,7 @@ #include "ui/aura/root_window.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" -#include "ui/gfx/display.h" #include "ui/gfx/insets.h" -#include "ui/gfx/screen.h" namespace ash { @@ -147,15 +145,14 @@ TEST_F(WorkspaceLayoutManager2Test, SizeToWorkArea) { 100, 101, work_area.width() + 1, work_area.height() + 2); scoped_ptr<aura::Window> window(CreateTestWindow(window_bounds)); EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), - window->bounds().ToString()); + window->bounds().ToString()); // Directly setting the bounds triggers a slightly different code path. Verify // that too. window->SetBounds(window_bounds); EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(), - window->bounds().ToString()); + window->bounds().ToString()); } } // namespace - } // namespace ash diff --git a/ash/wm/workspace/workspace_manager2.cc b/ash/wm/workspace/workspace_manager2.cc index 609fc79..d357d75 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" @@ -594,10 +595,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); } @@ -610,10 +615,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 75372cd..67145f5 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(); |