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 <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "ash/display/display_controller.h" | 10 #include "ash/display/display_controller.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 | 34 |
35 // Duration of the animation when snapping the window into place. | 35 // Duration of the animation when snapping the window into place. |
36 const int kSnapDurationMS = 100; | 36 const int kSnapDurationMS = 100; |
37 | 37 |
38 // Returns true if should snap to the edge. | 38 // Returns true if should snap to the edge. |
39 bool ShouldSnapToEdge(int distance_from_edge, int grid_size) { | 39 bool ShouldSnapToEdge(int distance_from_edge, int grid_size) { |
40 return distance_from_edge <= grid_size / 2 && | 40 return distance_from_edge <= grid_size / 2 && |
41 distance_from_edge > -grid_size * 2; | 41 distance_from_edge > -grid_size * 2; |
42 } | 42 } |
43 | 43 |
| 44 // Returns true if Ash has more than one root window. |
| 45 bool HasSecondaryRootWindow() { |
| 46 return Shell::GetAllRootWindows().size() > 1; |
| 47 } |
| 48 |
| 49 // When there are two root windows, returns one of the root windows which is not |
| 50 // |root_window|. Returns NULL if only one root window exists. |
| 51 aura::RootWindow* GetAnotherRootWindow(aura::RootWindow* root_window) { |
| 52 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); |
| 53 if (root_windows.size() < 2) |
| 54 return NULL; |
| 55 DCHECK_EQ(2U, root_windows.size()); |
| 56 if (root_windows[0] == root_window) |
| 57 return root_windows[1]; |
| 58 return root_windows[0]; |
| 59 } |
| 60 |
44 } // namespace | 61 } // namespace |
45 | 62 |
46 // static | 63 // static |
47 const int WorkspaceWindowResizer::kMinOnscreenSize = 20; | 64 const int WorkspaceWindowResizer::kMinOnscreenSize = 20; |
48 | 65 |
49 // static | 66 // static |
50 const int WorkspaceWindowResizer::kMinOnscreenHeight = 32; | 67 const int WorkspaceWindowResizer::kMinOnscreenHeight = 32; |
51 | 68 |
52 WorkspaceWindowResizer::~WorkspaceWindowResizer() { | 69 WorkspaceWindowResizer::~WorkspaceWindowResizer() { |
53 Shell* shell = Shell::GetInstance(); | 70 Shell* shell = Shell::GetInstance(); |
54 shell->display_controller()->set_dont_warp_mouse(false); | 71 shell->display_controller()->set_dont_warp_mouse(false); |
55 shell->cursor_manager()->UnlockCursor(); | 72 shell->cursor_manager()->UnlockCursor(); |
56 } | 73 } |
57 | 74 |
58 // static | 75 // static |
59 WorkspaceWindowResizer* WorkspaceWindowResizer::Create( | 76 WorkspaceWindowResizer* WorkspaceWindowResizer::Create( |
60 aura::Window* window, | 77 aura::Window* window, |
61 const gfx::Point& location_in_parent, | 78 const gfx::Point& location_in_parent, |
62 int window_component, | 79 int window_component, |
63 const std::vector<aura::Window*>& attached_windows) { | 80 const std::vector<aura::Window*>& attached_windows) { |
64 Details details(window, location_in_parent, window_component); | 81 Details details(window, location_in_parent, window_component); |
65 return details.is_resizable ? | 82 return details.is_resizable ? |
66 new WorkspaceWindowResizer(details, attached_windows) : NULL; | 83 new WorkspaceWindowResizer(details, attached_windows) : NULL; |
67 } | 84 } |
68 | 85 |
69 void WorkspaceWindowResizer::Drag(const gfx::Point& location, int event_flags) { | 86 void WorkspaceWindowResizer::Drag(const gfx::Point& location, int event_flags) { |
70 std::pair<aura::RootWindow*, gfx::Point> actual_location = | 87 std::pair<aura::RootWindow*, gfx::Point> actual_location = |
71 wm::GetRootWindowRelativeToWindow(window()->parent(), location); | 88 wm::GetRootWindowRelativeToWindow(window()->parent(), location); |
| 89 aura::RootWindow* current_root = actual_location.first; |
| 90 gfx::Point location_in_parent = actual_location.second; |
| 91 aura::Window::ConvertPointToWindow(current_root, |
| 92 window()->parent(), |
| 93 &location_in_parent); |
72 | 94 |
73 // TODO(yusukes): Implement dragging a window from one display to another. | 95 // Do not use |location| below this point, use |location_in_parent| instead. |
74 aura::RootWindow* current_root = actual_location.first; | 96 // When the pointer is on |window()->GetRootWindow()|, |location| and |
75 if (current_root != window()->GetRootWindow()) | 97 // |location_in_parent| have the same value and both of them are in |
76 return; | 98 // |window()->parent()|'s coordinates, but once the pointer enters the |
| 99 // other root window, you will see an unexpected value on the former. See |
| 100 // comments in wm::GetRootWindowRelativeToWindow() for details. |
77 | 101 |
78 int grid_size = event_flags & ui::EF_CONTROL_DOWN ? | 102 int grid_size = event_flags & ui::EF_CONTROL_DOWN ? |
79 0 : ash::Shell::GetInstance()->GetGridSize(); | 103 0 : ash::Shell::GetInstance()->GetGridSize(); |
80 gfx::Rect bounds = CalculateBoundsForDrag(details_, location, grid_size); | 104 gfx::Rect bounds = // in |window()->parent()|'s coordinates. |
| 105 CalculateBoundsForDrag(details_, location_in_parent, grid_size); |
81 | 106 |
82 if (wm::IsWindowNormal(details_.window)) | 107 if (wm::IsWindowNormal(details_.window)) |
83 AdjustBoundsForMainWindow(&bounds, grid_size); | 108 AdjustBoundsForMainWindow(&bounds, grid_size); |
84 if (bounds != details_.window->bounds()) { | 109 if (bounds != details_.window->bounds()) { |
85 if (!did_move_or_resize_) | 110 if (!did_move_or_resize_) |
86 RestackWindows(); | 111 RestackWindows(); |
87 did_move_or_resize_ = true; | 112 did_move_or_resize_ = true; |
88 } | 113 } |
89 UpdatePhantomWindow(location, bounds, grid_size); | 114 |
| 115 const bool in_original_root = (window()->GetRootWindow() == current_root); |
| 116 // Hide a phantom window for snapping if the cursor is in another root window. |
| 117 if (in_original_root) |
| 118 UpdateSnapPhantomWindow(location_in_parent, bounds, grid_size); |
| 119 else |
| 120 snap_phantom_window_controller_.reset(); |
| 121 |
| 122 // Show a phantom window for dragging in another root window. |
| 123 if (HasSecondaryRootWindow()) |
| 124 UpdateDragPhantomWindow(bounds); |
| 125 else |
| 126 drag_phantom_window_controller_.reset(); |
| 127 |
90 if (!attached_windows_.empty()) | 128 if (!attached_windows_.empty()) |
91 LayoutAttachedWindows(bounds, grid_size); | 129 LayoutAttachedWindows(bounds, grid_size); |
92 if (bounds != details_.window->bounds()) | 130 if (bounds != details_.window->bounds()) |
93 details_.window->SetBounds(bounds); | 131 details_.window->SetBounds(bounds); |
94 // WARNING: we may have been deleted. | 132 // WARNING: we may have been deleted. |
95 } | 133 } |
96 | 134 |
97 void WorkspaceWindowResizer::CompleteDrag(int event_flags) { | 135 void WorkspaceWindowResizer::CompleteDrag(int event_flags) { |
98 phantom_window_controller_.reset(); | 136 drag_phantom_window_controller_.reset(); |
| 137 snap_phantom_window_controller_.reset(); |
99 if (!did_move_or_resize_ || details_.window_component != HTCAPTION) | 138 if (!did_move_or_resize_ || details_.window_component != HTCAPTION) |
100 return; | 139 return; |
101 | 140 |
102 if (snap_type_ == SNAP_LEFT_EDGE || snap_type_ == SNAP_RIGHT_EDGE) { | 141 if (snap_type_ == SNAP_LEFT_EDGE || snap_type_ == SNAP_RIGHT_EDGE) { |
103 if (!GetRestoreBoundsInScreen(details_.window)) | 142 if (!GetRestoreBoundsInScreen(details_.window)) |
104 SetRestoreBoundsInParent(details_.window, details_.initial_bounds); | 143 SetRestoreBoundsInParent(details_.window, details_.initial_bounds); |
105 details_.window->SetBounds(snap_sizer_->target_bounds()); | 144 details_.window->SetBounds(snap_sizer_->target_bounds()); |
106 return; | 145 return; |
107 } | 146 } |
108 | 147 |
(...skipping 24 matching lines...) Expand all Loading... |
133 ui::ScopedLayerAnimationSettings scoped_setter( | 172 ui::ScopedLayerAnimationSettings scoped_setter( |
134 details_.window->layer()->GetAnimator()); | 173 details_.window->layer()->GetAnimator()); |
135 // Use a small duration since the grid is small. | 174 // Use a small duration since the grid is small. |
136 scoped_setter.SetTransitionDuration( | 175 scoped_setter.SetTransitionDuration( |
137 base::TimeDelta::FromMilliseconds(kSnapDurationMS)); | 176 base::TimeDelta::FromMilliseconds(kSnapDurationMS)); |
138 details_.window->SetBounds(bounds); | 177 details_.window->SetBounds(bounds); |
139 } | 178 } |
140 } | 179 } |
141 | 180 |
142 void WorkspaceWindowResizer::RevertDrag() { | 181 void WorkspaceWindowResizer::RevertDrag() { |
143 phantom_window_controller_.reset(); | 182 drag_phantom_window_controller_.reset(); |
| 183 snap_phantom_window_controller_.reset(); |
144 | 184 |
145 if (!did_move_or_resize_) | 185 if (!did_move_or_resize_) |
146 return; | 186 return; |
147 | 187 |
148 details_.window->SetBounds(details_.initial_bounds); | 188 details_.window->SetBounds(details_.initial_bounds); |
149 if (details_.window_component == HTRIGHT) { | 189 if (details_.window_component == HTRIGHT) { |
150 int last_x = details_.initial_bounds.right(); | 190 int last_x = details_.initial_bounds.right(); |
151 for (size_t i = 0; i < attached_windows_.size(); ++i) { | 191 for (size_t i = 0; i < attached_windows_.size(); ++i) { |
152 gfx::Rect bounds(attached_windows_[i]->bounds()); | 192 gfx::Rect bounds(attached_windows_[i]->bounds()); |
153 bounds.set_x(last_x); | 193 bounds.set_x(last_x); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 static_cast<float>(total_available)); | 266 static_cast<float>(total_available)); |
227 } else { | 267 } else { |
228 compress_fraction_.push_back(0.0f); | 268 compress_fraction_.push_back(0.0f); |
229 } | 269 } |
230 } | 270 } |
231 } | 271 } |
232 | 272 |
233 gfx::Rect WorkspaceWindowResizer::GetFinalBounds( | 273 gfx::Rect WorkspaceWindowResizer::GetFinalBounds( |
234 const gfx::Rect& bounds, | 274 const gfx::Rect& bounds, |
235 int grid_size) const { | 275 int grid_size) const { |
236 if (phantom_window_controller_.get() && | 276 if (snap_phantom_window_controller_.get() && |
237 phantom_window_controller_->IsShowing()) { | 277 snap_phantom_window_controller_->IsShowing()) { |
238 return phantom_window_controller_->bounds(); | 278 return snap_phantom_window_controller_->bounds(); |
239 } | 279 } |
240 return AdjustBoundsToGrid(bounds, grid_size); | 280 return AdjustBoundsToGrid(bounds, grid_size); |
241 } | 281 } |
242 | 282 |
243 void WorkspaceWindowResizer::LayoutAttachedWindows( | 283 void WorkspaceWindowResizer::LayoutAttachedWindows( |
244 const gfx::Rect& bounds, | 284 const gfx::Rect& bounds, |
245 int grid_size) { | 285 int grid_size) { |
246 gfx::Rect work_area( | 286 gfx::Rect work_area( |
247 gfx::Screen::GetDisplayNearestWindow(window()).work_area()); | 287 gfx::Screen::GetDisplayNearestWindow(window()).work_area()); |
248 std::vector<int> sizes; | 288 std::vector<int> sizes; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 if (i == attached_windows_.size()) | 345 if (i == attached_windows_.size()) |
306 size = end - current; | 346 size = end - current; |
307 current += size; | 347 current += size; |
308 sizes->push_back(size); | 348 sizes->push_back(size); |
309 } | 349 } |
310 } | 350 } |
311 } | 351 } |
312 | 352 |
313 void WorkspaceWindowResizer::AdjustBoundsForMainWindow( | 353 void WorkspaceWindowResizer::AdjustBoundsForMainWindow( |
314 gfx::Rect* bounds, int grid_size) const { | 354 gfx::Rect* bounds, int grid_size) const { |
315 // Always keep kMinOnscreenHeight on the bottom. | 355 // Always keep kMinOnscreenHeight on the bottom except when an extended |
| 356 // display is available and a window is being dragged. |
316 gfx::Rect work_area( | 357 gfx::Rect work_area( |
317 ScreenAsh::GetDisplayWorkAreaBoundsInParent(details_.window)); | 358 ScreenAsh::GetDisplayWorkAreaBoundsInParent(details_.window)); |
318 int max_y = AlignToGridRoundUp(work_area.bottom() - kMinOnscreenHeight, | 359 int max_y = AlignToGridRoundUp(work_area.bottom() - kMinOnscreenHeight, |
319 grid_size); | 360 grid_size); |
320 if (bounds->y() > max_y) | 361 if ((details_.window_component != HTCAPTION || !HasSecondaryRootWindow()) && |
| 362 bounds->y() > max_y) { |
321 bounds->set_y(max_y); | 363 bounds->set_y(max_y); |
| 364 } |
322 | 365 |
323 // Don't allow dragging above the top of the display. | 366 // Don't allow dragging above the top of the display except when an extended |
324 if (bounds->y() <= work_area.y()) | 367 // display is available and a window is being dragged. |
| 368 if ((details_.window_component != HTCAPTION || !HasSecondaryRootWindow()) && |
| 369 bounds->y() <= work_area.y()) { |
325 bounds->set_y(work_area.y()); | 370 bounds->set_y(work_area.y()); |
| 371 } |
326 | 372 |
327 if (grid_size >= 0 && details_.window_component == HTCAPTION) | 373 if (grid_size >= 0 && details_.window_component == HTCAPTION) |
328 SnapToWorkAreaEdges(work_area, bounds, grid_size); | 374 SnapToWorkAreaEdges(work_area, bounds, grid_size); |
329 | 375 |
330 if (attached_windows_.empty()) | 376 if (attached_windows_.empty()) |
331 return; | 377 return; |
332 | 378 |
333 if (details_.window_component == HTRIGHT) { | 379 if (details_.window_component == HTRIGHT) { |
334 bounds->set_width(std::min(bounds->width(), | 380 bounds->set_width(std::min(bounds->width(), |
335 work_area.right() - total_min_ - bounds->x())); | 381 work_area.right() - total_min_ - bounds->x())); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 case HTRIGHT: | 430 case HTRIGHT: |
385 return x; | 431 return x; |
386 case HTBOTTOM: | 432 case HTBOTTOM: |
387 return y; | 433 return y; |
388 default: | 434 default: |
389 NOTREACHED(); | 435 NOTREACHED(); |
390 } | 436 } |
391 return 0; | 437 return 0; |
392 } | 438 } |
393 | 439 |
394 void WorkspaceWindowResizer::UpdatePhantomWindow(const gfx::Point& location, | 440 void WorkspaceWindowResizer::UpdateDragPhantomWindow(const gfx::Rect& bounds) { |
395 const gfx::Rect& bounds, | 441 if (!did_move_or_resize_ || details_.window_component != HTCAPTION || |
396 int grid_size) { | 442 !ShouldAllowMouseWarp()) { |
| 443 return; |
| 444 } |
| 445 |
| 446 // It's available. Show a phantom window on the display if needed. |
| 447 aura::RootWindow* another_root = |
| 448 GetAnotherRootWindow(window()->GetRootWindow()); |
| 449 const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen()); |
| 450 const gfx::Rect bounds_in_screen = |
| 451 ScreenAsh::ConvertRectToScreen(window()->parent(), bounds); |
| 452 const gfx::Rect phantom(root_bounds_in_screen.Intersect(bounds_in_screen)); |
| 453 |
| 454 if (!phantom.IsEmpty()) { |
| 455 if (!drag_phantom_window_controller_.get()) { |
| 456 drag_phantom_window_controller_.reset( |
| 457 new PhantomWindowController(window())); |
| 458 drag_phantom_window_controller_->Show(phantom); |
| 459 } else { |
| 460 drag_phantom_window_controller_->SetBounds(phantom); // no animation |
| 461 } |
| 462 } else { |
| 463 drag_phantom_window_controller_.reset(); |
| 464 } |
| 465 } |
| 466 |
| 467 void WorkspaceWindowResizer::UpdateSnapPhantomWindow(const gfx::Point& location, |
| 468 const gfx::Rect& bounds, |
| 469 int grid_size) { |
397 if (!did_move_or_resize_ || details_.window_component != HTCAPTION) | 470 if (!did_move_or_resize_ || details_.window_component != HTCAPTION) |
398 return; | 471 return; |
399 | 472 |
400 SnapType last_type = snap_type_; | 473 SnapType last_type = snap_type_; |
401 snap_type_ = GetSnapType(location); | 474 snap_type_ = GetSnapType(location); |
402 if (snap_type_ == SNAP_NONE || snap_type_ != last_type) { | 475 if (snap_type_ == SNAP_NONE || snap_type_ != last_type) { |
403 phantom_window_controller_.reset(); | 476 snap_phantom_window_controller_.reset(); |
404 snap_sizer_.reset(); | 477 snap_sizer_.reset(); |
405 if (snap_type_ == SNAP_NONE) | 478 if (snap_type_ == SNAP_NONE) |
406 return; | 479 return; |
407 } | 480 } |
408 if (!snap_sizer_.get()) { | 481 if (!snap_sizer_.get()) { |
409 SnapSizer::Edge edge = (snap_type_ == SNAP_LEFT_EDGE) ? | 482 SnapSizer::Edge edge = (snap_type_ == SNAP_LEFT_EDGE) ? |
410 SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; | 483 SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; |
411 snap_sizer_.reset( | 484 snap_sizer_.reset( |
412 new SnapSizer(details_.window, location, edge, grid_size)); | 485 new SnapSizer(details_.window, location, edge, grid_size)); |
413 } else { | 486 } else { |
414 snap_sizer_->Update(location); | 487 snap_sizer_->Update(location); |
415 } | 488 } |
416 if (!phantom_window_controller_.get()) { | 489 if (!snap_phantom_window_controller_.get()) { |
417 phantom_window_controller_.reset( | 490 snap_phantom_window_controller_.reset( |
418 new PhantomWindowController(details_.window)); | 491 new PhantomWindowController(details_.window)); |
419 } | 492 } |
420 phantom_window_controller_->Show(ScreenAsh::ConvertRectToScreen( | 493 snap_phantom_window_controller_->Show(ScreenAsh::ConvertRectToScreen( |
421 details_.window->parent(), snap_sizer_->target_bounds())); | 494 details_.window->parent(), snap_sizer_->target_bounds())); |
422 } | 495 } |
423 | 496 |
424 void WorkspaceWindowResizer::RestackWindows() { | 497 void WorkspaceWindowResizer::RestackWindows() { |
425 if (attached_windows_.empty()) | 498 if (attached_windows_.empty()) |
426 return; | 499 return; |
427 // Build a map from index in children to window, returning if there is a | 500 // Build a map from index in children to window, returning if there is a |
428 // window with a different parent. | 501 // window with a different parent. |
429 typedef std::map<size_t, aura::Window*> IndexToWindowMap; | 502 typedef std::map<size_t, aura::Window*> IndexToWindowMap; |
430 IndexToWindowMap map; | 503 IndexToWindowMap map; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 } | 538 } |
466 | 539 |
467 bool WorkspaceWindowResizer::ShouldAllowMouseWarp() const { | 540 bool WorkspaceWindowResizer::ShouldAllowMouseWarp() const { |
468 return (details_.window_component == HTCAPTION) && | 541 return (details_.window_component == HTCAPTION) && |
469 (window()->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE) && | 542 (window()->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE) && |
470 (window()->type() == aura::client::WINDOW_TYPE_NORMAL); | 543 (window()->type() == aura::client::WINDOW_TYPE_NORMAL); |
471 } | 544 } |
472 | 545 |
473 } // namespace internal | 546 } // namespace internal |
474 } // namespace ash | 547 } // namespace ash |
OLD | NEW |