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