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