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/mru_window_tracker.h"
10 #include "ash/wm/property_util.h"
11 #include "ash/wm/window_animations.h"
12 #include "ash/wm/window_util.h"
13 #include "base/command_line.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/client/aura_constants.h"
16 #include "ui/compositor/layer_animator.h"
17 #include "ui/compositor/scoped_layer_animation_settings.h"
18 #include "ui/gfx/screen.h"
25 // The time in milliseconds which should be used to visually move a window
26 // through an automatic "intelligent" window management option.
27 const int kWindowAutoMoveDurationMS
= 125;
29 // Check if any management should be performed (with a given |window|).
30 bool UseAutoWindowManager(const aura::Window
* window
) {
31 return GetTrackedByWorkspace(window
) &&
32 wm::IsWindowPositionManaged(window
);
35 // Check if a given |window| can be managed. This includes that it's state is
36 // not minimized/maximized/the user has changed it's size by hand already.
37 // It furthermore checks for the WindowIsManaged status.
38 bool WindowPositionCanBeManaged(const aura::Window
* window
) {
39 return (wm::IsWindowPositionManaged(window
) &&
40 !wm::IsWindowMinimized(window
) &&
41 !wm::IsWindowMaximized(window
) &&
42 !wm::HasUserChangedWindowPositionOrSize(window
));
45 // Get the work area for a given |window|.
46 gfx::Rect
GetWorkAreaForWindow(aura::Window
* window
) {
48 // On Win 8, the host window can't be resized, so
49 // use window's bounds instead.
50 // TODO(oshima): Emulate host window resize on win8.
51 gfx::Rect work_area
= gfx::Rect(window
->parent()->bounds().size());
52 work_area
.Inset(Shell::GetScreen()->GetDisplayMatching(
53 work_area
).GetWorkAreaInsets());
56 return Shell::GetScreen()->GetDisplayNearestWindow(window
).work_area();
60 // Move the given |bounds| on the available |parent_width| to the
61 // direction. If |move_right| is true, the rectangle gets moved to the right
62 // corner, otherwise to the left one.
63 bool MoveRectToOneSide(int parent_width
, bool move_right
, gfx::Rect
* bounds
) {
65 if (parent_width
> bounds
->right()) {
66 bounds
->set_x(parent_width
- bounds
->width());
70 if (0 < bounds
->x()) {
78 // Move a |window| to a new |bound|. Animate if desired by user.
79 // Note: The function will do nothing if the bounds did not change.
80 void SetBoundsAnimated(aura::Window
* window
, const gfx::Rect
& bounds
) {
81 if (bounds
== window
->GetTargetBounds())
84 if (views::corewm::WindowAnimationsDisabled(window
)) {
85 window
->SetBounds(bounds
);
89 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
90 settings
.SetTransitionDuration(
91 base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS
));
92 window
->SetBounds(bounds
);
95 // Move |window| into the center of the screen - or restore it to the previous
97 void AutoPlaceSingleWindow(aura::Window
* window
, bool animated
) {
98 gfx::Rect work_area
= GetWorkAreaForWindow(window
);
99 gfx::Rect bounds
= window
->bounds();
100 const gfx::Rect
* user_defined_area
=
101 ash::wm::GetPreAutoManageWindowBounds(window
);
102 if (user_defined_area
) {
103 bounds
= *user_defined_area
;
104 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area
, &bounds
);
106 // Center the window (only in x).
107 bounds
.set_x((work_area
.width() - bounds
.width()) / 2);
111 SetBoundsAnimated(window
, bounds
);
113 window
->SetBounds(bounds
);
116 // Get the first open (non minimized) window which is on the screen defined.
117 aura::Window
* GetReferenceWindow(const aura::RootWindow
* root_window
,
118 const aura::Window
* exclude
,
119 bool *single_window
) {
121 *single_window
= true;
122 // Get the active window.
123 aura::Window
* active
= ash::wm::GetActiveWindow();
124 if (active
&& active
->GetRootWindow() != root_window
)
127 // Get a list of all windows.
128 const std::vector
<aura::Window
*> windows
=
129 ash::MruWindowTracker::BuildWindowList(false);
134 aura::Window::Windows::const_iterator iter
= windows
.begin();
135 // Find the index of the current active window.
137 iter
= std::find(windows
.begin(), windows
.end(), active
);
139 int index
= (iter
== windows
.end()) ? 0 : (iter
- windows
.begin());
141 // Scan the cycle list backwards to see which is the second topmost window
142 // (and so on). Note that we might cycle a few indices twice if there is no
143 // suitable window. However - since the list is fairly small this should be
144 // very fast anyways.
145 aura::Window
* found
= NULL
;
146 for (int i
= index
+ windows
.size(); i
>= 0; i
--) {
147 aura::Window
* window
= windows
[i
% windows
.size()];
148 if (window
!= exclude
&&
149 window
->type() == aura::client::WINDOW_TYPE_NORMAL
&&
150 window
->GetRootWindow() == root_window
&&
151 window
->TargetVisibility() &&
152 wm::IsWindowPositionManaged(window
)) {
153 if (found
&& found
!= window
) {
154 // no need to check !signle_window because the function must have
155 // been already returned in the "if (!single_window)" below.
156 *single_window
= false;
160 // If there is no need to check single window, return now.
170 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window
* removed_window
) {
171 if (!UseAutoWindowManager(removed_window
))
173 // Find a single open browser window.
175 aura::Window
* other_shown_window
= GetReferenceWindow(
176 removed_window
->GetRootWindow(), removed_window
, &single_window
);
177 if (!other_shown_window
|| !single_window
||
178 !WindowPositionCanBeManaged(other_shown_window
))
180 AutoPlaceSingleWindow(other_shown_window
, true);
183 void RearrangeVisibleWindowOnShow(aura::Window
* added_window
) {
184 if (!UseAutoWindowManager(added_window
) ||
185 wm::HasUserChangedWindowPositionOrSize(added_window
) ||
186 !added_window
->TargetVisibility())
188 // Find a single open managed window.
190 aura::Window
* other_shown_window
= GetReferenceWindow(
191 added_window
->GetRootWindow(), added_window
, &single_window
);
193 if (!other_shown_window
) {
194 // It could be that this window is the first window joining the workspace.
195 if (!WindowPositionCanBeManaged(added_window
) || other_shown_window
)
197 // Since we might be going from 0 to 1 window, we have to arrange the new
198 // window to a good default.
199 AutoPlaceSingleWindow(added_window
, false);
203 gfx::Rect other_bounds
= other_shown_window
->bounds();
204 gfx::Rect work_area
= GetWorkAreaForWindow(added_window
);
205 bool move_other_right
=
206 other_bounds
.CenterPoint().x() > work_area
.width() / 2;
208 // Push the other window to the size only if there are two windows left.
210 // When going from one to two windows both windows loose their "positioned
212 ash::wm::SetUserHasChangedWindowPositionOrSize(added_window
, false);
213 ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window
, false);
215 if (WindowPositionCanBeManaged(other_shown_window
)) {
216 // Don't override pre auto managed bounds as the current bounds
217 // may not be original.
218 if (!ash::wm::GetPreAutoManageWindowBounds(other_shown_window
))
219 ash::wm::SetPreAutoManageWindowBounds(other_shown_window
, other_bounds
);
221 // Push away the other window after remembering its current position.
222 if (MoveRectToOneSide(work_area
.width(), move_other_right
, &other_bounds
))
223 SetBoundsAnimated(other_shown_window
, other_bounds
);
227 // Remember the current location of the window if it's new and push
228 // it also to the opposite location if needed. Since it is just
229 // being shown, we do not need to animate it.
230 gfx::Rect added_bounds
= added_window
->bounds();
231 if (!ash::wm::GetPreAutoManageWindowBounds(added_window
))
232 ash::wm::SetPreAutoManageWindowBounds(added_window
, added_bounds
);
233 if (MoveRectToOneSide(work_area
.width(), !move_other_right
, &added_bounds
))
234 added_window
->SetBounds(added_bounds
);
237 } // namespace internal
239 aura::Window
* GetTopWindowForNewWindow(const aura::RootWindow
* root_window
) {
240 return internal::GetReferenceWindow(root_window
, NULL
, NULL
);