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