OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |