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

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

Powered by Google App Engine
This is Rietveld 408576698