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

Side by Side Diff: ash/wm/dock/docked_window_layout_manager.cc

Issue 19054013: Implement automatic layout and stacking for docked windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@issue_233331_sized
Patch Set: Implement automatic layout and stacking (separate CL for attached panels and comments) Created 7 years, 5 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
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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/dock/docked_window_layout_manager.h" 5 #include "ash/wm/dock/docked_window_layout_manager.h"
6 6
7 #include "ash/launcher/launcher.h" 7 #include "ash/launcher/launcher.h"
8 #include "ash/screen_ash.h" 8 #include "ash/screen_ash.h"
9 #include "ash/shelf/shelf_layout_manager.h" 9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shelf/shelf_types.h" 10 #include "ash/shelf/shelf_types.h"
(...skipping 11 matching lines...) Expand all
22 #include "ui/aura/window.h" 22 #include "ui/aura/window.h"
23 #include "ui/gfx/rect.h" 23 #include "ui/gfx/rect.h"
24 24
25 namespace ash { 25 namespace ash {
26 namespace internal { 26 namespace internal {
27 27
28 // Minimum, maximum width of the dock area and a width of the gap 28 // Minimum, maximum width of the dock area and a width of the gap
29 const int DockedWindowLayoutManager::kMinDockWidth = 200; 29 const int DockedWindowLayoutManager::kMinDockWidth = 200;
30 const int DockedWindowLayoutManager::kMaxDockWidth = 450; 30 const int DockedWindowLayoutManager::kMaxDockWidth = 450;
31 const int DockedWindowLayoutManager::kMinDockGap = 2; 31 const int DockedWindowLayoutManager::kMinDockGap = 2;
32 const int kWindowIdealSpacing = 4;
32 33
33 namespace { 34 namespace {
34 35
35 DockedWindowLayoutManager* GetDockLayoutManager(aura::Window* window, 36 DockedWindowLayoutManager* GetDockLayoutManager(aura::Window* window,
36 const gfx::Point& location) { 37 const gfx::Point& location) {
37 gfx::Rect near_location(location, gfx::Size()); 38 gfx::Rect near_location(location, gfx::Size());
38 aura::Window* dock = Shell::GetContainer( 39 aura::Window* dock = Shell::GetContainer(
39 wm::GetRootWindowMatching(near_location), 40 wm::GetRootWindowMatching(near_location),
40 kShellWindowId_DockedContainer); 41 kShellWindowId_DockedContainer);
41 return static_cast<internal::DockedWindowLayoutManager*>( 42 return static_cast<internal::DockedWindowLayoutManager*>(
42 dock->layout_manager()); 43 dock->layout_manager());
43 } 44 }
44 45
45 // Certain windows (minimized, hidden or popups) do not matter to docking. 46 // Certain windows (minimized, hidden or popups) do not matter to docking.
46 bool IsUsedByLayout(aura::Window* window) { 47 bool IsUsedByLayout(aura::Window* window) {
47 return (window->IsVisible() && 48 return (window->IsVisible() &&
48 !wm::IsWindowMinimized(window) && 49 !wm::IsWindowMinimized(window) &&
49 window->type() != aura::client::WINDOW_TYPE_POPUP); 50 window->type() != aura::client::WINDOW_TYPE_POPUP);
50 } 51 }
51 52
53 bool CompareWindowPos(const aura::Window* win1, const aura::Window* win2) {
54 return win1->GetTargetBounds().CenterPoint().y() <
55 win2->GetTargetBounds().CenterPoint().y();
56 }
57
52 } // namespace 58 } // namespace
53 59
54 //////////////////////////////////////////////////////////////////////////////// 60 ////////////////////////////////////////////////////////////////////////////////
55 // DockLayoutManager public implementation: 61 // DockLayoutManager public implementation:
56 DockedWindowLayoutManager::DockedWindowLayoutManager( 62 DockedWindowLayoutManager::DockedWindowLayoutManager(
57 aura::Window* dock_container) 63 aura::Window* dock_container)
58 : dock_container_(dock_container), 64 : dock_container_(dock_container),
59 in_layout_(false), 65 in_layout_(false),
66 dragged_former_child_(NULL),
60 dragged_window_(NULL), 67 dragged_window_(NULL),
61 launcher_(NULL), 68 launcher_(NULL),
62 shelf_layout_manager_(NULL), 69 shelf_layout_manager_(NULL),
63 shelf_hidden_(false), 70 shelf_hidden_(false),
64 docked_width_(0), 71 docked_width_(0),
65 alignment_(DOCKED_ALIGNMENT_NONE) { 72 alignment_(DOCKED_ALIGNMENT_NONE),
73 last_active_(NULL) {
66 DCHECK(dock_container); 74 DCHECK(dock_container);
67 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> 75 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
68 AddObserver(this); 76 AddObserver(this);
69 Shell::GetInstance()->AddShellObserver(this); 77 Shell::GetInstance()->AddShellObserver(this);
70 } 78 }
71 79
72 DockedWindowLayoutManager::~DockedWindowLayoutManager() { 80 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
73 Shutdown(); 81 Shutdown();
74 } 82 }
75 83
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 window->type() != aura::client::WINDOW_TYPE_POPUP) { 130 window->type() != aura::client::WINDOW_TYPE_POPUP) {
123 found_docked_window = true; 131 found_docked_window = true;
124 break; 132 break;
125 } 133 }
126 } 134 }
127 if (!found_docked_window) 135 if (!found_docked_window)
128 alignment_ = AlignmentOfWindow(dragged_window_); 136 alignment_ = AlignmentOfWindow(dragged_window_);
129 } 137 }
130 dragged_window_ = NULL; 138 dragged_window_ = NULL;
131 139
140 // We no longer need |dragged_former_child_| if it is added back;
141 if (dragged_former_child_) {
142 if (dragged_former_child_->parent() != dock_container_)
143 dragged_former_child_->RemoveObserver(this);
144 dragged_former_child_ = NULL;
145 }
146
132 Relayout(); 147 Relayout();
133 UpdateDockBounds(); 148 UpdateDockBounds();
134 } 149 }
135 150
136 // static 151 // static
137 bool DockedWindowLayoutManager::ShouldWindowDock(aura::Window* window, 152 bool DockedWindowLayoutManager::ShouldWindowDock(aura::Window* window,
138 const gfx::Point& location) { 153 const gfx::Point& location) {
139 DockedWindowLayoutManager* layout_manager = GetDockLayoutManager(window, 154 DockedWindowLayoutManager* layout_manager = GetDockLayoutManager(window,
140 location); 155 location);
141 const DockedAlignment alignment = layout_manager->CalculateAlignment(); 156 const DockedAlignment alignment = layout_manager->CalculateAlignment();
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 UpdateDockBounds(); 234 UpdateDockBounds();
220 } 235 }
221 236
222 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) { 237 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
223 if (child->type() == aura::client::WINDOW_TYPE_POPUP) 238 if (child->type() == aura::client::WINDOW_TYPE_POPUP)
224 return; 239 return;
225 240
226 // If this is the first window getting docked - update alignment. 241 // If this is the first window getting docked - update alignment.
227 if (alignment_ == DOCKED_ALIGNMENT_NONE) 242 if (alignment_ == DOCKED_ALIGNMENT_NONE)
228 alignment_ = AlignmentOfWindow(child); 243 alignment_ = AlignmentOfWindow(child);
229 child->AddObserver(this); 244 // We no longer need |dragged_former_child_| if it is added back.
245 if (child == dragged_former_child_)
246 dragged_former_child_ = NULL;
247 else
248 child->AddObserver(this);
230 Relayout(); 249 Relayout();
231 UpdateDockBounds(); 250 UpdateDockBounds();
232 } 251 }
233 252
234 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { 253 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
235 if (child->type() == aura::client::WINDOW_TYPE_POPUP) 254 if (child->type() == aura::client::WINDOW_TYPE_POPUP)
236 return; 255 return;
237 256
238 // Try to find a child that is not a popup and is not being dragged. 257 // Try to find a child that is not a popup and is not being dragged.
239 bool found_docked_window = false; 258 bool found_docked_window = false;
240 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 259 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
241 aura::Window* window(dock_container_->children()[i]); 260 aura::Window* window(dock_container_->children()[i]);
242 if (window != dragged_window_ && 261 if (window != dragged_window_ &&
243 window->type() != aura::client::WINDOW_TYPE_POPUP) { 262 window->type() != aura::client::WINDOW_TYPE_POPUP) {
244 found_docked_window = true; 263 found_docked_window = true;
245 break; 264 break;
246 } 265 }
247 } 266 }
248 if (!found_docked_window) 267 if (!found_docked_window)
249 alignment_ = DOCKED_ALIGNMENT_NONE; 268 alignment_ = DOCKED_ALIGNMENT_NONE;
250 269
251 child->RemoveObserver(this); 270 // Keep track of former children if they are dragged as they can be reparented
271 // temporarily just for the drag.
272 if (child == dragged_window_)
273 dragged_former_child_ = child;
274 else
275 child->RemoveObserver(this);
276
252 // Close the dock and expand workspace work area. 277 // Close the dock and expand workspace work area.
253 Relayout(); 278 Relayout();
254 279
255 // When a panel is removed from this layout manager it may still be dragged. 280 // When a panel is removed from this layout manager it may still be dragged.
256 // Delay updating dock and workspace bounds until the drag is completed. This 281 // Delay updating dock and workspace bounds until the drag is completed. This
257 // preserves the docked area width until the panel drag is finished. 282 // preserves the docked area width until the panel drag is finished.
258 if (child->type() != aura::client::WINDOW_TYPE_PANEL) 283 if (child->type() != aura::client::WINDOW_TYPE_PANEL)
259 UpdateDockBounds(); 284 UpdateDockBounds();
260 } 285 }
261 286
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 // The window property will still be set, but no actual change will occur 344 // The window property will still be set, but no actual change will occur
320 // until WillChangeVisibilityState is called when the shelf is visible again 345 // until WillChangeVisibilityState is called when the shelf is visible again
321 if (shelf_hidden_) 346 if (shelf_hidden_)
322 return; 347 return;
323 if (wm::IsWindowMinimized(window)) 348 if (wm::IsWindowMinimized(window))
324 MinimizeWindow(window); 349 MinimizeWindow(window);
325 else 350 else
326 RestoreWindow(window); 351 RestoreWindow(window);
327 } 352 }
328 353
354 void DockedWindowLayoutManager::OnWindowBoundsChanged(
355 aura::Window* window,
356 const gfx::Rect& old_bounds,
357 const gfx::Rect& new_bounds) {
358 if (window == dragged_former_child_)
359 Relayout();
360 if (window == dragged_former_child_)
361 LOG(INFO) << "OnWindowBoundsChanged";
362 }
363
329 //////////////////////////////////////////////////////////////////////////////// 364 ////////////////////////////////////////////////////////////////////////////////
330 // DockLayoutManager, aura::client::ActivationChangeObserver implementation: 365 // DockLayoutManager, aura::client::ActivationChangeObserver implementation:
331 366
332 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, 367 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
333 aura::Window* lost_active) { 368 aura::Window* lost_active) {
334 // Ignore if the window that is not managed by this was activated. 369 // Ignore if the window that is not managed by this was activated.
335 aura::Window* ancestor = NULL; 370 aura::Window* ancestor = NULL;
336 for (aura::Window* parent = gained_active; 371 for (aura::Window* parent = gained_active;
337 parent; parent = parent->parent()) { 372 parent; parent = parent->parent()) {
338 if (parent->parent() == dock_container_) { 373 if (parent->parent() == dock_container_) {
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 return DOCKED_ALIGNMENT_NONE; 436 return DOCKED_ALIGNMENT_NONE;
402 } 437 }
403 438
404 void DockedWindowLayoutManager::Relayout() { 439 void DockedWindowLayoutManager::Relayout() {
405 if (in_layout_ || alignment_ == DOCKED_ALIGNMENT_NONE) 440 if (in_layout_ || alignment_ == DOCKED_ALIGNMENT_NONE)
406 return; 441 return;
407 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); 442 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
408 443
409 gfx::Rect dock_bounds = dock_container_->bounds(); 444 gfx::Rect dock_bounds = dock_container_->bounds();
410 aura::Window* active_window = NULL; 445 aura::Window* active_window = NULL;
446 aura::Window::Windows visible_windows;
411 docked_width_ = 0; 447 docked_width_ = 0;
412 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 448 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
413 aura::Window* window(dock_container_->children()[i]); 449 aura::Window* window(dock_container_->children()[i]);
414 450
415 if (!IsUsedByLayout(window)) 451 if (!IsUsedByLayout(window))
416 continue; 452 continue;
417 453
418 // If the shelf is currently hidden (full-screen mode), hide window until 454 // If the shelf is currently hidden (full-screen mode), hide window until
419 // full-screen mode is exited. 455 // full-screen mode is exited.
420 if (shelf_hidden_) { 456 if (shelf_hidden_) {
421 // The call to Hide does not set the minimize property, so the window will 457 // The call to Hide does not set the minimize property, so the window will
422 // be restored when the shelf becomes visible again. 458 // be restored when the shelf becomes visible again.
423 window->Hide(); 459 window->Hide();
424 continue; 460 continue;
425 } 461 }
426
427 if (window->HasFocus() || 462 if (window->HasFocus() ||
428 window->Contains( 463 window->Contains(
429 aura::client::GetFocusClient(window)->GetFocusedWindow())) { 464 aura::client::GetFocusClient(window)->GetFocusedWindow())) {
430 DCHECK(!active_window); 465 DCHECK(!active_window);
431 active_window = window; 466 active_window = window;
432 } 467 }
468 visible_windows.push_back(window);
469 }
433 470
434 // all docked windows other than the one currently dragged remain stuck 471 // Consider windows that were formerly children of the |dock_container_|
435 // to the screen edge 472 // when fanning out other child windows.
473 if (dragged_former_child_)
474 visible_windows.push_back(dragged_former_child_);
475
476 // Sort windows by their center positions and fan out overlapping
477 // windows.
478 std::sort(visible_windows.begin(), visible_windows.end(), CompareWindowPos);
479 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
480 dock_container_);
481 const gfx::Rect work_area = display.work_area();
482 int available_room = work_area.height();
483 for (aura::Window::Windows::const_iterator iter = visible_windows.begin();
484 iter != visible_windows.end(); ++iter) {
485 available_room -= (*iter)->bounds().height();
486 }
487 const int num_windows = visible_windows.size();
488 const int delta = available_room /
489 ((available_room > 0 || num_windows <= 1) ?
490 num_windows + 1 : num_windows - 1);
491 int y_pos = (available_room > 0) ? delta : 0;
492
493 for (aura::Window::Windows::const_iterator iter = visible_windows.begin();
494 iter != visible_windows.end(); ++iter) {
495 aura::Window* window = *iter;
436 gfx::Rect bounds = window->GetTargetBounds(); 496 gfx::Rect bounds = window->GetTargetBounds();
437 if (window != dragged_window_) { 497 // Do not force position of a single window.
438 switch (alignment_) { 498 if (num_windows == 1)
439 case DOCKED_ALIGNMENT_LEFT: 499 y_pos = bounds.y();
440 bounds.set_x(0); 500
441 break; 501 // Fan out windows evenly distributing the overlap or remaining free space.
442 case DOCKED_ALIGNMENT_RIGHT: 502 bounds.set_y(std::max(work_area.y(),
443 bounds.set_x(dock_bounds.right() - bounds.width()); 503 std::min(work_area.bottom()- bounds.height(),
444 break; 504 y_pos)));
flackr 2013/07/18 22:16:53 The clamping within the work area should never be
varkha 2013/07/18 22:58:14 I think it may still need clamping in valid corner
445 case DOCKED_ALIGNMENT_NONE: 505 y_pos += (bounds.height() + delta);
446 NOTREACHED() << "Relayout called when dock alignment is 'NONE'"; 506
447 break; 507 // All docked windows other than the one currently dragged remain stuck
448 } 508 // to the screen edge.
449 // Keep the dock at least kMinDockWidth when all windows in it overhang. 509 if (window == dragged_window_)
450 docked_width_ = std::min(kMaxDockWidth, 510 continue;
451 std::max(docked_width_, 511 switch (alignment_) {
452 bounds.width() > kMaxDockWidth ? 512 case DOCKED_ALIGNMENT_LEFT:
453 kMinDockWidth : bounds.width())); 513 bounds.set_x(0);
514 break;
515 case DOCKED_ALIGNMENT_RIGHT:
516 bounds.set_x(dock_bounds.right() - bounds.width());
517 break;
518 case DOCKED_ALIGNMENT_NONE:
519 NOTREACHED() << "Relayout called when dock alignment is 'NONE'";
520 break;
454 } 521 }
522 // Keep the dock at least kMinDockWidth when all windows in it overhang.
523 docked_width_ = std::min(kMaxDockWidth,
524 std::max(docked_width_,
525 bounds.width() > kMaxDockWidth ?
526 kMinDockWidth : bounds.width()));
455 SetChildBoundsDirect(window, bounds); 527 SetChildBoundsDirect(window, bounds);
456 } 528 }
457 UpdateStacking(active_window); 529 UpdateStacking(active_window);
458 } 530 }
459 531
460 void DockedWindowLayoutManager::UpdateDockBounds() { 532 void DockedWindowLayoutManager::UpdateDockBounds() {
461 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0); 533 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0);
462 gfx::Rect bounds = gfx::Rect( 534 gfx::Rect bounds = gfx::Rect(
463 alignment_ == DOCKED_ALIGNMENT_RIGHT ? 535 alignment_ == DOCKED_ALIGNMENT_RIGHT ?
464 dock_container_->bounds().right() - dock_inset: 536 dock_container_->bounds().right() - dock_inset:
465 dock_container_->bounds().x(), 537 dock_container_->bounds().x(),
466 dock_container_->bounds().y(), 538 dock_container_->bounds().y(),
467 dock_inset, 539 dock_inset,
468 dock_container_->bounds().height()); 540 dock_container_->bounds().height());
469 docked_bounds_ = bounds + 541 docked_bounds_ = bounds +
470 dock_container_->GetBoundsInScreen().OffsetFromOrigin(); 542 dock_container_->GetBoundsInScreen().OffsetFromOrigin();
471 FOR_EACH_OBSERVER( 543 FOR_EACH_OBSERVER(
472 DockedWindowLayoutManagerObserver, 544 DockedWindowLayoutManagerObserver,
473 observer_list_, 545 observer_list_,
474 OnDockBoundsChanging(bounds)); 546 OnDockBoundsChanging(bounds));
475 } 547 }
476 548
477 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { 549 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
478 // TODO(varkha): Implement restacking to ensure that all docked windows are at 550 if (!active_window) {
479 // least partially visible and selectable. 551 if (!last_active_)
552 return;
553 active_window = last_active_;
554 }
555
556 // We want to to stack the windows like a deck of cards:
557 // ,------.
558 // |,------.|
559 // |,------.|
560 // | active |
561 // | window |
562 // |`------'|
563 // |`------'|
564 // `------'
565 // We use the middle of each window to figure out how to stack the window.
566 // This allows us to update the stacking when a window is being dragged around
567 // by the titlebar.
568 std::map<int, aura::Window*> window_ordering;
569 for (aura::Window::Windows::const_iterator it =
570 dock_container_->children().begin();
571 it != dock_container_->children().end(); ++it) {
572 gfx::Rect bounds = (*it)->bounds();
573 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2,
574 *it));
575 }
576 int active_center_y = active_window->bounds().CenterPoint().y();
577
578 aura::Window* previous_window = NULL;
579 for (std::map<int, aura::Window*>::const_iterator it =
580 window_ordering.begin();
581 it != window_ordering.end() && it->first < active_center_y; ++it) {
582 if (previous_window)
583 dock_container_->StackChildAbove(it->second, previous_window);
584 previous_window = it->second;
585 }
586
587 previous_window = NULL;
588 for (std::map<int, aura::Window*>::const_reverse_iterator it =
589 window_ordering.rbegin();
590 it != window_ordering.rend() && it->first > active_center_y; ++it) {
591 if (previous_window)
592 dock_container_->StackChildAbove(it->second, previous_window);
593 previous_window = it->second;
594 }
595
596 if (active_window->parent() == dock_container_)
597 dock_container_->StackChildAtTop(active_window);
598 if (dragged_window_ && dragged_window_->parent() == dock_container_)
599 dock_container_->StackChildAtTop(dragged_window_);
600 last_active_ = active_window;
480 } 601 }
481 602
482 //////////////////////////////////////////////////////////////////////////////// 603 ////////////////////////////////////////////////////////////////////////////////
483 // keyboard::KeyboardControllerObserver implementation: 604 // keyboard::KeyboardControllerObserver implementation:
484 605
485 void DockedWindowLayoutManager::OnKeyboardBoundsChanging( 606 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
486 const gfx::Rect& keyboard_bounds) { 607 const gfx::Rect& keyboard_bounds) {
487 // This bounds change will have caused a change to the Shelf which does not 608 // This bounds change will have caused a change to the Shelf which does not
488 // propagate automatically to this class, so manually recalculate bounds. 609 // propagate automatically to this class, so manually recalculate bounds.
489 UpdateDockBounds(); 610 UpdateDockBounds();
490 } 611 }
491 612
492 } // namespace internal 613 } // namespace internal
493 } // namespace ash 614 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698