window_sizer_ash.cc revision 58537e28ecd584eab876aee8be7156509866d23a
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/window_sizer/window_sizer.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/ash_switches.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/shell.h"
9a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ash/wm/mru_window_tracker.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/wm/window_util.h"
11424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "ash/wm/workspace/auto_window_management.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/root_window.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window_delegate.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// When a window gets opened in default mode and the screen is less than or
25ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// equal to this width, the window will get opened in maximized mode. This value
26ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// can be reduced to a "tame" number if the feature is disabled.
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kForceMaximizeWidthLimit = 1366;
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kForceMaximizeWidthLimitDisabled = 640;
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Check if the given browser is 'valid': It is a tabbed, non minimized
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// window, which intersects with the |bounds_in_screen| area of a given screen.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsValidBrowser(Browser* browser, const gfx::Rect& bounds_in_screen) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (browser && browser->window() &&
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          !browser->is_type_popup() &&
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !browser->window()->IsMinimized() &&
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          browser->window()->GetNativeWindow() &&
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          bounds_in_screen.Intersects(
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              browser->window()->GetNativeWindow()->GetBoundsInScreen()));
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Return the number of valid top level windows on the screen defined by
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the |bounds_in_screen| rectangle.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int GetNumberOfValidTopLevelBrowserWindows(const gfx::Rect& bounds_in_screen) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = 0;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator iter = ash_browser_list->begin();
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != ash_browser_list->end();
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsValidBrowser(*iter, bounds_in_screen))
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      count++;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return count;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Move the given |bounds_in_screen| on the available |work_area| to the
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// direction. If |move_right| is true, the rectangle gets moved to the right
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// corner. Otherwise to the left side.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MoveRect(const gfx::Rect& work_area,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              gfx::Rect& bounds_in_screen,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              bool move_right) {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (move_right) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (work_area.right() > bounds_in_screen.right()) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bounds_in_screen.set_x(work_area.right() - bounds_in_screen.width());
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (work_area.x() < bounds_in_screen.x()) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bounds_in_screen.set_x(work_area.x());
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int WindowSizer::GetForceMaximizedWidthLimit() {
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  static int maximum_limit = 0;
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!maximum_limit) {
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    maximum_limit = CommandLine::ForCurrentProcess()->HasSwitch(
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        ash::switches::kAshDisableAutoMaximizing) ?
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        kForceMaximizeWidthLimitDisabled : kForceMaximizeWidthLimit;
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return maximum_limit;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void WindowSizer::GetTabbedBrowserBoundsAsh(
9058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    gfx::Rect* bounds_in_screen,
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    ui::WindowShowState* show_state) const {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(show_state);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(bounds_in_screen);
9458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(!browser_ || browser_->is_type_tabbed());
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bounds_in_screen->SetRect(0, 0, 0, 0);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Experiment: Force the maximize mode for all tabbed windows
9958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (ash::Shell::IsForcedMaximizeMode())
10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    *show_state = ui::SHOW_STATE_MAXIMIZED;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::WindowShowState passed_show_state = *show_state;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool has_saved_bounds = true;
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!GetSavedWindowBounds(bounds_in_screen, show_state)) {
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    has_saved_bounds = false;
10658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    GetDefaultWindowBoundsAsh(bounds_in_screen);
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  aura::RootWindow* active = ash::Shell::GetActiveRootWindow();
11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Always open new window in the active display.
11158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  gfx::Rect work_area =
11258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      screen_->GetDisplayMatching(active->GetBoundsInScreen()).work_area();
11358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // This is a window / app. See if there is no window and try to place it.
11558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  int count = GetNumberOfValidTopLevelBrowserWindows(work_area);
11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  aura::Window* top_window = ash::GetTopWindowForNewWindow(active);
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Our window should not have any impact if we are already on top.
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (browser_->window() &&
11958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      top_window == browser_->window()->GetNativeWindow())
12058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    top_window = NULL;
12158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // If there is no valid other window we take the coordinates as is.
12358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if ((!count || !top_window)) {
12458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (has_saved_bounds) {
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      // Restore to previous state - if there is one.
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      bounds_in_screen->AdjustToFit(work_area);
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // When using "small screens" we want to always open in full screen mode.
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (passed_show_state == ui::SHOW_STATE_DEFAULT &&
13258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        !browser_->is_session_restore() &&
13358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        work_area.width() <= GetForceMaximizedWidthLimit() &&
13458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        (!browser_->window() || !browser_->window()->IsFullscreen()))
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      *show_state = ui::SHOW_STATE_MAXIMIZED;
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  bool maximized = ash::wm::IsWindowMaximized(top_window);
13958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // We ignore the saved show state, but look instead for the top level
14058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // window's show state.
14158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (passed_show_state == ui::SHOW_STATE_DEFAULT) {
14258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    *show_state = maximized ? ui::SHOW_STATE_MAXIMIZED :
14358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        ui::SHOW_STATE_DEFAULT;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Use the size of the other window. The window's bound will be rearranged
14758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // in ash::WorkspaceLayoutManager using this location.
14858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  *bounds_in_screen = top_window->GetBoundsInScreen();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(default_bounds);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  gfx::Rect work_area = screen_->GetPrimaryDisplay().work_area();
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There should be a 'desktop' border around the window at the left and right
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // side.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int default_width = work_area.width() - 2 * kDesktopBorderSize;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There should also be a 'desktop' border around the window at the top.
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since the workspace excludes the tray area we only need one border size.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int default_height = work_area.height() - kDesktopBorderSize;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We align the size to the grid size to avoid any surprise when the
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // monitor height isn't divide-able by our alignment factor.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  default_width -= default_width % kDesktopBorderSize;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  default_height -= default_height % kDesktopBorderSize;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int offset_x = kDesktopBorderSize;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (default_width > kMaximumWindowWidth) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The window should get centered on the screen and not follow the grid.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset_x = (work_area.width() - kMaximumWindowWidth) / 2;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default_width = kMaximumWindowWidth;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  default_bounds->SetRect(work_area.x() + offset_x,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          work_area.y() + kDesktopBorderSize,
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          default_width,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          default_height);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
177