Revert 222512 "Group WM related properties to ash::wm::WindowSet..."
[chromium-blink-merge.git] / ash / wm / workspace / auto_window_management.cc
blobddcd19c62949823b2658b50f4a9f215b5d51f087
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"
8 #include "ash/shell.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"
20 namespace ash {
21 namespace internal {
23 namespace {
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) {
47 #if defined(OS_WIN)
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());
54 return work_area;
55 #else
56 return Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
57 #endif
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) {
64 if (move_right) {
65 if (parent_width > bounds->right()) {
66 bounds->set_x(parent_width - bounds->width());
67 return true;
69 } else {
70 if (0 < bounds->x()) {
71 bounds->set_x(0);
72 return true;
75 return false;
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())
82 return;
84 if (views::corewm::WindowAnimationsDisabled(window)) {
85 window->SetBounds(bounds);
86 return;
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
96 // position.
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);
105 } else {
106 // Center the window (only in x).
107 bounds.set_x((work_area.width() - bounds.width()) / 2);
110 if (animated)
111 SetBoundsAnimated(window, bounds);
112 else
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) {
120 if (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)
125 active = NULL;
127 // Get a list of all windows.
128 const std::vector<aura::Window*> windows =
129 ash::MruWindowTracker::BuildWindowList(false);
131 if (windows.empty())
132 return NULL;
134 aura::Window::Windows::const_iterator iter = windows.begin();
135 // Find the index of the current active window.
136 if (active)
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;
157 return found;
159 found = window;
160 // If there is no need to check single window, return now.
161 if (!single_window)
162 return found;
165 return found;
168 } // namespace
170 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) {
171 if (!UseAutoWindowManager(removed_window))
172 return;
173 // Find a single open browser window.
174 bool single_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))
179 return;
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())
187 return;
188 // Find a single open managed window.
189 bool single_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)
196 return;
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);
200 return;
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.
209 if (single_window) {
210 // When going from one to two windows both windows loose their "positioned
211 // by user" flags.
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);
243 } // namespace ash