Home | History | Annotate | Download | only in ash
      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/ash/window_positioner.h"
      6 
      7 #include "ash/shell.h"
      8 #include "ash/wm/mru_window_tracker.h"
      9 #include "ash/wm/window_resizer.h"
     10 #include "ash/wm/window_util.h"
     11 #include "ui/aura/window.h"
     12 #include "ui/aura/window_delegate.h"
     13 #include "ui/compositor/layer.h"
     14 #include "ui/gfx/screen.h"
     15 
     16 namespace ash {
     17 
     18 // statics
     19 
     20 const int WindowPositioner::kMinimumWindowOffset = 32;
     21 
     22 WindowPositioner::WindowPositioner()
     23     : pop_position_offset_increment_x(0),
     24       pop_position_offset_increment_y(0),
     25       popup_position_offset_from_screen_corner_x(0),
     26       popup_position_offset_from_screen_corner_y(0),
     27       last_popup_position_x_(0),
     28       last_popup_position_y_(0) {
     29 }
     30 
     31 WindowPositioner::~WindowPositioner() {
     32 }
     33 
     34 gfx::Rect WindowPositioner::GetPopupPosition(const gfx::Rect& old_pos) {
     35   int grid = kMinimumWindowOffset;
     36   popup_position_offset_from_screen_corner_x = grid;
     37   popup_position_offset_from_screen_corner_y = grid;
     38   if (!pop_position_offset_increment_x) {
     39     // When the popup position increment is , the last popup position
     40     // was not yet initialized.
     41     last_popup_position_x_ = popup_position_offset_from_screen_corner_x;
     42     last_popup_position_y_ = popup_position_offset_from_screen_corner_y;
     43   }
     44   pop_position_offset_increment_x = grid;
     45   pop_position_offset_increment_y = grid;
     46   // We handle the Multi monitor support by retrieving the active window's
     47   // work area.
     48   aura::Window* window = ash::wm::GetActiveWindow();
     49   const gfx::Rect work_area = window && window->IsVisible() ?
     50       Shell::GetScreen()->GetDisplayNearestWindow(window).work_area() :
     51       Shell::GetScreen()->GetPrimaryDisplay().work_area();
     52   // Only try to reposition the popup when it is not spanning the entire
     53   // screen.
     54   if ((old_pos.width() + popup_position_offset_from_screen_corner_x >=
     55       work_area.width()) ||
     56       (old_pos.height() + popup_position_offset_from_screen_corner_y >=
     57        work_area.height()))
     58     return AlignPopupPosition(old_pos, work_area, grid);
     59   const gfx::Rect result = SmartPopupPosition(old_pos, work_area, grid);
     60   if (!result.IsEmpty())
     61     return AlignPopupPosition(result, work_area, grid);
     62   return NormalPopupPosition(old_pos, work_area);
     63 }
     64 
     65 gfx::Rect WindowPositioner::NormalPopupPosition(
     66     const gfx::Rect& old_pos,
     67     const gfx::Rect& work_area) {
     68   int w = old_pos.width();
     69   int h = old_pos.height();
     70   // Note: The 'last_popup_position' is checked and kept relative to the
     71   // screen size. The offsetting will be done in the last step when the
     72   // target rectangle gets returned.
     73   bool reset = false;
     74   if (last_popup_position_y_ + h > work_area.height() ||
     75       last_popup_position_x_ + w > work_area.width()) {
     76     // Popup does not fit on screen. Reset to next diagonal row.
     77     last_popup_position_x_ -= last_popup_position_y_ -
     78                               popup_position_offset_from_screen_corner_x -
     79                               pop_position_offset_increment_x;
     80     last_popup_position_y_ = popup_position_offset_from_screen_corner_y;
     81     reset = true;
     82   }
     83   if (last_popup_position_x_ + w > work_area.width()) {
     84     // Start again over.
     85     last_popup_position_x_ = popup_position_offset_from_screen_corner_x;
     86     last_popup_position_y_ = popup_position_offset_from_screen_corner_y;
     87     reset = true;
     88   }
     89   int x = last_popup_position_x_;
     90   int y = last_popup_position_y_;
     91   if (!reset) {
     92     last_popup_position_x_ += pop_position_offset_increment_x;
     93     last_popup_position_y_ += pop_position_offset_increment_y;
     94   }
     95   return gfx::Rect(x + work_area.x(), y + work_area.y(), w, h);
     96 }
     97 
     98 gfx::Rect WindowPositioner::SmartPopupPosition(
     99     const gfx::Rect& old_pos,
    100     const gfx::Rect& work_area,
    101     int grid) {
    102   const std::vector<aura::Window*> windows =
    103       ash::MruWindowTracker::BuildWindowList(false);
    104 
    105   std::vector<const gfx::Rect*> regions;
    106   // Process the window list and check if we can bail immediately.
    107   for (size_t i = 0; i < windows.size(); i++) {
    108     // We only include opaque and visible windows.
    109     if (windows[i] && windows[i]->IsVisible() && windows[i]->layer() &&
    110         (!windows[i]->transparent() ||
    111          windows[i]->layer()->GetTargetOpacity() == 1.0)) {
    112       // When any window is maximized we cannot find any free space.
    113       if (ash::wm::IsWindowMaximized(windows[i]) ||
    114           ash::wm::IsWindowFullscreen(windows[i]))
    115         return gfx::Rect(0, 0, 0, 0);
    116       if (ash::wm::IsWindowNormal(windows[i]))
    117         regions.push_back(&windows[i]->bounds());
    118     }
    119   }
    120 
    121   if (regions.empty())
    122     return gfx::Rect(0, 0, 0, 0);
    123 
    124   int w = old_pos.width();
    125   int h = old_pos.height();
    126   int x_end = work_area.width() / 2;
    127   int x, x_increment;
    128   // We parse for a proper location on the screen. We do this in two runs:
    129   // The first run will start from the left, parsing down, skipping any
    130   // overlapping windows it will encounter until the popup's height can not
    131   // be served anymore. Then the next grid position to the right will be
    132   // taken, and the same cycle starts again. This will be repeated until we
    133   // hit the middle of the screen (or we find a suitable location).
    134   // In the second run we parse beginning from the right corner downwards and
    135   // then to the left.
    136   // When no location was found, an empty rectangle will be returned.
    137   for (int run = 0; run < 2; run++) {
    138     if (run == 0) { // First run: Start left, parse right till mid screen.
    139       x = 0;
    140       x_increment = pop_position_offset_increment_x;
    141     } else { // Second run: Start right, parse left till mid screen.
    142       x = work_area.width() - w;
    143       x_increment = -pop_position_offset_increment_x;
    144     }
    145     // Note: The passing (x,y,w,h) window is always relative to the work area's
    146     // origin.
    147     for (; x_increment > 0 ? (x < x_end) : (x > x_end); x += x_increment) {
    148       int y = 0;
    149       while (y + h <= work_area.height()) {
    150         size_t i;
    151         for (i = 0; i < regions.size(); i++) {
    152           if (regions[i]->Intersects(gfx::Rect(x + work_area.x(),
    153                                                y + work_area.y(), w, h))) {
    154             y = regions[i]->bottom() - work_area.y();
    155             break;
    156           }
    157         }
    158         if (i >= regions.size())
    159           return gfx::Rect(x + work_area.x(), y + work_area.y(), w, h);
    160       }
    161     }
    162   }
    163   return gfx::Rect(0, 0, 0, 0);
    164 }
    165 
    166 gfx::Rect WindowPositioner::AlignPopupPosition(
    167     const gfx::Rect& pos,
    168     const gfx::Rect& work_area,
    169     int grid) {
    170   if (grid <= 1)
    171     return pos;
    172 
    173   int x = pos.x() - (pos.x() - work_area.x()) % grid;
    174   int y = pos.y() - (pos.y() - work_area.y()) % grid;
    175   int w = pos.width();
    176   int h = pos.height();
    177 
    178   // If the alignment was pushing the window out of the screen, we ignore the
    179   // alignment for that call.
    180   if (abs(pos.right() - work_area.right()) < grid)
    181     x = work_area.right() - w;
    182   if (abs(pos.bottom() - work_area.bottom()) < grid)
    183     y = work_area.bottom() - h;
    184   return gfx::Rect(x, y, w, h);
    185 }
    186 
    187 }  // namespace ash
    188