window_sizer_ash.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/window_cycle_controller.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 24namespace { 25 26// When a window gets opened in default mode and the screen is less then this 27// width, the window will get opened in maximized mode. This value can be 28// reduced to a "tame" number if the feature is disabled. 29const int kForceMaximizeWidthLimit = 1366; 30const 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. 34bool 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. 45bool 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. 65aura::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::WindowCycleController::BuildWindowList(NULL, 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. 105int 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. 121bool 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// Adjust the |target_in_screen| rectangle so it moves as much as possible into 139// the |work_area| . 140void AdjustTargetRectVerticallyAgainstWorkspace(const gfx::Rect& work_area, 141 gfx::Rect* target_in_screen) { 142 if (target_in_screen->bottom() > work_area.bottom()) 143 target_in_screen->set_y(std::max(work_area.y(), 144 work_area.bottom() - target_in_screen->height())); 145} 146 147} // namespace 148 149// static 150int WindowSizer::GetForceMaximizedWidthLimit() { 151 static int maximum_limit = 0; 152 if (!maximum_limit) { 153 maximum_limit = CommandLine::ForCurrentProcess()->HasSwitch( 154 ash::switches::kAshDisableAutoMaximizing) ? 155 kForceMaximizeWidthLimitDisabled : kForceMaximizeWidthLimit; 156 } 157 return maximum_limit; 158} 159 160bool WindowSizer::GetBoundsOverrideAsh(gfx::Rect* bounds_in_screen, 161 ui::WindowShowState* show_state) const { 162 DCHECK(show_state); 163 DCHECK(bounds_in_screen); 164 165 if (browser_ && 166 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH) { 167 return false; 168 } 169 bounds_in_screen->SetRect(0, 0, 0, 0); 170 171 // Experiment: Force the maximize mode for all windows. 172 if (ash::Shell::IsForcedMaximizeMode()) { 173 // Exceptions: Do not maximize popups and do not maximize windowed V1 apps 174 // which explicitly specify a |show_state| (they might be tuned for a 175 // particular resolution / type). 176 bool is_tabbed = browser_ && browser_->is_type_tabbed(); 177 bool is_popup = browser_ && browser_->is_type_popup(); 178 if (!is_popup && (is_tabbed || *show_state == ui::SHOW_STATE_DEFAULT)) 179 *show_state = ui::SHOW_STATE_MAXIMIZED; 180 } 181 182 ui::WindowShowState passed_show_state = *show_state; 183 bool has_saved_bounds = true; 184 if (!GetSavedWindowBounds(bounds_in_screen, show_state)) { 185 has_saved_bounds = false; 186 GetDefaultWindowBounds(bounds_in_screen); 187 } 188 189 if (browser_ && browser_->is_type_tabbed()) { 190 aura::RootWindow* active = ash::Shell::GetActiveRootWindow(); 191 // Always open new window in the active display. 192 gfx::Rect active_area = active->GetBoundsInScreen(); 193 gfx::Rect work_area = 194 monitor_info_provider_->GetMonitorWorkAreaMatching(active_area); 195 196 // This is a window / app. See if there is no window and try to place it. 197 int count = GetNumberOfValidTopLevelBrowserWindows(work_area); 198 aura::Window* top_window = GetTopWindow(work_area); 199 // Our window should not have any impact if we are already on top. 200 if (browser_->window() && 201 top_window == browser_->window()->GetNativeWindow()) 202 top_window = NULL; 203 204 // If there is no valid other window we take the coordinates as is. 205 if ((!count || !top_window)) { 206 if (has_saved_bounds) { 207 // Restore to previous state - if there is one. 208 AdjustTargetRectVerticallyAgainstWorkspace(work_area, 209 bounds_in_screen); 210 return true; 211 } 212 // When using "small screens" we want to always open in full screen mode. 213 if (passed_show_state == ui::SHOW_STATE_DEFAULT && 214 !browser_->is_session_restore() && 215 work_area.width() < GetForceMaximizedWidthLimit() && 216 (!browser_->window() || !browser_->window()->IsFullscreen()) && 217 (!browser_->fullscreen_controller() || 218 !browser_->fullscreen_controller()->IsFullscreenForBrowser())) 219 *show_state = ui::SHOW_STATE_MAXIMIZED; 220 return true; 221 } 222 bool maximized = ash::wm::IsWindowMaximized(top_window); 223 // We ignore the saved show state, but look instead for the top level 224 // window's show state. 225 if (passed_show_state == ui::SHOW_STATE_DEFAULT) { 226 *show_state = maximized ? ui::SHOW_STATE_MAXIMIZED : 227 ui::SHOW_STATE_DEFAULT; 228 } 229 230 if (maximized) 231 return true; 232 233 // Use the size of the other window, and mirror the location to the 234 // opposite side. Then make sure that it is inside our work area 235 // (if possible). 236 *bounds_in_screen = top_window->GetBoundsInScreen(); 237 238 bool move_right = 239 bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x(); 240 241 MoveRect(work_area, *bounds_in_screen, move_right); 242 AdjustTargetRectVerticallyAgainstWorkspace(work_area, bounds_in_screen); 243 return true; 244 } 245 246 return false; 247} 248 249void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const { 250 DCHECK(default_bounds); 251 DCHECK(monitor_info_provider_.get()); 252 253 gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea(); 254 255 // There should be a 'desktop' border around the window at the left and right 256 // side. 257 int default_width = work_area.width() - 2 * kDesktopBorderSize; 258 // There should also be a 'desktop' border around the window at the top. 259 // Since the workspace excludes the tray area we only need one border size. 260 int default_height = work_area.height() - kDesktopBorderSize; 261 // We align the size to the grid size to avoid any surprise when the 262 // monitor height isn't divide-able by our alignment factor. 263 default_width -= default_width % kDesktopBorderSize; 264 default_height -= default_height % kDesktopBorderSize; 265 int offset_x = kDesktopBorderSize; 266 if (default_width > kMaximumWindowWidth) { 267 // The window should get centered on the screen and not follow the grid. 268 offset_x = (work_area.width() - kMaximumWindowWidth) / 2; 269 default_width = kMaximumWindowWidth; 270 } 271 default_bounds->SetRect(work_area.x() + offset_x, 272 work_area.y() + kDesktopBorderSize, 273 default_width, 274 default_height); 275} 276