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. 4 5 #include "ash/wm/workspace/auto_window_management.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/shell.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" 18 19 namespace ash { 20 namespace internal { 21 22 namespace { 23 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; 27 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); 34 } 35 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)); 44 } 45 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 49 // found. 50 // If the return value is true a single window was found. 51 bool GetOtherVisibleAndManageableWindow(const aura::Window* window, 52 aura::Window** other_window) { 53 *other_window = NULL; 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. 63 if (*other_window) 64 return false; 65 *other_window = iterated_window; 66 } 67 } 68 return *other_window != NULL; 69 } 70 71 // Get the work area for a given |window|. 72 gfx::Rect GetWorkAreaForWindow(aura::Window* window) { 73 #if defined(OS_WIN) 74 // On Win 8, the host window can't be resized, so 75 // use window's bounds instead. 76 // TODO(oshima): Emulate host window resize on win8. 77 gfx::Rect work_area = gfx::Rect(window->parent()->bounds().size()); 78 work_area.Inset(Shell::GetScreen()->GetDisplayMatching( 79 work_area).GetWorkAreaInsets()); 80 return work_area; 81 #else 82 return Shell::GetScreen()->GetDisplayNearestWindow(window).work_area(); 83 #endif 84 } 85 86 // Move the given |bounds| on the available |parent_width| to the 87 // direction. If |move_right| is true, the rectangle gets moved to the right 88 // corner, otherwise to the left one. 89 bool MoveRectToOneSide(int parent_width, bool move_right, gfx::Rect* bounds) { 90 if (move_right) { 91 if (parent_width > bounds->right()) { 92 bounds->set_x(parent_width - bounds->width()); 93 return true; 94 } 95 } else { 96 if (0 < bounds->x()) { 97 bounds->set_x(0); 98 return true; 99 } 100 } 101 return false; 102 } 103 104 // Move a |window| to a new |bound|. Animate if desired by user. 105 // Note: The function will do nothing if the bounds did not change. 106 void SetBoundsAnimated(aura::Window* window, const gfx::Rect& bounds) { 107 if (bounds == window->GetTargetBounds()) 108 return; 109 110 if (views::corewm::WindowAnimationsDisabled(window)) { 111 window->SetBounds(bounds); 112 return; 113 } 114 115 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); 116 settings.SetTransitionDuration( 117 base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS)); 118 window->SetBounds(bounds); 119 } 120 121 // Move |window| into the center of the screen - or restore it to the previous 122 // position. 123 void AutoPlaceSingleWindow(aura::Window* window, bool animated) { 124 gfx::Rect work_area = GetWorkAreaForWindow(window); 125 gfx::Rect bounds = window->bounds(); 126 const gfx::Rect* user_defined_area = 127 ash::wm::GetPreAutoManageWindowBounds(window); 128 if (user_defined_area) { 129 bounds = *user_defined_area; 130 ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area, &bounds); 131 } else { 132 // Center the window (only in x). 133 bounds.set_x((work_area.width() - bounds.width()) / 2); 134 } 135 136 if (animated) 137 SetBoundsAnimated(window, bounds); 138 else 139 window->SetBounds(bounds); 140 } 141 142 } // namespace 143 144 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) { 145 if (!UseAutoWindowMagerForWindow(removed_window)) 146 return; 147 // Find a single open browser window. 148 aura::Window* other_shown_window = NULL; 149 if (!GetOtherVisibleAndManageableWindow(removed_window, 150 &other_shown_window) || 151 !WindowPositionCanBeManaged(other_shown_window)) 152 return; 153 AutoPlaceSingleWindow(other_shown_window, true); 154 } 155 156 void RearrangeVisibleWindowOnShow(aura::Window* added_window) { 157 if (!UseAutoWindowMagerForWindow(added_window) || 158 wm::HasUserChangedWindowPositionOrSize(added_window) || 159 !added_window->TargetVisibility()) 160 return; 161 // Find a single open managed window. 162 aura::Window* other_shown_window = NULL; 163 if (!GetOtherVisibleAndManageableWindow(added_window, 164 &other_shown_window)) { 165 // It could be that this window is the first window joining the workspace. 166 if (!WindowPositionCanBeManaged(added_window) || other_shown_window) 167 return; 168 // Since we might be going from 0 to 1 window, we have to arrange the new 169 // window to a good default. 170 AutoPlaceSingleWindow(added_window, false); 171 return; 172 } 173 174 // When going from one to two windows both windows loose their "positioned 175 // by user" flags. 176 ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false); 177 ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false); 178 179 if (WindowPositionCanBeManaged(other_shown_window)) { 180 gfx::Rect work_area = GetWorkAreaForWindow(added_window); 181 182 // Push away the other window after remembering its current position. 183 gfx::Rect other_bounds = other_shown_window->bounds(); 184 ash::wm::SetPreAutoManageWindowBounds(other_shown_window, other_bounds); 185 186 bool move_right = other_bounds.CenterPoint().x() > work_area.width() / 2; 187 if (MoveRectToOneSide(work_area.width(), move_right, &other_bounds)) 188 SetBoundsAnimated(other_shown_window, other_bounds); 189 190 // Remember the current location of the new window and push it also to the 191 // opposite location (if needed). 192 // Since it is just coming into view, we do not need to animate it. 193 gfx::Rect added_bounds = added_window->bounds(); 194 ash::wm::SetPreAutoManageWindowBounds(added_window, added_bounds); 195 if (MoveRectToOneSide(work_area.width(), !move_right, &added_bounds)) 196 added_window->SetBounds(added_bounds); 197 } 198 } 199 200 } // namespace internal 201 } // namespace ash 202