Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: ash/wm/workspace/workspace_window_resizer.cc

Issue 9609016: Initial cut at multi-window resize code. There's still some TODOs, but (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ash/wm/workspace/workspace_window_resizer.h" 5 #include "ash/wm/workspace/workspace_window_resizer.h"
6 6
7 #include "ash/shell.h" 7 #include "ash/shell.h"
8 #include "ash/wm/root_window_event_filter.h" 8 #include "ash/wm/root_window_event_filter.h"
9 #include "ash/wm/window_util.h" 9 #include "ash/wm/window_util.h"
10 #include "ui/aura/window.h" 10 #include "ui/aura/window.h"
11 #include "ui/aura/window_delegate.h" 11 #include "ui/aura/window_delegate.h"
12 #include "ui/aura/window_property.h" 12 #include "ui/aura/window_property.h"
13 #include "ui/base/hit_test.h" 13 #include "ui/base/hit_test.h"
14 #include "ui/gfx/compositor/scoped_layer_animation_settings.h" 14 #include "ui/gfx/compositor/scoped_layer_animation_settings.h"
15 #include "ui/gfx/compositor/layer.h" 15 #include "ui/gfx/compositor/layer.h"
16 #include "ui/gfx/screen.h" 16 #include "ui/gfx/screen.h"
17 #include "ui/gfx/transform.h" 17 #include "ui/gfx/transform.h"
18 18
19 DECLARE_WINDOW_PROPERTY_TYPE(int) 19 DECLARE_WINDOW_PROPERTY_TYPE(int)
20 20
21 namespace ash { 21 namespace ash {
22 namespace internal { 22 namespace internal {
23 23
24 namespace { 24 namespace {
25 25
26 const aura::WindowProperty<int> kHeightBeforeObscuredProp = {0}; 26 const aura::WindowProperty<int> kHeightBeforeObscuredProp = {0};
27 const aura::WindowProperty<int>* const kHeightBeforeObscuredKey = 27 const aura::WindowProperty<int>* const kHeightBeforeObscuredKey =
28 &kHeightBeforeObscuredProp; 28 &kHeightBeforeObscuredProp;
29 29
30 const aura::WindowProperty<int> kWidthBeforeObscuredProp = {0};
31 const aura::WindowProperty<int>* const kWidthBeforeObscuredKey =
32 &kWidthBeforeObscuredProp;
33
30 void SetHeightBeforeObscured(aura::Window* window, int height) { 34 void SetHeightBeforeObscured(aura::Window* window, int height) {
31 window->SetProperty(kHeightBeforeObscuredKey, height); 35 window->SetProperty(kHeightBeforeObscuredKey, height);
32 } 36 }
33 37
34 int GetHeightBeforeObscured(aura::Window* window) { 38 int GetHeightBeforeObscured(aura::Window* window) {
35 return window->GetProperty(kHeightBeforeObscuredKey); 39 return window->GetProperty(kHeightBeforeObscuredKey);
36 } 40 }
37 41
38 void ClearHeightBeforeObscured(aura::Window* window) { 42 void ClearHeightBeforeObscured(aura::Window* window) {
39 window->SetProperty(kHeightBeforeObscuredKey, 0); 43 window->SetProperty(kHeightBeforeObscuredKey, 0);
40 } 44 }
41 45
46 void SetWidthBeforeObscured(aura::Window* window, int width) {
47 window->SetProperty(kWidthBeforeObscuredKey, width);
48 }
49
50 int GetWidthBeforeObscured(aura::Window* window) {
51 return window->GetProperty(kWidthBeforeObscuredKey);
52 }
53
54 void ClearWidthBeforeObscured(aura::Window* window) {
55 window->SetProperty(kWidthBeforeObscuredKey, 0);
56 }
57
42 } // namespace 58 } // namespace
43 59
60 // static
61 const int WorkspaceWindowResizer::kMinOnscreenSize = 20;
62
44 WorkspaceWindowResizer::~WorkspaceWindowResizer() { 63 WorkspaceWindowResizer::~WorkspaceWindowResizer() {
45 if (root_filter_) 64 if (root_filter_)
46 root_filter_->UnlockCursor(); 65 root_filter_->UnlockCursor();
47 } 66 }
48 67
49 // static 68 // static
50 WorkspaceWindowResizer* WorkspaceWindowResizer::Create( 69 WorkspaceWindowResizer* WorkspaceWindowResizer::Create(
51 aura::Window* window, 70 aura::Window* window,
52 const gfx::Point& location, 71 const gfx::Point& location,
53 int window_component, 72 int window_component,
54 int grid_size) { 73 int grid_size,
74 const std::vector<aura::Window*>& attached_windows) {
55 Details details(window, location, window_component, grid_size); 75 Details details(window, location, window_component, grid_size);
56 return details.is_resizable ? 76 return details.is_resizable ?
57 new WorkspaceWindowResizer(details) : NULL; 77 new WorkspaceWindowResizer(details, attached_windows) : NULL;
58 } 78 }
59 79
60 void WorkspaceWindowResizer::Drag(const gfx::Point& location) { 80 void WorkspaceWindowResizer::Drag(const gfx::Point& location) {
61 gfx::Rect bounds = CalculateBoundsForDrag(details_, location); 81 gfx::Rect bounds = CalculateBoundsForDrag(details_, location);
62 if (constrain_size_) 82 if (constrain_size_)
63 AdjustBoundsForMainWindow(&bounds); 83 AdjustBoundsForMainWindow(&bounds);
64 if (bounds != details_.window->bounds()) { 84 if (bounds != details_.window->bounds()) {
65 did_move_or_resize_ = true; 85 did_move_or_resize_ = true;
66 details_.window->SetBounds(bounds); 86 details_.window->SetBounds(bounds);
67 } 87 }
88 if (!attached_windows_.empty()) {
89 if (details_.window_component == HTRIGHT)
90 LayoutAttachedWindowsHorizontally(bounds);
91 else
92 LayoutAttachedWindowsVertically(bounds);
93 }
68 } 94 }
69 95
70 void WorkspaceWindowResizer::CompleteDrag() { 96 void WorkspaceWindowResizer::CompleteDrag() {
97 // This code only matters when dragging the caption and there's a grid, so
98 // it doesn't need to worry about attached windows.
71 if (details_.grid_size <= 1 || !did_move_or_resize_ || 99 if (details_.grid_size <= 1 || !did_move_or_resize_ ||
72 details_.window_component != HTCAPTION) 100 details_.window_component != HTCAPTION)
73 return; 101 return;
74 102
75 gfx::Rect bounds(AdjustBoundsToGrid(details_)); 103 gfx::Rect bounds(AdjustBoundsToGrid(details_));
76 if (GetHeightBeforeObscured(window()) || constrain_size_) { 104 if (GetHeightBeforeObscured(window()) || constrain_size_) {
77 // Two things can happen: 105 // Two things can happen:
78 // . We'll snap to the grid, which may result in different bounds. When 106 // . We'll snap to the grid, which may result in different bounds. When
79 // dragging we only snap on release. 107 // dragging we only snap on release.
80 // . If the bounds are different, and the windows height was truncated 108 // . If the bounds are different, and the windows height was truncated
(...skipping 24 matching lines...) Expand all
105 // Use a small duration since the grid is small. 133 // Use a small duration since the grid is small.
106 scoped_setter.SetTransitionDuration(base::TimeDelta::FromMilliseconds(100)); 134 scoped_setter.SetTransitionDuration(base::TimeDelta::FromMilliseconds(100));
107 details_.window->SetBounds(bounds); 135 details_.window->SetBounds(bounds);
108 } 136 }
109 137
110 void WorkspaceWindowResizer::RevertDrag() { 138 void WorkspaceWindowResizer::RevertDrag() {
111 if (!did_move_or_resize_) 139 if (!did_move_or_resize_)
112 return; 140 return;
113 141
114 details_.window->SetBounds(details_.initial_bounds); 142 details_.window->SetBounds(details_.initial_bounds);
143 if (details_.window_component == HTRIGHT) {
144 int last_x = details_.initial_bounds.right();
145 for (size_t i = 0; i < attached_windows_.size(); ++i) {
146 gfx::Rect bounds(attached_windows_[i]->bounds());
147 bounds.set_x(last_x);
148 bounds.set_width(initial_size_[i]);
149 attached_windows_[i]->SetBounds(bounds);
150 last_x = attached_windows_[i]->bounds().right();
151 }
152 } else {
153 int last_y = details_.initial_bounds.bottom();
154 for (size_t i = 0; i < attached_windows_.size(); ++i) {
155 gfx::Rect bounds(attached_windows_[i]->bounds());
156 bounds.set_y(last_y);
157 bounds.set_height(initial_size_[i]);
158 attached_windows_[i]->SetBounds(bounds);
159 last_y = attached_windows_[i]->bounds().bottom();
160 }
161 }
115 } 162 }
116 163
117 WorkspaceWindowResizer::WorkspaceWindowResizer( 164 WorkspaceWindowResizer::WorkspaceWindowResizer(
118 const Details& details) 165 const Details& details,
166 const std::vector<aura::Window*>& attached_windows)
119 : details_(details), 167 : details_(details),
120 constrain_size_(wm::IsWindowNormal(details.window)), 168 constrain_size_(wm::IsWindowNormal(details.window)),
169 attached_windows_(attached_windows),
121 did_move_or_resize_(false), 170 did_move_or_resize_(false),
122 root_filter_(NULL) { 171 root_filter_(NULL),
172 total_min_(0),
173 total_initial_size_(0) {
123 DCHECK(details_.is_resizable); 174 DCHECK(details_.is_resizable);
124 root_filter_ = Shell::GetInstance()->root_filter(); 175 root_filter_ = Shell::GetInstance()->root_filter();
125 if (root_filter_) 176 if (root_filter_)
126 root_filter_->LockCursor(); 177 root_filter_->LockCursor();
127 178
179 // We should never be in a situation where we have an attached window and not
180 // constrain the size. The only case we don't constrain size is for dragging
181 // tabs, which should never have an attached window.
182 DCHECK(attached_windows_.empty() || constrain_size_);
183 // Only support attaching to the right/bottom.
184 DCHECK(attached_windows_.empty() ||
185 (details.window_component == HTRIGHT ||
186 details.window_component == HTBOTTOM));
187
188 // TODO: figure out how to deal with window going off the edge.
189
190 // Calculate sizes so that we can maintain the ratios if we need to resize.
191 int total_available = 0;
192 for (size_t i = 0; i < attached_windows_.size(); ++i) {
193 gfx::Size min(attached_windows_[i]->delegate()->GetMinimumSize());
194 int initial_size = PrimaryAxisSize(attached_windows_[i]->bounds().size());
195 int cached_size = PrimaryAxisCoordinate(
196 GetWidthBeforeObscured(attached_windows_[i]),
197 GetHeightBeforeObscured(attached_windows_[i]));
198 if (initial_size > cached_size) {
199 if (details.window_component == HTRIGHT)
200 ClearWidthBeforeObscured(attached_windows_[i]);
201 else
202 ClearHeightBeforeObscured(attached_windows_[i]);
203 } else if (cached_size) {
204 initial_size = cached_size;
205 }
206 initial_size_.push_back(initial_size);
207 // If current size is smaller than the min, use the current size as the min.
208 // This way we don't snap on resize.
209 int min_size = std::min(initial_size,
210 std::max(PrimaryAxisSize(min), kMinOnscreenSize));
211 // Make sure the min size falls on the grid.
212 if (details_.grid_size > 1 && min_size % details_.grid_size != 0)
213 min_size = (min_size / details_.grid_size + 1) * details_.grid_size;
214 min_size_.push_back(min_size);
215 total_min_ += min_size;
216 total_initial_size_ += initial_size;
217 total_available += std::max(min_size, initial_size) - min_size;
218 }
219
220 for (size_t i = 0; i < attached_windows_.size(); ++i) {
221 if (total_initial_size_ != total_min_) {
222 compress_fraction_.push_back(
223 static_cast<float>(initial_size_[i] - min_size_[i]) /
224 static_cast<float>(total_available));
225 } else {
226 compress_fraction_.push_back(0.0f);
227 }
228 }
229
128 if (is_resizable() && constrain_size_ && 230 if (is_resizable() && constrain_size_ &&
129 (!TouchesBottomOfScreen() || 231 (!TouchesBottomOfScreen() ||
130 details_.bounds_change != kBoundsChange_Repositions)) { 232 details_.bounds_change != kBoundsChange_Repositions)) {
131 ClearCachedHeights(); 233 ClearCachedHeights();
132 } 234 }
235
236 if (is_resizable() && constrain_size_ &&
237 (!TouchesRightSideOfScreen() ||
238 details_.bounds_change != kBoundsChange_Repositions)) {
239 ClearCachedWidths();
240 }
241 }
242
243 void WorkspaceWindowResizer::LayoutAttachedWindowsHorizontally(
244 const gfx::Rect& bounds) {
245 gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window()));
246 int last_x = bounds.right();
247 if (bounds.right() <= work_area.right() - total_initial_size_) {
248 ClearCachedWidths();
249 // All the windows fit at their initial size; tile them horizontally.
250 for (size_t i = 0; i < attached_windows_.size(); ++i) {
251 gfx::Rect attached_bounds(attached_windows_[i]->bounds());
252 attached_bounds.set_x(last_x);
253 attached_bounds.set_width(initial_size_[i]);
254 attached_windows_[i]->SetBounds(attached_bounds);
255 last_x = attached_bounds.right();
256 }
257 } else {
258 DCHECK_NE(total_initial_size_, total_min_);
259 int delta = total_initial_size_ - (work_area.right() - bounds.right());
260 for (size_t i = 0; i < attached_windows_.size(); ++i) {
261 gfx::Rect attached_bounds(attached_windows_[i]->bounds());
262 int size = initial_size_[i] -
263 static_cast<int>(compress_fraction_[i] * delta);
264 size = AlignToGrid(size, details_.grid_size);
265 if (!GetWidthBeforeObscured(attached_windows_[i]))
266 SetWidthBeforeObscured(attached_windows_[i], attached_bounds.width());
267 attached_bounds.set_x(last_x);
268 if (i == attached_windows_.size())
269 size = work_area.right() - last_x;
270 attached_bounds.set_width(size);
271 attached_windows_[i]->SetBounds(attached_bounds);
272 last_x = attached_bounds.right();
273 }
274 }
275 }
276
277 void WorkspaceWindowResizer::LayoutAttachedWindowsVertically(
278 const gfx::Rect& bounds) {
279 gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window()));
280 int last_y = bounds.bottom();
281 if (bounds.bottom() <= work_area.bottom() - total_initial_size_) {
282 ClearCachedHeights();
283 // All the windows fit at their initial size; tile them vertically.
284 for (size_t i = 0; i < attached_windows_.size(); ++i) {
285 gfx::Rect attached_bounds(attached_windows_[i]->bounds());
286 attached_bounds.set_y(last_y);
287 attached_bounds.set_height(initial_size_[i]);
288 attached_windows_[i]->SetBounds(attached_bounds);
289 last_y = attached_bounds.bottom();
290 }
291 } else {
292 DCHECK_NE(total_initial_size_, total_min_);
293 int delta = total_initial_size_ - (work_area.bottom() - bounds.bottom());
294 for (size_t i = 0; i < attached_windows_.size(); ++i) {
295 gfx::Rect attached_bounds(attached_windows_[i]->bounds());
296 int size = initial_size_[i] -
297 static_cast<int>(compress_fraction_[i] * delta);
298 size = AlignToGrid(size, details_.grid_size);
299 if (i == attached_windows_.size())
300 size = work_area.bottom() - last_y;
301 if (!GetHeightBeforeObscured(attached_windows_[i]))
302 SetHeightBeforeObscured(attached_windows_[i], attached_bounds.height());
303 attached_bounds.set_height(size);
304 attached_bounds.set_y(last_y);
305 attached_windows_[i]->SetBounds(attached_bounds);
306 last_y = attached_bounds.bottom();
307 }
308 }
133 } 309 }
134 310
135 void WorkspaceWindowResizer::AdjustBoundsForMainWindow( 311 void WorkspaceWindowResizer::AdjustBoundsForMainWindow(
136 gfx::Rect* bounds) const { 312 gfx::Rect* bounds) const {
137 gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window())); 313 gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window()));
314 if (!attached_windows_.empty() && details_.window_component == HTBOTTOM)
315 work_area.set_height(work_area.height() - total_min_);
138 AdjustBoundsForWindow(work_area, window(), bounds); 316 AdjustBoundsForWindow(work_area, window(), bounds);
317
318 if (!attached_windows_.empty() && details_.window_component == HTRIGHT) {
319 bounds->set_width(std::min(bounds->width(),
320 work_area.right() - total_min_ - bounds->x()));
321 }
139 } 322 }
140 323
141 void WorkspaceWindowResizer::AdjustBoundsForWindow( 324 void WorkspaceWindowResizer::AdjustBoundsForWindow(
142 const gfx::Rect& work_area, 325 const gfx::Rect& work_area,
143 aura::Window* window, 326 aura::Window* window,
144 gfx::Rect* bounds) const { 327 gfx::Rect* bounds) const {
145 if (bounds->bottom() < work_area.bottom()) { 328 if (bounds->bottom() < work_area.bottom()) {
146 int height = GetHeightBeforeObscured(window); 329 int height = GetHeightBeforeObscured(window);
147 if (!height) 330 if (!height)
148 return; 331 return;
(...skipping 12 matching lines...) Expand all
161 bounds->set_height( 344 bounds->set_height(
162 std::max(0, work_area.bottom() - bounds->y())); 345 std::max(0, work_area.bottom() - bounds->y()));
163 if (bounds->height() < min_size.height()) { 346 if (bounds->height() < min_size.height()) {
164 bounds->set_height(min_size.height()); 347 bounds->set_height(min_size.height());
165 bounds->set_y(work_area.bottom() - min_size.height()); 348 bounds->set_y(work_area.bottom() - min_size.height());
166 } 349 }
167 } 350 }
168 351
169 void WorkspaceWindowResizer::ClearCachedHeights() { 352 void WorkspaceWindowResizer::ClearCachedHeights() {
170 ClearHeightBeforeObscured(details_.window); 353 ClearHeightBeforeObscured(details_.window);
354 for (size_t i = 0; i < attached_windows_.size(); ++i)
355 ClearHeightBeforeObscured(attached_windows_[i]);
356 }
357
358 void WorkspaceWindowResizer::ClearCachedWidths() {
359 ClearWidthBeforeObscured(details_.window);
360 for (size_t i = 0; i < attached_windows_.size(); ++i)
361 ClearWidthBeforeObscured(attached_windows_[i]);
171 } 362 }
172 363
173 bool WorkspaceWindowResizer::TouchesBottomOfScreen() const { 364 bool WorkspaceWindowResizer::TouchesBottomOfScreen() const {
174 gfx::Rect work_area( 365 gfx::Rect work_area(
175 gfx::Screen::GetMonitorWorkAreaNearestWindow(details_.window)); 366 gfx::Screen::GetMonitorWorkAreaNearestWindow(details_.window));
176 return details_.window->bounds().bottom() == work_area.bottom(); 367 return (attached_windows_.empty() &&
368 details_.window->bounds().bottom() == work_area.bottom()) ||
369 (!attached_windows_.empty() &&
370 attached_windows_.back()->bounds().bottom() == work_area.bottom());
371 }
372
373 bool WorkspaceWindowResizer::TouchesRightSideOfScreen() const {
374 gfx::Rect work_area(
375 gfx::Screen::GetMonitorWorkAreaNearestWindow(details_.window));
376 return (attached_windows_.empty() &&
377 details_.window->bounds().right() == work_area.right()) ||
378 (!attached_windows_.empty() &&
379 attached_windows_.back()->bounds().right() == work_area.right());
380 }
381
382 int WorkspaceWindowResizer::PrimaryAxisSize(const gfx::Size& size) const {
383 return PrimaryAxisCoordinate(size.width(), size.height());
384 }
385
386 int WorkspaceWindowResizer::PrimaryAxisCoordinate(int x, int y) const {
387 switch (details_.window_component) {
388 case HTRIGHT:
389 return x;
390 case HTBOTTOM:
391 return y;
392 default:
393 NOTREACHED();
394 }
395 return 0;
177 } 396 }
178 397
179 } // namespace internal 398 } // namespace internal
180 } // namespace ash 399 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698