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 "chrome/browser/ui/window_sizer/window_sizer.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/shell.h" 9 #include "ash/wm/mru_window_tracker.h" 10 #include "ash/wm/window_util.h" 11 #include "base/command_line.h" 12 #include "base/compiler_specific.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser_list.h" 16 #include "chrome/browser/ui/browser_window.h" 17 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 18 #include "chrome/browser/ui/host_desktop.h" 19 #include "ui/aura/root_window.h" 20 #include "ui/aura/window.h" 21 #include "ui/aura/window_delegate.h" 22 #include "ui/gfx/screen.h" 23 24 namespace { 25 26 // When a window gets opened in default mode and the screen is less than or 27 // equal to this width, the window will get opened in maximized mode. This value 28 // can be reduced to a "tame" number if the feature is disabled. 29 const int kForceMaximizeWidthLimit = 1366; 30 const int kForceMaximizeWidthLimitDisabled = 640; 31 32 // Check if the given browser is 'valid': It is a tabbed, non minimized 33 // window, which intersects with the |bounds_in_screen| area of a given screen. 34 bool IsValidBrowser(Browser* browser, const gfx::Rect& bounds_in_screen) { 35 return (browser && browser->window() && 36 !browser->is_type_popup() && 37 !browser->window()->IsMinimized() && 38 browser->window()->GetNativeWindow() && 39 bounds_in_screen.Intersects( 40 browser->window()->GetNativeWindow()->GetBoundsInScreen())); 41 } 42 43 // Check if the window was not created as popup or as panel, it is 44 // on the screen defined by |bounds_in_screen| and visible. 45 bool IsValidToplevelWindow(aura::Window* window, 46 const gfx::Rect& bounds_in_screen) { 47 const BrowserList* ash_browser_list = 48 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 49 for (BrowserList::const_iterator iter = ash_browser_list->begin(); 50 iter != ash_browser_list->end(); 51 ++iter) { 52 Browser* browser = *iter; 53 if (browser && browser->window() && 54 browser->window()->GetNativeWindow() == window) { 55 return IsValidBrowser(browser, bounds_in_screen); 56 } 57 } 58 // A window which has no browser associated with it is probably not a window 59 // of which we want to copy the size from. 60 return false; 61 } 62 63 // Get the first open (non minimized) window which is on the screen defined 64 // by |bounds_in_screen| and visible. 65 aura::Window* GetTopWindow(const gfx::Rect& bounds_in_screen) { 66 // Get the active window. 67 aura::Window* window = ash::wm::GetActiveWindow(); 68 if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL && 69 window->IsVisible() && IsValidToplevelWindow(window, bounds_in_screen)) { 70 return window; 71 } 72 73 // Get a list of all windows. 74 const std::vector<aura::Window*> windows = 75 ash::MruWindowTracker::BuildWindowList(false); 76 77 if (windows.empty()) 78 return NULL; 79 80 aura::Window::Windows::const_iterator iter = windows.begin(); 81 // Find the index of the current window. 82 if (window) 83 iter = std::find(windows.begin(), windows.end(), window); 84 85 int index = (iter == windows.end()) ? 0 : (iter - windows.begin()); 86 87 // Scan the cycle list backwards to see which is the second topmost window 88 // (and so on). Note that we might cycle a few indices twice if there is no 89 // suitable window. However - since the list is fairly small this should be 90 // very fast anyways. 91 for (int i = index + windows.size(); i >= 0; i--) { 92 aura::Window* window = windows[i % windows.size()]; 93 if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL && 94 bounds_in_screen.Intersects(window->GetBoundsInScreen()) && 95 window->IsVisible() 96 && IsValidToplevelWindow(window, bounds_in_screen)) { 97 return window; 98 } 99 } 100 return NULL; 101 } 102 103 // Return the number of valid top level windows on the screen defined by 104 // the |bounds_in_screen| rectangle. 105 int GetNumberOfValidTopLevelBrowserWindows(const gfx::Rect& bounds_in_screen) { 106 int count = 0; 107 const BrowserList* ash_browser_list = 108 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 109 for (BrowserList::const_iterator iter = ash_browser_list->begin(); 110 iter != ash_browser_list->end(); 111 ++iter) { 112 if (IsValidBrowser(*iter, bounds_in_screen)) 113 count++; 114 } 115 return count; 116 } 117 118 // Move the given |bounds_in_screen| on the available |work_area| to the 119 // direction. If |move_right| is true, the rectangle gets moved to the right 120 // corner. Otherwise to the left side. 121 bool MoveRect(const gfx::Rect& work_area, 122 gfx::Rect& bounds_in_screen, 123 bool move_right) { 124 if (move_right) { 125 if (work_area.right() > bounds_in_screen.right()) { 126 bounds_in_screen.set_x(work_area.right() - bounds_in_screen.width()); 127 return true; 128 } 129 } else { 130 if (work_area.x() < bounds_in_screen.x()) { 131 bounds_in_screen.set_x(work_area.x()); 132 return true; 133 } 134 } 135 return false; 136 } 137 138 } // namespace 139 140 // static 141 int WindowSizer::GetForceMaximizedWidthLimit() { 142 static int maximum_limit = 0; 143 if (!maximum_limit) { 144 maximum_limit = CommandLine::ForCurrentProcess()->HasSwitch( 145 ash::switches::kAshDisableAutoMaximizing) ? 146 kForceMaximizeWidthLimitDisabled : kForceMaximizeWidthLimit; 147 } 148 return maximum_limit; 149 } 150 151 bool WindowSizer::GetBoundsOverrideAsh(gfx::Rect* bounds_in_screen, 152 ui::WindowShowState* show_state) const { 153 DCHECK(show_state); 154 DCHECK(bounds_in_screen); 155 156 if (browser_ && 157 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH) { 158 return false; 159 } 160 bounds_in_screen->SetRect(0, 0, 0, 0); 161 162 // Experiment: Force the maximize mode for all windows. 163 if (ash::Shell::IsForcedMaximizeMode()) { 164 // Exceptions: Do not maximize popups and do not maximize windowed V1 apps 165 // which explicitly specify a |show_state| (they might be tuned for a 166 // particular resolution / type). 167 bool is_tabbed = browser_ && browser_->is_type_tabbed(); 168 bool is_popup = browser_ && browser_->is_type_popup(); 169 if (!is_popup && (is_tabbed || *show_state == ui::SHOW_STATE_DEFAULT)) 170 *show_state = ui::SHOW_STATE_MAXIMIZED; 171 } 172 173 ui::WindowShowState passed_show_state = *show_state; 174 bool has_saved_bounds = true; 175 if (!GetSavedWindowBounds(bounds_in_screen, show_state)) { 176 has_saved_bounds = false; 177 GetDefaultWindowBounds(bounds_in_screen); 178 } 179 180 if (browser_ && browser_->is_type_tabbed()) { 181 aura::RootWindow* active = ash::Shell::GetActiveRootWindow(); 182 // Always open new window in the active display. 183 gfx::Rect active_area = active->GetBoundsInScreen(); 184 gfx::Rect work_area = 185 monitor_info_provider_->GetMonitorWorkAreaMatching(active_area); 186 187 // This is a window / app. See if there is no window and try to place it. 188 int count = GetNumberOfValidTopLevelBrowserWindows(work_area); 189 aura::Window* top_window = GetTopWindow(work_area); 190 // Our window should not have any impact if we are already on top. 191 if (browser_->window() && 192 top_window == browser_->window()->GetNativeWindow()) 193 top_window = NULL; 194 195 // If there is no valid other window we take the coordinates as is. 196 if ((!count || !top_window)) { 197 if (has_saved_bounds) { 198 // Restore to previous state - if there is one. 199 bounds_in_screen->AdjustToFit(work_area); 200 return true; 201 } 202 // When using "small screens" we want to always open in full screen mode. 203 if (passed_show_state == ui::SHOW_STATE_DEFAULT && 204 !browser_->is_session_restore() && 205 work_area.width() <= GetForceMaximizedWidthLimit() && 206 (!browser_->window() || !browser_->window()->IsFullscreen()) && 207 (!browser_->fullscreen_controller() || 208 !browser_->fullscreen_controller()->IsFullscreenForBrowser())) 209 *show_state = ui::SHOW_STATE_MAXIMIZED; 210 return true; 211 } 212 bool maximized = ash::wm::IsWindowMaximized(top_window); 213 // We ignore the saved show state, but look instead for the top level 214 // window's show state. 215 if (passed_show_state == ui::SHOW_STATE_DEFAULT) { 216 *show_state = maximized ? ui::SHOW_STATE_MAXIMIZED : 217 ui::SHOW_STATE_DEFAULT; 218 } 219 220 if (maximized) 221 return true; 222 223 // Use the size of the other window, and mirror the location to the 224 // opposite side. Then make sure that it is inside our work area 225 // (if possible). 226 *bounds_in_screen = top_window->GetBoundsInScreen(); 227 228 bool move_right = 229 bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x(); 230 231 MoveRect(work_area, *bounds_in_screen, move_right); 232 bounds_in_screen->AdjustToFit(work_area); 233 return true; 234 } 235 236 return false; 237 } 238 239 void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const { 240 DCHECK(default_bounds); 241 DCHECK(monitor_info_provider_.get()); 242 243 gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea(); 244 245 // There should be a 'desktop' border around the window at the left and right 246 // side. 247 int default_width = work_area.width() - 2 * kDesktopBorderSize; 248 // There should also be a 'desktop' border around the window at the top. 249 // Since the workspace excludes the tray area we only need one border size. 250 int default_height = work_area.height() - kDesktopBorderSize; 251 // We align the size to the grid size to avoid any surprise when the 252 // monitor height isn't divide-able by our alignment factor. 253 default_width -= default_width % kDesktopBorderSize; 254 default_height -= default_height % kDesktopBorderSize; 255 int offset_x = kDesktopBorderSize; 256 if (default_width > kMaximumWindowWidth) { 257 // The window should get centered on the screen and not follow the grid. 258 offset_x = (work_area.width() - kMaximumWindowWidth) / 2; 259 default_width = kMaximumWindowWidth; 260 } 261 default_bounds->SetRect(work_area.x() + offset_x, 262 work_area.y() + kDesktopBorderSize, 263 default_width, 264 default_height); 265 } 266