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