1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/wm/workspace/auto_window_management.h"
7 #include "ash/ash_switches.h"
9 #include "ash/wm/property_util.h"
10 #include "ash/wm/window_animations.h"
11 #include "ash/wm/window_util.h"
12 #include "base/command_line.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/client/aura_constants.h"
15 #include "ui/compositor/layer_animator.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/gfx/screen.h"
24 // The time in milliseconds which should be used to visually move a window
25 // through an automatic "intelligent" window management option.
26 const int kWindowAutoMoveDurationMS
= 125;
28 // Check if any management should be performed (with a given |window|).
29 bool UseAutoWindowMagerForWindow(const aura::Window
* window
) {
30 return !CommandLine::ForCurrentProcess()->HasSwitch(
31 switches::kAshDisableAutoWindowPlacement
) &&
32 GetTrackedByWorkspace(window
) &&
33 wm::IsWindowPositionManaged(window
);
36 // Check if a given |window| can be managed. This includes that it's state is
37 // not minimized/maximized/the user has changed it's size by hand already.
38 // It furthermore checks for the WindowIsManaged status.
39 bool WindowPositionCanBeManaged(const aura::Window
* window
) {
40 return (wm::IsWindowPositionManaged(window
) &&
41 !wm::IsWindowMinimized(window
) &&
42 !wm::IsWindowMaximized(window
) &&
43 !wm::HasUserChangedWindowPositionOrSize(window
));
46 // Given a |window|, return the only |other_window| which has an impact on
47 // the automated windows location management. If there is more then one window,
48 // false is returned, but the |other_window| will be set to the first one
50 // If the return value is true a single window was found.
51 bool GetOtherVisibleAndManageableWindow(const aura::Window
* window
,
52 aura::Window
** other_window
) {
54 const aura::Window::Windows
& windows
= window
->parent()->children();
55 // Find a single open managed window.
56 for (size_t i
= 0; i
< windows
.size(); i
++) {
57 aura::Window
* iterated_window
= windows
[i
];
58 if (window
!= iterated_window
&&
59 iterated_window
->type() == aura::client::WINDOW_TYPE_NORMAL
&&
60 iterated_window
->TargetVisibility() &&
61 wm::IsWindowPositionManaged(iterated_window
)) {
62 // Bail if we find a second usable window.
65 *other_window
= iterated_window
;
68 return *other_window
!= NULL
;
71 // Get the work area for a given |window|.
72 gfx::Rect
GetWorkAreaForWindow(const aura::Window
* window
) {
73 gfx::Rect work_area
= gfx::Rect(window
->parent()->bounds().size());
74 work_area
.Inset(Shell::GetScreen()->GetDisplayMatching(
75 work_area
).GetWorkAreaInsets());
79 // Move the given |bounds| on the available |parent_width| to the
80 // direction. If |move_right| is true, the rectangle gets moved to the right
81 // corner, otherwise to the left one.
82 bool MoveRectToOneSide(int parent_width
, bool move_right
, gfx::Rect
* bounds
) {
84 if (parent_width
> bounds
->right()) {
85 bounds
->set_x(parent_width
- bounds
->width());
89 if (0 < bounds
->x()) {
97 // Move a |window| to a new |bound|. Animate if desired by user.
98 // Note: The function will do nothing if the bounds did not change.
99 void SetBoundsAnimated(aura::Window
* window
, const gfx::Rect
& bounds
) {
100 if (bounds
== window
->GetTargetBounds())
103 if (views::corewm::WindowAnimationsDisabled(window
)) {
104 window
->SetBounds(bounds
);
108 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
109 settings
.SetTransitionDuration(
110 base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS
));
111 window
->SetBounds(bounds
);
114 // Move |window| into the center of the screen - or restore it to the previous
116 void AutoPlaceSingleWindow(aura::Window
* window
, bool animated
) {
117 gfx::Rect work_area
= GetWorkAreaForWindow(window
);
118 gfx::Rect bounds
= window
->bounds();
119 const gfx::Rect
* user_defined_area
=
120 ash::wm::GetPreAutoManageWindowBounds(window
);
121 if (user_defined_area
) {
122 bounds
= *user_defined_area
;
123 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area
, &bounds
);
125 // Center the window (only in x).
126 bounds
.set_x((work_area
.width() - bounds
.width()) / 2);
130 SetBoundsAnimated(window
, bounds
);
132 window
->SetBounds(bounds
);
137 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window
* removed_window
) {
138 if (!UseAutoWindowMagerForWindow(removed_window
))
140 // Find a single open browser window.
141 aura::Window
* other_shown_window
= NULL
;
142 if (!GetOtherVisibleAndManageableWindow(removed_window
,
143 &other_shown_window
) ||
144 !WindowPositionCanBeManaged(other_shown_window
))
146 AutoPlaceSingleWindow(other_shown_window
, true);
149 void RearrangeVisibleWindowOnShow(aura::Window
* added_window
) {
150 if (!UseAutoWindowMagerForWindow(added_window
) ||
151 wm::HasUserChangedWindowPositionOrSize(added_window
) ||
152 !added_window
->TargetVisibility())
154 // Find a single open managed window.
155 aura::Window
* other_shown_window
= NULL
;
156 if (!GetOtherVisibleAndManageableWindow(added_window
,
157 &other_shown_window
)) {
158 // It could be that this window is the first window joining the workspace.
159 if (!WindowPositionCanBeManaged(added_window
) || other_shown_window
)
161 // Since we might be going from 0 to 1 window, we have to arrange the new
162 // window to a good default.
163 AutoPlaceSingleWindow(added_window
, false);
167 // When going from one to two windows both windows loose their "positioned
169 ash::wm::SetUserHasChangedWindowPositionOrSize(added_window
, false);
170 ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window
, false);
172 if (WindowPositionCanBeManaged(other_shown_window
)) {
173 gfx::Rect work_area
= GetWorkAreaForWindow(added_window
);
175 // Push away the other window after remembering its current position.
176 gfx::Rect other_bounds
= other_shown_window
->bounds();
177 ash::wm::SetPreAutoManageWindowBounds(other_shown_window
, other_bounds
);
179 bool move_right
= other_bounds
.CenterPoint().x() > work_area
.width() / 2;
180 if (MoveRectToOneSide(work_area
.width(), move_right
, &other_bounds
))
181 SetBoundsAnimated(other_shown_window
, other_bounds
);
183 // Remember the current location of the new window and push it also to the
184 // opposite location (if needed).
185 // Since it is just coming into view, we do not need to animate it.
186 gfx::Rect added_bounds
= added_window
->bounds();
187 ash::wm::SetPreAutoManageWindowBounds(added_window
, added_bounds
);
188 if (MoveRectToOneSide(work_area
.width(), !move_right
, &added_bounds
))
189 added_window
->SetBounds(added_bounds
);
193 } // namespace internal