window_sizer_ash.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/shell.h"
8#include "ash/wm/window_cycle_controller.h"
9#include "ash/wm/window_util.h"
10#include "base/compiler_specific.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_list.h"
14#include "chrome/browser/ui/browser_window.h"
15#include "ui/aura/root_window.h"
16#include "ui/aura/window.h"
17#include "ui/aura/window_delegate.h"
18#include "ui/gfx/screen.h"
19
20namespace {
21
22// Check if the given browser is 'valid': It is a tabbed, non minimized
23// window, which intersects with the |bounds_in_screen| area of a given screen.
24bool IsValidBrowser(Browser* browser, const gfx::Rect& bounds_in_screen) {
25  return (browser && browser->window() &&
26          !(browser->is_type_popup() || browser->is_type_panel()) &&
27          !browser->window()->IsMinimized() &&
28          browser->window()->GetNativeWindow() &&
29          bounds_in_screen.Intersects(
30              browser->window()->GetNativeWindow()->GetBoundsInScreen()));
31}
32
33// Check if the window was not created as popup or as panel, it is
34// on the screen defined by |bounds_in_screen| and visible.
35bool IsValidToplevelWindow(aura::Window* window,
36                           const gfx::Rect& bounds_in_screen) {
37  for (BrowserList::const_iterator iter = BrowserList::begin();
38       iter != BrowserList::end();
39       ++iter) {
40    Browser* browser = *iter;
41    if (browser && browser->window() &&
42        browser->window()->GetNativeWindow() == window)
43      return IsValidBrowser(browser, bounds_in_screen);
44  }
45  // A window which has no browser associated with it is probably not a window
46  // of which we want to copy the size from.
47  return false;
48}
49
50// Get the first open (non minimized) window which is on the screen defined
51// by |bounds_in_screen| and visible.
52aura::Window* GetTopWindow(const gfx::Rect& bounds_in_screen) {
53  // Get the active window.
54  aura::Window* window = ash::wm::GetActiveWindow();
55  if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
56      window->IsVisible() && IsValidToplevelWindow(window, bounds_in_screen))
57    return window;
58
59  // Get a list of all windows.
60  const std::vector<aura::Window*> windows =
61      ash::WindowCycleController::BuildWindowList(NULL);
62
63  if (windows.empty())
64    return NULL;
65
66  aura::Window::Windows::const_iterator iter = windows.begin();
67  // Find the index of the current window.
68  if (window)
69    iter = std::find(windows.begin(), windows.end(), window);
70
71  int index = (iter == windows.end()) ? 0 : (iter - windows.begin());
72
73  // Scan the cycle list backwards to see which is the second topmost window
74  // (and so on). Note that we might cycle a few indices twice if there is no
75  // suitable window. However - since the list is fairly small this should be
76  // very fast anyways.
77  for (int i = index + windows.size(); i >= 0; i--) {
78    aura::Window* window = windows[i % windows.size()];
79    if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
80        bounds_in_screen.Intersects(window->GetBoundsInScreen()) &&
81        window->IsVisible() && IsValidToplevelWindow(window, bounds_in_screen))
82      return window;
83  }
84  return NULL;
85}
86
87// Return the number of valid top level windows on the screen defined by
88// the |bounds_in_screen| rectangle.
89int GetNumberOfValidTopLevelBrowserWindows(const gfx::Rect& bounds_in_screen) {
90  int count = 0;
91  for (BrowserList::const_iterator iter = BrowserList::begin();
92       iter != BrowserList::end();
93       ++iter) {
94    if (IsValidBrowser(*iter, bounds_in_screen))
95      count++;
96  }
97  return count;
98}
99
100// Move the given |bounds_in_screen| on the available |work_area| to the
101// direction. If |move_right| is true, the rectangle gets moved to the right
102// corner. Otherwise to the left side.
103bool MoveRect(const gfx::Rect& work_area,
104              gfx::Rect& bounds_in_screen,
105              bool move_right) {
106  if (move_right) {
107    if (work_area.right() > bounds_in_screen.right()) {
108      bounds_in_screen.set_x(work_area.right() - bounds_in_screen.width());
109      return true;
110    }
111  } else {
112    if (work_area.x() < bounds_in_screen.x()) {
113      bounds_in_screen.set_x(work_area.x());
114      return true;
115    }
116  }
117  return false;
118}
119
120}  // namespace
121
122bool WindowSizer::GetBoundsOverrideAsh(const gfx::Rect& specified_bounds,
123                                       gfx::Rect* bounds_in_screen,
124                                       ui::WindowShowState* show_state) const {
125  DCHECK(show_state);
126  DCHECK(bounds_in_screen);
127  *bounds_in_screen = specified_bounds;
128  DCHECK(bounds_in_screen->IsEmpty());
129
130  ui::WindowShowState passed_show_state = *show_state;
131  if (!GetSavedWindowBounds(bounds_in_screen, show_state))
132    GetDefaultWindowBounds(bounds_in_screen);
133
134  if (browser_ && browser_->is_type_tabbed()) {
135    gfx::Rect work_area =
136        monitor_info_provider_->GetMonitorWorkAreaMatching(*bounds_in_screen);
137    // This is a window / app. See if there is no window and try to place it.
138    int count = GetNumberOfValidTopLevelBrowserWindows(work_area);
139    aura::Window* top_window = GetTopWindow(work_area);
140    // The window should not be able to reflect on itself.
141    if (browser_->window() &&
142        top_window == browser_->window()->GetNativeWindow())
143      return true;
144    // If there is no valid other window we take the coordinates as is.
145    if (!count || !top_window)
146      return true;
147
148    bool maximized = ash::wm::IsWindowMaximized(top_window);
149    // We ignore the saved show state, but look instead for the top level
150    // window's show state.
151    if (passed_show_state == ui::SHOW_STATE_DEFAULT) {
152      *show_state = maximized ? ui::SHOW_STATE_MAXIMIZED :
153                                ui::SHOW_STATE_DEFAULT;
154    }
155
156    if (maximized)
157      return true;
158
159    // Use the size of the other window, and mirror the location to the
160    // opposite side. Then make sure that it is inside our work area
161    // (if possible).
162    *bounds_in_screen = top_window->GetBoundsInScreen();
163
164    bool move_right =
165        bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x();
166
167    MoveRect(work_area, *bounds_in_screen, move_right);
168    if (bounds_in_screen->bottom() > work_area.bottom())
169      bounds_in_screen->set_y(std::max(work_area.y(),
170          work_area.bottom() - bounds_in_screen->height()));
171    return true;
172  }
173
174  return false;
175}
176
177void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const {
178  DCHECK(default_bounds);
179  DCHECK(monitor_info_provider_.get());
180
181  gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea();
182
183  // There should be a 'desktop' border around the window at the left and right
184  // side.
185  int default_width = work_area.width() - 2 * kDesktopBorderSize;
186  // There should also be a 'desktop' border around the window at the top.
187  // Since the workspace excludes the tray area we only need one border size.
188  int default_height = work_area.height() - kDesktopBorderSize;
189  // We align the size to the grid size to avoid any surprise when the
190  // monitor height isn't divide-able by our alignment factor.
191  default_width -= default_width % kDesktopBorderSize;
192  default_height -= default_height % kDesktopBorderSize;
193  int offset_x = kDesktopBorderSize;
194  if (default_width > kMaximumWindowWidth) {
195    // The window should get centered on the screen and not follow the grid.
196    offset_x = (work_area.width() - kMaximumWindowWidth) / 2;
197    default_width = kMaximumWindowWidth;
198  }
199  default_bounds->SetRect(work_area.x() + offset_x,
200                          work_area.y() + kDesktopBorderSize,
201                          default_width,
202                          default_height);
203}
204