blob: a4ee1d11e4bd8a0d37fac00a5a6acc972d219297 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/window_sizer/window_sizer.h"
#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
#include "chrome/browser/ui/host_desktop.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/gfx/screen.h"
namespace {
// When a window gets opened in default mode and the screen is less than or
// equal to this width, the window will get opened in maximized mode. This value
// can be reduced to a "tame" number if the feature is disabled.
const int kForceMaximizeWidthLimit = 1366;
const int kForceMaximizeWidthLimitDisabled = 640;
// Check if the given browser is 'valid': It is a tabbed, non minimized
// window, which intersects with the |bounds_in_screen| area of a given screen.
bool IsValidBrowser(Browser* browser, const gfx::Rect& bounds_in_screen) {
return (browser && browser->window() &&
!browser->is_type_popup() &&
!browser->window()->IsMinimized() &&
browser->window()->GetNativeWindow() &&
bounds_in_screen.Intersects(
browser->window()->GetNativeWindow()->GetBoundsInScreen()));
}
// Check if the window was not created as popup or as panel, it is
// on the screen defined by |bounds_in_screen| and visible.
bool IsValidToplevelWindow(aura::Window* window,
const gfx::Rect& bounds_in_screen) {
const BrowserList* ash_browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
for (BrowserList::const_iterator iter = ash_browser_list->begin();
iter != ash_browser_list->end();
++iter) {
Browser* browser = *iter;
if (browser && browser->window() &&
browser->window()->GetNativeWindow() == window) {
return IsValidBrowser(browser, bounds_in_screen);
}
}
// A window which has no browser associated with it is probably not a window
// of which we want to copy the size from.
return false;
}
// Get the first open (non minimized) window which is on the screen defined
// by |bounds_in_screen| and visible.
aura::Window* GetTopWindow(const gfx::Rect& bounds_in_screen) {
// Get the active window.
aura::Window* window = ash::wm::GetActiveWindow();
if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
window->IsVisible() && IsValidToplevelWindow(window, bounds_in_screen)) {
return window;
}
// Get a list of all windows.
const std::vector<aura::Window*> windows =
ash::MruWindowTracker::BuildWindowList(false);
if (windows.empty())
return NULL;
aura::Window::Windows::const_iterator iter = windows.begin();
// Find the index of the current window.
if (window)
iter = std::find(windows.begin(), windows.end(), window);
int index = (iter == windows.end()) ? 0 : (iter - windows.begin());
// Scan the cycle list backwards to see which is the second topmost window
// (and so on). Note that we might cycle a few indices twice if there is no
// suitable window. However - since the list is fairly small this should be
// very fast anyways.
for (int i = index + windows.size(); i >= 0; i--) {
aura::Window* window = windows[i % windows.size()];
if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
bounds_in_screen.Intersects(window->GetBoundsInScreen()) &&
window->IsVisible()
&& IsValidToplevelWindow(window, bounds_in_screen)) {
return window;
}
}
return NULL;
}
// Return the number of valid top level windows on the screen defined by
// the |bounds_in_screen| rectangle.
int GetNumberOfValidTopLevelBrowserWindows(const gfx::Rect& bounds_in_screen) {
int count = 0;
const BrowserList* ash_browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
for (BrowserList::const_iterator iter = ash_browser_list->begin();
iter != ash_browser_list->end();
++iter) {
if (IsValidBrowser(*iter, bounds_in_screen))
count++;
}
return count;
}
// Move the given |bounds_in_screen| on the available |work_area| to the
// direction. If |move_right| is true, the rectangle gets moved to the right
// corner. Otherwise to the left side.
bool MoveRect(const gfx::Rect& work_area,
gfx::Rect& bounds_in_screen,
bool move_right) {
if (move_right) {
if (work_area.right() > bounds_in_screen.right()) {
bounds_in_screen.set_x(work_area.right() - bounds_in_screen.width());
return true;
}
} else {
if (work_area.x() < bounds_in_screen.x()) {
bounds_in_screen.set_x(work_area.x());
return true;
}
}
return false;
}
} // namespace
// static
int WindowSizer::GetForceMaximizedWidthLimit() {
static int maximum_limit = 0;
if (!maximum_limit) {
maximum_limit = CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kAshDisableAutoMaximizing) ?
kForceMaximizeWidthLimitDisabled : kForceMaximizeWidthLimit;
}
return maximum_limit;
}
bool WindowSizer::GetBoundsOverrideAsh(gfx::Rect* bounds_in_screen,
ui::WindowShowState* show_state) const {
DCHECK(show_state);
DCHECK(bounds_in_screen);
if (browser_ &&
browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH) {
return false;
}
bounds_in_screen->SetRect(0, 0, 0, 0);
// Experiment: Force the maximize mode for all windows.
if (ash::Shell::IsForcedMaximizeMode()) {
// Exceptions: Do not maximize popups and do not maximize windowed V1 apps
// which explicitly specify a |show_state| (they might be tuned for a
// particular resolution / type).
bool is_tabbed = browser_ && browser_->is_type_tabbed();
bool is_popup = browser_ && browser_->is_type_popup();
if (!is_popup && (is_tabbed || *show_state == ui::SHOW_STATE_DEFAULT))
*show_state = ui::SHOW_STATE_MAXIMIZED;
}
ui::WindowShowState passed_show_state = *show_state;
bool has_saved_bounds = true;
if (!GetSavedWindowBounds(bounds_in_screen, show_state)) {
has_saved_bounds = false;
GetDefaultWindowBounds(bounds_in_screen);
}
if (browser_ && browser_->is_type_tabbed()) {
aura::RootWindow* active = ash::Shell::GetActiveRootWindow();
// Always open new window in the active display.
gfx::Rect active_area = active->GetBoundsInScreen();
gfx::Rect work_area =
monitor_info_provider_->GetMonitorWorkAreaMatching(active_area);
// This is a window / app. See if there is no window and try to place it.
int count = GetNumberOfValidTopLevelBrowserWindows(work_area);
aura::Window* top_window = GetTopWindow(work_area);
// Our window should not have any impact if we are already on top.
if (browser_->window() &&
top_window == browser_->window()->GetNativeWindow())
top_window = NULL;
// If there is no valid other window we take the coordinates as is.
if ((!count || !top_window)) {
if (has_saved_bounds) {
// Restore to previous state - if there is one.
bounds_in_screen->AdjustToFit(work_area);
return true;
}
// When using "small screens" we want to always open in full screen mode.
if (passed_show_state == ui::SHOW_STATE_DEFAULT &&
!browser_->is_session_restore() &&
work_area.width() <= GetForceMaximizedWidthLimit() &&
(!browser_->window() || !browser_->window()->IsFullscreen()) &&
(!browser_->fullscreen_controller() ||
!browser_->fullscreen_controller()->IsFullscreenForBrowser()))
*show_state = ui::SHOW_STATE_MAXIMIZED;
return true;
}
bool maximized = ash::wm::IsWindowMaximized(top_window);
// We ignore the saved show state, but look instead for the top level
// window's show state.
if (passed_show_state == ui::SHOW_STATE_DEFAULT) {
*show_state = maximized ? ui::SHOW_STATE_MAXIMIZED :
ui::SHOW_STATE_DEFAULT;
}
if (maximized)
return true;
// Use the size of the other window, and mirror the location to the
// opposite side. Then make sure that it is inside our work area
// (if possible).
*bounds_in_screen = top_window->GetBoundsInScreen();
bool move_right =
bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x();
MoveRect(work_area, *bounds_in_screen, move_right);
bounds_in_screen->AdjustToFit(work_area);
return true;
}
return false;
}
void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const {
DCHECK(default_bounds);
DCHECK(monitor_info_provider_.get());
gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea();
// There should be a 'desktop' border around the window at the left and right
// side.
int default_width = work_area.width() - 2 * kDesktopBorderSize;
// There should also be a 'desktop' border around the window at the top.
// Since the workspace excludes the tray area we only need one border size.
int default_height = work_area.height() - kDesktopBorderSize;
// We align the size to the grid size to avoid any surprise when the
// monitor height isn't divide-able by our alignment factor.
default_width -= default_width % kDesktopBorderSize;
default_height -= default_height % kDesktopBorderSize;
int offset_x = kDesktopBorderSize;
if (default_width > kMaximumWindowWidth) {
// The window should get centered on the screen and not follow the grid.
offset_x = (work_area.width() - kMaximumWindowWidth) / 2;
default_width = kMaximumWindowWidth;
}
default_bounds->SetRect(work_area.x() + offset_x,
work_area.y() + kDesktopBorderSize,
default_width,
default_height);
}