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 3 // found in the LICENSE file. 4 5 #include "ash/wm/dock/docked_window_layout_manager.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/launcher/launcher.h" 9 #include "ash/screen_ash.h" 10 #include "ash/shelf/shelf_layout_manager.h" 11 #include "ash/shelf/shelf_types.h" 12 #include "ash/shelf/shelf_widget.h" 13 #include "ash/shell.h" 14 #include "ash/shell_window_ids.h" 15 #include "ash/wm/coordinate_conversion.h" 16 #include "ash/wm/window_animations.h" 17 #include "ash/wm/window_properties.h" 18 #include "ash/wm/window_state.h" 19 #include "ash/wm/window_util.h" 20 #include "ash/wm/workspace_controller.h" 21 #include "base/auto_reset.h" 22 #include "base/command_line.h" 23 #include "base/metrics/histogram.h" 24 #include "grit/ash_resources.h" 25 #include "third_party/skia/include/core/SkColor.h" 26 #include "third_party/skia/include/core/SkPaint.h" 27 #include "ui/aura/client/activation_client.h" 28 #include "ui/aura/client/focus_client.h" 29 #include "ui/aura/client/window_tree_client.h" 30 #include "ui/aura/root_window.h" 31 #include "ui/aura/window.h" 32 #include "ui/aura/window_delegate.h" 33 #include "ui/base/resource/resource_bundle.h" 34 #include "ui/compositor/scoped_layer_animation_settings.h" 35 #include "ui/gfx/canvas.h" 36 #include "ui/gfx/image/image_skia_operations.h" 37 #include "ui/gfx/rect.h" 38 #include "ui/views/background.h" 39 40 namespace ash { 41 namespace internal { 42 43 // Minimum, maximum width of the dock area and a width of the gap 44 // static 45 const int DockedWindowLayoutManager::kMaxDockWidth = 360; 46 // static 47 const int DockedWindowLayoutManager::kMinDockWidth = 200; 48 // static 49 const int DockedWindowLayoutManager::kMinDockGap = 2; 50 // static 51 const int DockedWindowLayoutManager::kIdealWidth = 250; 52 const int kMinimumHeight = 250; 53 const int kSlideDurationMs = 120; 54 const int kFadeDurationMs = 60; 55 const int kMinimizeDurationMs = 720; 56 57 class DockedBackgroundWidget : public views::Widget, 58 public internal::BackgroundAnimatorDelegate { 59 public: 60 explicit DockedBackgroundWidget(aura::Window* container) 61 : alignment_(DOCKED_ALIGNMENT_NONE), 62 background_animator_(this, 0, kLauncherBackgroundAlpha), 63 alpha_(0), 64 opaque_background_(ui::LAYER_SOLID_COLOR) { 65 InitWidget(container); 66 } 67 68 // Sets widget bounds and sizes opaque background layer to fill the widget. 69 void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) { 70 SetBounds(bounds); 71 opaque_background_.SetBounds(gfx::Rect(bounds.size())); 72 alignment_ = alignment; 73 } 74 75 // Sets the docked area background type and starts transition animation. 76 void SetPaintsBackground( 77 ShelfBackgroundType background_type, 78 BackgroundAnimatorChangeType change_type) { 79 float target_opacity = 80 (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f; 81 scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation; 82 if (change_type != BACKGROUND_CHANGE_IMMEDIATE) { 83 opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings( 84 opaque_background_.GetAnimator())); 85 opaque_background_animation->SetTransitionDuration( 86 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs)); 87 } 88 opaque_background_.SetOpacity(target_opacity); 89 90 // TODO(varkha): use ui::Layer on both opaque_background and normal 91 // background retire background_animator_ at all. It would be simpler. 92 // See also ShelfWidget::SetPaintsBackground. 93 background_animator_.SetPaintsBackground( 94 background_type != SHELF_BACKGROUND_DEFAULT, 95 change_type); 96 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size())); 97 } 98 99 // views::Widget: 100 virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE { 101 const gfx::ImageSkia& launcher_background( 102 alignment_ == DOCKED_ALIGNMENT_LEFT ? 103 launcher_background_left_ : launcher_background_right_); 104 gfx::Rect rect = gfx::Rect(GetWindowBoundsInScreen().size()); 105 SkPaint paint; 106 paint.setAlpha(alpha_); 107 canvas->DrawImageInt( 108 launcher_background, 109 0, 0, launcher_background.width(), launcher_background.height(), 110 alignment_ == DOCKED_ALIGNMENT_LEFT ? 111 rect.width() - launcher_background.width() : 0, 0, 112 launcher_background.width(), rect.height(), 113 false, 114 paint); 115 canvas->DrawImageInt( 116 launcher_background, 117 alignment_ == DOCKED_ALIGNMENT_LEFT ? 118 0 : launcher_background.width() - 1, 0, 119 1, launcher_background.height(), 120 alignment_ == DOCKED_ALIGNMENT_LEFT ? 121 0 : launcher_background.width(), 0, 122 rect.width() - launcher_background.width(), rect.height(), 123 false, 124 paint); 125 } 126 127 // BackgroundAnimatorDelegate: 128 virtual void UpdateBackground(int alpha) OVERRIDE { 129 alpha_ = alpha; 130 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size())); 131 } 132 133 private: 134 void InitWidget(aura::Window* parent) { 135 views::Widget::InitParams params; 136 params.type = views::Widget::InitParams::TYPE_POPUP; 137 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 138 params.can_activate = false; 139 params.keep_on_top = false; 140 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 141 params.parent = parent; 142 params.accept_events = false; 143 set_focus_on_creation(false); 144 Init(params); 145 GetNativeWindow()->SetProperty(internal::kStayInSameRootWindowKey, true); 146 opaque_background_.SetColor(SK_ColorBLACK); 147 opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size())); 148 opaque_background_.SetOpacity(0.0f); 149 GetNativeWindow()->layer()->Add(&opaque_background_); 150 Hide(); 151 152 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 153 gfx::ImageSkia launcher_background = 154 *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_BACKGROUND); 155 launcher_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage( 156 launcher_background, SkBitmapOperations::ROTATION_90_CW); 157 launcher_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage( 158 launcher_background, SkBitmapOperations::ROTATION_270_CW); 159 } 160 161 DockedAlignment alignment_; 162 163 // The animator for the background transitions. 164 internal::BackgroundAnimator background_animator_; 165 166 // The alpha to use for drawing image assets covering the docked background. 167 int alpha_; 168 169 // Solid black background that can be made fully opaque. 170 ui::Layer opaque_background_; 171 172 // Backgrounds created from shelf background by 90 or 270 degree rotation. 173 gfx::ImageSkia launcher_background_left_; 174 gfx::ImageSkia launcher_background_right_; 175 176 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget); 177 }; 178 179 namespace { 180 181 // Returns true if a window is a popup or a transient child. 182 bool IsPopupOrTransient(const aura::Window* window) { 183 return (window->type() == aura::client::WINDOW_TYPE_POPUP || 184 window->transient_parent()); 185 } 186 187 // Certain windows (minimized, hidden or popups) do not matter to docking. 188 bool IsUsedByLayout(const aura::Window* window) { 189 return (window->IsVisible() && 190 !wm::GetWindowState(window)->IsMinimized() && 191 !IsPopupOrTransient(window)); 192 } 193 194 void UndockWindow(aura::Window* window) { 195 gfx::Rect previous_bounds = window->bounds(); 196 aura::Window* old_parent = window->parent(); 197 aura::client::ParentWindowWithContext(window, window, gfx::Rect()); 198 if (window->parent() != old_parent) 199 wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent()); 200 // Start maximize or fullscreen (affecting packaged apps) animation from 201 // previous window bounds. 202 window->layer()->SetBounds(previous_bounds); 203 } 204 205 // Returns width that is as close as possible to |target_width| while being 206 // consistent with docked min and max restrictions and respects the |window|'s 207 // minimum and maximum size. 208 int GetWindowWidthCloseTo(const aura::Window* window, int target_width) { 209 if (!wm::GetWindowState(window)->CanResize()) { 210 DCHECK_LE(window->bounds().width(), 211 DockedWindowLayoutManager::kMaxDockWidth); 212 return window->bounds().width(); 213 } 214 int width = std::max(DockedWindowLayoutManager::kMinDockWidth, 215 std::min(target_width, 216 DockedWindowLayoutManager::kMaxDockWidth)); 217 if (window->delegate()) { 218 if (window->delegate()->GetMinimumSize().width() != 0) 219 width = std::max(width, window->delegate()->GetMinimumSize().width()); 220 if (window->delegate()->GetMaximumSize().width() != 0) 221 width = std::min(width, window->delegate()->GetMaximumSize().width()); 222 } 223 DCHECK_LE(width, DockedWindowLayoutManager::kMaxDockWidth); 224 return width; 225 } 226 227 // Returns height that is as close as possible to |target_height| while 228 // respecting the |window|'s minimum and maximum size. 229 int GetWindowHeightCloseTo(const aura::Window* window, int target_height) { 230 if (!wm::GetWindowState(window)->CanResize()) 231 return window->bounds().height(); 232 int minimum_height = kMinimumHeight; 233 int maximum_height = 0; 234 const aura::WindowDelegate* delegate(window->delegate()); 235 if (delegate) { 236 if (delegate->GetMinimumSize().height() != 0) { 237 minimum_height = std::max(kMinimumHeight, 238 delegate->GetMinimumSize().height()); 239 } 240 if (delegate->GetMaximumSize().height() != 0) 241 maximum_height = delegate->GetMaximumSize().height(); 242 } 243 if (minimum_height) 244 target_height = std::max(target_height, minimum_height); 245 if (maximum_height) 246 target_height = std::min(target_height, maximum_height); 247 return target_height; 248 } 249 250 // A functor used to sort the windows in order of their minimum height. 251 struct CompareMinimumHeight { 252 bool operator()(WindowWithHeight win1, WindowWithHeight win2) { 253 return GetWindowHeightCloseTo(win1.window(), 0) < 254 GetWindowHeightCloseTo(win2.window(), 0); 255 } 256 }; 257 258 // A functor used to sort the windows in order of their center Y position. 259 // |delta| is a pre-calculated distance from the bottom of one window to the top 260 // of the next. Its value can be positive (gap) or negative (overlap). 261 // Half of |delta| is used as a transition point at which windows could ideally 262 // swap positions. 263 struct CompareWindowPos { 264 CompareWindowPos(aura::Window* dragged_window, float delta) 265 : dragged_window_(dragged_window), 266 delta_(delta / 2) {} 267 268 bool operator()(WindowWithHeight window_with_height1, 269 WindowWithHeight window_with_height2) { 270 // Use target coordinates since animations may be active when windows are 271 // reordered. 272 aura::Window* win1(window_with_height1.window()); 273 aura::Window* win2(window_with_height2.window()); 274 gfx::Rect win1_bounds = ScreenAsh::ConvertRectToScreen( 275 win1->parent(), win1->GetTargetBounds()); 276 gfx::Rect win2_bounds = ScreenAsh::ConvertRectToScreen( 277 win2->parent(), win2->GetTargetBounds()); 278 win1_bounds.set_height(window_with_height1.height_); 279 win2_bounds.set_height(window_with_height2.height_); 280 // If one of the windows is the |dragged_window_| attempt to make an 281 // earlier swap between the windows than just based on their centers. 282 // This is possible if the dragged window is at least as tall as the other 283 // window. 284 if (win1 == dragged_window_) 285 return compare_two_windows(win1_bounds, win2_bounds); 286 if (win2 == dragged_window_) 287 return !compare_two_windows(win2_bounds, win1_bounds); 288 // Otherwise just compare the centers. 289 return win1_bounds.CenterPoint().y() < win2_bounds.CenterPoint().y(); 290 } 291 292 // Based on center point tries to deduce where the drag is coming from. 293 // When dragging from below up the transition point is lower. 294 // When dragging from above down the transition point is higher. 295 bool compare_bounds(const gfx::Rect dragged, const gfx::Rect other) { 296 if (dragged.CenterPoint().y() < other.CenterPoint().y()) 297 return dragged.CenterPoint().y() < other.y() - delta_; 298 return dragged.CenterPoint().y() < other.bottom() + delta_; 299 } 300 301 // Performs comparison both ways and selects stable result. 302 bool compare_two_windows(const gfx::Rect bounds1, const gfx::Rect bounds2) { 303 // Try comparing windows in both possible orders and see if the comparison 304 // is stable. 305 bool result1 = compare_bounds(bounds1, bounds2); 306 bool result2 = compare_bounds(bounds2, bounds1); 307 if (result1 != result2) 308 return result1; 309 310 // Otherwise it is not possible to be sure that the windows will not bounce. 311 // In this case just compare the centers. 312 return bounds1.CenterPoint().y() < bounds2.CenterPoint().y(); 313 } 314 315 private: 316 aura::Window* dragged_window_; 317 float delta_; 318 }; 319 320 } // namespace 321 322 //////////////////////////////////////////////////////////////////////////////// 323 // A class that observes launcher shelf for bounds changes. 324 class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver { 325 public: 326 explicit ShelfWindowObserver( 327 DockedWindowLayoutManager* docked_layout_manager) 328 : docked_layout_manager_(docked_layout_manager) { 329 DCHECK(docked_layout_manager_->launcher()->shelf_widget()); 330 docked_layout_manager_->launcher()->shelf_widget()->GetNativeView() 331 ->AddObserver(this); 332 } 333 334 virtual ~ShelfWindowObserver() { 335 if (docked_layout_manager_->launcher() && 336 docked_layout_manager_->launcher()->shelf_widget()) 337 docked_layout_manager_->launcher()->shelf_widget()->GetNativeView() 338 ->RemoveObserver(this); 339 } 340 341 // aura::WindowObserver: 342 virtual void OnWindowBoundsChanged(aura::Window* window, 343 const gfx::Rect& old_bounds, 344 const gfx::Rect& new_bounds) OVERRIDE { 345 shelf_bounds_in_screen_ = ScreenAsh::ConvertRectToScreen( 346 window->parent(), new_bounds); 347 docked_layout_manager_->OnShelfBoundsChanged(); 348 } 349 350 const gfx::Rect& shelf_bounds_in_screen() const { 351 return shelf_bounds_in_screen_; 352 } 353 354 private: 355 DockedWindowLayoutManager* docked_layout_manager_; 356 gfx::Rect shelf_bounds_in_screen_; 357 358 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver); 359 }; 360 361 //////////////////////////////////////////////////////////////////////////////// 362 // DockedWindowLayoutManager public implementation: 363 DockedWindowLayoutManager::DockedWindowLayoutManager( 364 aura::Window* dock_container, WorkspaceController* workspace_controller) 365 : dock_container_(dock_container), 366 in_layout_(false), 367 dragged_window_(NULL), 368 is_dragged_window_docked_(false), 369 is_dragged_from_dock_(false), 370 launcher_(NULL), 371 workspace_controller_(workspace_controller), 372 in_fullscreen_(workspace_controller_->GetWindowState() == 373 WORKSPACE_WINDOW_STATE_FULL_SCREEN), 374 docked_width_(0), 375 alignment_(DOCKED_ALIGNMENT_NONE), 376 last_active_window_(NULL), 377 last_action_time_(base::Time::Now()), 378 background_widget_(new DockedBackgroundWidget(dock_container_)) { 379 DCHECK(dock_container); 380 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> 381 AddObserver(this); 382 Shell::GetInstance()->AddShellObserver(this); 383 } 384 385 DockedWindowLayoutManager::~DockedWindowLayoutManager() { 386 Shutdown(); 387 } 388 389 void DockedWindowLayoutManager::Shutdown() { 390 if (launcher_ && launcher_->shelf_widget()) { 391 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher( 392 launcher_->shelf_widget()->GetNativeWindow()); 393 shelf_layout_manager->RemoveObserver(this); 394 shelf_observer_.reset(); 395 } 396 launcher_ = NULL; 397 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 398 aura::Window* child = dock_container_->children()[i]; 399 child->RemoveObserver(this); 400 wm::GetWindowState(child)->RemoveObserver(this); 401 } 402 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())-> 403 RemoveObserver(this); 404 Shell::GetInstance()->RemoveShellObserver(this); 405 } 406 407 void DockedWindowLayoutManager::AddObserver( 408 DockedWindowLayoutManagerObserver* observer) { 409 observer_list_.AddObserver(observer); 410 } 411 412 void DockedWindowLayoutManager::RemoveObserver( 413 DockedWindowLayoutManagerObserver* observer) { 414 observer_list_.RemoveObserver(observer); 415 } 416 417 void DockedWindowLayoutManager::StartDragging(aura::Window* window) { 418 DCHECK(!dragged_window_); 419 dragged_window_ = window; 420 DCHECK(!IsPopupOrTransient(window)); 421 // Start observing a window unless it is docked container's child in which 422 // case it is already observed. 423 if (dragged_window_->parent() != dock_container_) { 424 dragged_window_->AddObserver(this); 425 wm::GetWindowState(dragged_window_)->AddObserver(this); 426 } 427 is_dragged_from_dock_ = window->parent() == dock_container_; 428 DCHECK(!is_dragged_window_docked_); 429 } 430 431 void DockedWindowLayoutManager::DockDraggedWindow(aura::Window* window) { 432 DCHECK(!IsPopupOrTransient(window)); 433 OnDraggedWindowDocked(window); 434 Relayout(); 435 } 436 437 void DockedWindowLayoutManager::UndockDraggedWindow() { 438 DCHECK(!IsPopupOrTransient(dragged_window_)); 439 OnDraggedWindowUndocked(); 440 Relayout(); 441 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 442 is_dragged_from_dock_ = false; 443 } 444 445 void DockedWindowLayoutManager::FinishDragging(DockedAction action, 446 DockedActionSource source) { 447 DCHECK(dragged_window_); 448 DCHECK(!IsPopupOrTransient(dragged_window_)); 449 if (is_dragged_window_docked_) 450 OnDraggedWindowUndocked(); 451 DCHECK (!is_dragged_window_docked_); 452 // Stop observing a window unless it is docked container's child in which 453 // case it needs to keep being observed after the drag completes. 454 if (dragged_window_->parent() != dock_container_) { 455 dragged_window_->RemoveObserver(this); 456 wm::GetWindowState(dragged_window_)->RemoveObserver(this); 457 if (last_active_window_ == dragged_window_) 458 last_active_window_ = NULL; 459 } else { 460 // A window is no longer dragged and is a child. 461 // When a window becomes a child at drag start this is 462 // the only opportunity we will have to enforce a window 463 // count limit so do it here. 464 MaybeMinimizeChildrenExcept(dragged_window_); 465 } 466 dragged_window_ = NULL; 467 dragged_bounds_ = gfx::Rect(); 468 Relayout(); 469 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 470 RecordUmaAction(action, source); 471 } 472 473 void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) { 474 DCHECK(!launcher_); 475 launcher_ = launcher; 476 if (launcher_->shelf_widget()) { 477 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher( 478 launcher_->shelf_widget()->GetNativeWindow()); 479 shelf_layout_manager->AddObserver(this); 480 shelf_observer_.reset(new ShelfWindowObserver(this)); 481 } 482 } 483 484 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow( 485 const aura::Window* window) const { 486 const gfx::Rect& bounds(window->GetBoundsInScreen()); 487 488 // Test overlap with an existing docked area first. 489 if (docked_bounds_.Intersects(bounds) && 490 alignment_ != DOCKED_ALIGNMENT_NONE) { 491 // A window is being added to other docked windows (on the same side). 492 return alignment_; 493 } 494 495 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen(); 496 if (bounds.x() <= container_bounds.x() && 497 bounds.right() > container_bounds.x()) { 498 return DOCKED_ALIGNMENT_LEFT; 499 } else if (bounds.x() < container_bounds.right() && 500 bounds.right() >= container_bounds.right()) { 501 return DOCKED_ALIGNMENT_RIGHT; 502 } 503 return DOCKED_ALIGNMENT_NONE; 504 } 505 506 DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const { 507 // Find a child that is not being dragged and is not a popup. 508 // If such exists the current alignment is returned - even if some of the 509 // children are hidden or minimized (so they can be restored without losing 510 // the docked state). 511 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 512 aura::Window* window(dock_container_->children()[i]); 513 if (window != dragged_window_ && !IsPopupOrTransient(window)) 514 return alignment_; 515 } 516 // No docked windows remain other than possibly the window being dragged. 517 // Return |NONE| to indicate that windows may get docked on either side. 518 return DOCKED_ALIGNMENT_NONE; 519 } 520 521 bool DockedWindowLayoutManager::CanDockWindow(aura::Window* window, 522 SnapType edge) { 523 if (!switches::UseDockedWindows()) 524 return false; 525 // Don't allow interactive docking of windows with transient parents such as 526 // modal browser dialogs. 527 if (IsPopupOrTransient(window)) 528 return false; 529 // If a window is wide and cannot be resized down to maximum width allowed 530 // then it cannot be docked. 531 // TODO(varkha). Prevent windows from changing size programmatically while 532 // they are docked. The size will take effect only once a window is undocked. 533 // See http://crbug.com/307792. 534 if (window->bounds().width() > kMaxDockWidth && 535 (!wm::GetWindowState(window)->CanResize() || 536 (window->delegate() && 537 window->delegate()->GetMinimumSize().width() != 0 && 538 window->delegate()->GetMinimumSize().width() > kMaxDockWidth))) { 539 return false; 540 } 541 // If a window is tall and cannot be resized down to maximum height allowed 542 // then it cannot be docked. 543 const gfx::Rect work_area = 544 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); 545 if (GetWindowHeightCloseTo(window, work_area.height() - 2 * kMinDockGap) > 546 work_area.height() - 2 * kMinDockGap) { 547 return false; 548 } 549 // Cannot dock on the other size from an existing dock. 550 const DockedAlignment alignment = CalculateAlignment(); 551 if ((edge == SNAP_LEFT && alignment == DOCKED_ALIGNMENT_RIGHT) || 552 (edge == SNAP_RIGHT && alignment == DOCKED_ALIGNMENT_LEFT)) { 553 return false; 554 } 555 // Do not allow docking on the same side as launcher shelf. 556 ShelfAlignment shelf_alignment = SHELF_ALIGNMENT_BOTTOM; 557 if (launcher_) 558 shelf_alignment = launcher_->alignment(); 559 if ((edge == SNAP_LEFT && shelf_alignment == SHELF_ALIGNMENT_LEFT) || 560 (edge == SNAP_RIGHT && shelf_alignment == SHELF_ALIGNMENT_RIGHT)) { 561 return false; 562 } 563 return true; 564 } 565 566 void DockedWindowLayoutManager::OnShelfBoundsChanged() { 567 Relayout(); 568 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED); 569 } 570 571 //////////////////////////////////////////////////////////////////////////////// 572 // DockedWindowLayoutManager, aura::LayoutManager implementation: 573 void DockedWindowLayoutManager::OnWindowResized() { 574 MaybeMinimizeChildrenExcept(dragged_window_); 575 Relayout(); 576 // When screen resizes update the insets even when dock width or alignment 577 // does not change. 578 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED); 579 } 580 581 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) { 582 if (IsPopupOrTransient(child)) 583 return; 584 // Dragged windows are already observed by StartDragging and do not change 585 // docked alignment during the drag. 586 if (child == dragged_window_) 587 return; 588 // If this is the first window getting docked - update alignment. 589 if (alignment_ == DOCKED_ALIGNMENT_NONE) { 590 alignment_ = GetAlignmentOfWindow(child); 591 DCHECK(alignment_ != DOCKED_ALIGNMENT_NONE); 592 } 593 MaybeMinimizeChildrenExcept(child); 594 child->AddObserver(this); 595 wm::GetWindowState(child)->AddObserver(this); 596 Relayout(); 597 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 598 } 599 600 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) { 601 if (IsPopupOrTransient(child)) 602 return; 603 // Dragged windows are stopped being observed by FinishDragging and do not 604 // change alignment during the drag. They also cannot be set to be the 605 // |last_active_window_|. 606 if (child == dragged_window_) 607 return; 608 // If this is the last window, set alignment and maximize the workspace. 609 if (!IsAnyWindowDocked()) { 610 alignment_ = DOCKED_ALIGNMENT_NONE; 611 UpdateDockedWidth(0); 612 } 613 if (last_active_window_ == child) 614 last_active_window_ = NULL; 615 child->RemoveObserver(this); 616 wm::GetWindowState(child)->RemoveObserver(this); 617 Relayout(); 618 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 619 } 620 621 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged( 622 aura::Window* child, 623 bool visible) { 624 if (IsPopupOrTransient(child)) 625 return; 626 if (visible) 627 wm::GetWindowState(child)->Restore(); 628 Relayout(); 629 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 630 } 631 632 void DockedWindowLayoutManager::SetChildBounds( 633 aura::Window* child, 634 const gfx::Rect& requested_bounds) { 635 // Whenever one of our windows is moved or resized enforce layout. 636 SetChildBoundsDirect(child, requested_bounds); 637 if (IsPopupOrTransient(child)) 638 return; 639 ShelfLayoutManager* shelf_layout = internal::ShelfLayoutManager::ForLauncher( 640 dock_container_); 641 if (shelf_layout) 642 shelf_layout->UpdateVisibilityState(); 643 } 644 645 //////////////////////////////////////////////////////////////////////////////// 646 // DockedWindowLayoutManager, ash::ShellObserver implementation: 647 648 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() { 649 Relayout(); 650 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED); 651 MaybeMinimizeChildrenExcept(dragged_window_); 652 } 653 654 void DockedWindowLayoutManager::OnFullscreenStateChanged( 655 bool is_fullscreen, aura::Window* root_window) { 656 if (dock_container_->GetRootWindow() != root_window) 657 return; 658 // Entering fullscreen mode (including immersive) hides docked windows. 659 in_fullscreen_ = workspace_controller_->GetWindowState() == 660 WORKSPACE_WINDOW_STATE_FULL_SCREEN; 661 { 662 // prevent Relayout from getting called multiple times during this 663 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); 664 // Use a copy of children array because a call to MinimizeDockedWindow or 665 // RestoreDockedWindow can change order. 666 aura::Window::Windows children(dock_container_->children()); 667 for (aura::Window::Windows::const_iterator iter = children.begin(); 668 iter != children.end(); ++iter) { 669 aura::Window* window(*iter); 670 if (IsPopupOrTransient(window)) 671 continue; 672 wm::WindowState* window_state = wm::GetWindowState(window); 673 if (in_fullscreen_) { 674 if (window->IsVisible()) 675 MinimizeDockedWindow(window_state); 676 } else { 677 if (!window_state->IsMinimized()) 678 RestoreDockedWindow(window_state); 679 } 680 } 681 } 682 Relayout(); 683 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED); 684 } 685 686 void DockedWindowLayoutManager::OnShelfAlignmentChanged( 687 aura::Window* root_window) { 688 if (dock_container_->GetRootWindow() != root_window) 689 return; 690 691 if (!launcher_ || !launcher_->shelf_widget()) 692 return; 693 694 if (alignment_ == DOCKED_ALIGNMENT_NONE) 695 return; 696 697 // Do not allow launcher and dock on the same side. Switch side that 698 // the dock is attached to and move all dock windows to that new side. 699 ShelfAlignment shelf_alignment = launcher_->shelf_widget()->GetAlignment(); 700 if (alignment_ == DOCKED_ALIGNMENT_LEFT && 701 shelf_alignment == SHELF_ALIGNMENT_LEFT) { 702 alignment_ = DOCKED_ALIGNMENT_RIGHT; 703 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT && 704 shelf_alignment == SHELF_ALIGNMENT_RIGHT) { 705 alignment_ = DOCKED_ALIGNMENT_LEFT; 706 } 707 Relayout(); 708 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED); 709 } 710 711 ///////////////////////////////////////////////////////////////////////////// 712 // DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation: 713 void DockedWindowLayoutManager::OnBackgroundUpdated( 714 ShelfBackgroundType background_type, 715 BackgroundAnimatorChangeType change_type) { 716 background_widget_->SetPaintsBackground(background_type, change_type); 717 } 718 719 ///////////////////////////////////////////////////////////////////////////// 720 // DockedWindowLayoutManager, WindowStateObserver implementation: 721 722 void DockedWindowLayoutManager::OnWindowShowTypeChanged( 723 wm::WindowState* window_state, 724 wm::WindowShowType old_type) { 725 aura::Window* window = window_state->window(); 726 if (IsPopupOrTransient(window)) 727 return; 728 // The window property will still be set, but no actual change will occur 729 // until OnFullscreenStateChange is called when exiting fullscreen. 730 if (in_fullscreen_) 731 return; 732 if (window_state->IsMinimized()) { 733 MinimizeDockedWindow(window_state); 734 } else if (window_state->IsMaximizedOrFullscreen() || 735 window_state->IsSnapped()) { 736 if (window != dragged_window_) { 737 UndockWindow(window); 738 RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN); 739 } 740 } else if (old_type == wm::SHOW_TYPE_MINIMIZED) { 741 RestoreDockedWindow(window_state); 742 } 743 } 744 745 ///////////////////////////////////////////////////////////////////////////// 746 // DockedWindowLayoutManager, WindowObserver implementation: 747 748 void DockedWindowLayoutManager::OnWindowBoundsChanged( 749 aura::Window* window, 750 const gfx::Rect& old_bounds, 751 const gfx::Rect& new_bounds) { 752 // Only relayout if the dragged window would get docked. 753 if (window == dragged_window_ && is_dragged_window_docked_) 754 Relayout(); 755 } 756 757 void DockedWindowLayoutManager::OnWindowVisibilityChanging( 758 aura::Window* window, bool visible) { 759 if (IsPopupOrTransient(window)) 760 return; 761 int animation_type = views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT; 762 if (visible) { 763 animation_type = views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP; 764 views::corewm::SetWindowVisibilityAnimationDuration( 765 window, base::TimeDelta::FromMilliseconds(kFadeDurationMs)); 766 } else if (wm::GetWindowState(window)->IsMinimized()) { 767 animation_type = WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE; 768 } 769 views::corewm::SetWindowVisibilityAnimationType(window, animation_type); 770 } 771 772 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) { 773 if (dragged_window_ == window) { 774 FinishDragging(DOCKED_ACTION_NONE, DOCKED_ACTION_SOURCE_UNKNOWN); 775 DCHECK(!dragged_window_); 776 DCHECK (!is_dragged_window_docked_); 777 } 778 if (window == last_active_window_) 779 last_active_window_ = NULL; 780 RecordUmaAction(DOCKED_ACTION_CLOSE, DOCKED_ACTION_SOURCE_UNKNOWN); 781 } 782 783 784 //////////////////////////////////////////////////////////////////////////////// 785 // DockedWindowLayoutManager, aura::client::ActivationChangeObserver 786 // implementation: 787 788 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active, 789 aura::Window* lost_active) { 790 if (gained_active && IsPopupOrTransient(gained_active)) 791 return; 792 // Ignore if the window that is not managed by this was activated. 793 aura::Window* ancestor = NULL; 794 for (aura::Window* parent = gained_active; 795 parent; parent = parent->parent()) { 796 if (parent->parent() == dock_container_) { 797 ancestor = parent; 798 break; 799 } 800 } 801 if (ancestor) 802 UpdateStacking(ancestor); 803 } 804 805 //////////////////////////////////////////////////////////////////////////////// 806 // DockedWindowLayoutManager private implementation: 807 808 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept( 809 aura::Window* child) { 810 // Minimize any windows that don't fit without overlap. 811 const gfx::Rect work_area = 812 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); 813 int available_room = work_area.height() - kMinDockGap; 814 if (child) 815 available_room -= (GetWindowHeightCloseTo(child, 0) + kMinDockGap); 816 // Use a copy of children array because a call to Minimize can change order. 817 aura::Window::Windows children(dock_container_->children()); 818 aura::Window::Windows::const_reverse_iterator iter = children.rbegin(); 819 while (iter != children.rend()) { 820 aura::Window* window(*iter++); 821 if (window == child || !IsUsedByLayout(window)) 822 continue; 823 int room_needed = GetWindowHeightCloseTo(window, 0) + kMinDockGap; 824 if (available_room > room_needed) { 825 available_room -= room_needed; 826 } else { 827 // Slow down minimizing animations. Lock duration so that it is not 828 // overridden by other ScopedLayerAnimationSettings down the stack. 829 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); 830 settings.SetTransitionDuration( 831 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs)); 832 settings.LockTransitionDuration(); 833 wm::GetWindowState(window)->Minimize(); 834 } 835 } 836 } 837 838 void DockedWindowLayoutManager::MinimizeDockedWindow( 839 wm::WindowState* window_state) { 840 DCHECK(!IsPopupOrTransient(window_state->window())); 841 window_state->window()->Hide(); 842 if (window_state->IsActive()) 843 window_state->Deactivate(); 844 RecordUmaAction(DOCKED_ACTION_MINIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN); 845 } 846 847 void DockedWindowLayoutManager::RestoreDockedWindow( 848 wm::WindowState* window_state) { 849 aura::Window* window = window_state->window(); 850 DCHECK(!IsPopupOrTransient(window)); 851 // Always place restored window at the bottom shuffling the other windows up. 852 // TODO(varkha): add a separate container for docked windows to keep track 853 // of ordering. 854 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow( 855 dock_container_); 856 const gfx::Rect work_area = display.work_area(); 857 858 // Evict the window if it can no longer be docked because of its height. 859 if (!CanDockWindow(window, SNAP_NONE)) { 860 UndockWindow(window); 861 RecordUmaAction(DOCKED_ACTION_EVICT, DOCKED_ACTION_SOURCE_UNKNOWN); 862 return; 863 } 864 gfx::Rect bounds(window->bounds()); 865 bounds.set_y(work_area.bottom()); 866 window->SetBounds(bounds); 867 window->Show(); 868 MaybeMinimizeChildrenExcept(window); 869 RecordUmaAction(DOCKED_ACTION_RESTORE, DOCKED_ACTION_SOURCE_UNKNOWN); 870 } 871 872 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action, 873 DockedActionSource source) { 874 if (action == DOCKED_ACTION_NONE) 875 return; 876 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action, DOCKED_ACTION_COUNT); 877 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source, 878 DOCKED_ACTION_SOURCE_COUNT); 879 base::Time time_now = base::Time::Now(); 880 base::TimeDelta time_between_use = time_now - last_action_time_; 881 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse", 882 time_between_use.InSeconds(), 883 1, 884 base::TimeDelta::FromHours(10).InSeconds(), 885 100); 886 last_action_time_ = time_now; 887 int docked_all_count = 0; 888 int docked_visible_count = 0; 889 int docked_panels_count = 0; 890 int large_windows_count = 0; 891 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 892 const aura::Window* window(dock_container_->children()[i]); 893 if (IsPopupOrTransient(window)) 894 continue; 895 docked_all_count++; 896 if (!IsUsedByLayout(window)) 897 continue; 898 docked_visible_count++; 899 if (window->type() == aura::client::WINDOW_TYPE_PANEL) 900 docked_panels_count++; 901 const wm::WindowState* window_state = wm::GetWindowState(window); 902 if (window_state->HasRestoreBounds()) { 903 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen(); 904 if (restore_bounds.width() > kMaxDockWidth) 905 large_windows_count++; 906 } 907 } 908 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count); 909 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count); 910 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count); 911 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count); 912 } 913 914 void DockedWindowLayoutManager::UpdateDockedWidth(int width) { 915 if (docked_width_ == width) 916 return; 917 docked_width_ = width; 918 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_); 919 } 920 921 void DockedWindowLayoutManager::OnDraggedWindowDocked(aura::Window* window) { 922 DCHECK(!is_dragged_window_docked_); 923 is_dragged_window_docked_ = true; 924 925 // If there are no other docked windows update alignment. 926 if (!IsAnyWindowDocked()) 927 alignment_ = DOCKED_ALIGNMENT_NONE; 928 } 929 930 void DockedWindowLayoutManager::OnDraggedWindowUndocked() { 931 // If this is the first window getting docked - update alignment. 932 if (!IsAnyWindowDocked()) 933 alignment_ = GetAlignmentOfWindow(dragged_window_); 934 935 DCHECK (is_dragged_window_docked_); 936 is_dragged_window_docked_ = false; 937 } 938 939 bool DockedWindowLayoutManager::IsAnyWindowDocked() { 940 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE; 941 } 942 943 void DockedWindowLayoutManager::Relayout() { 944 if (in_layout_) 945 return; 946 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_) 947 return; 948 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true); 949 950 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen(); 951 aura::Window* active_window = NULL; 952 std::vector<WindowWithHeight> visible_windows; 953 for (size_t i = 0; i < dock_container_->children().size(); ++i) { 954 aura::Window* window(dock_container_->children()[i]); 955 956 if (!IsUsedByLayout(window) || window == dragged_window_) 957 continue; 958 959 // If the shelf is currently hidden (full-screen mode), hide window until 960 // full-screen mode is exited. 961 if (in_fullscreen_) { 962 // The call to Hide does not set the minimize property, so the window will 963 // be restored when the shelf becomes visible again. 964 window->Hide(); 965 continue; 966 } 967 if (window->HasFocus() || 968 window->Contains( 969 aura::client::GetFocusClient(window)->GetFocusedWindow())) { 970 DCHECK(!active_window); 971 active_window = window; 972 } 973 visible_windows.push_back(WindowWithHeight(window)); 974 } 975 // Consider docked dragged_window_ when fanning out other child windows. 976 if (is_dragged_window_docked_) { 977 visible_windows.push_back(WindowWithHeight(dragged_window_)); 978 DCHECK(!active_window); 979 active_window = dragged_window_; 980 } 981 982 // Position docked windows as well as the window being dragged. 983 gfx::Rect work_area = 984 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); 985 if (shelf_observer_) 986 work_area.Subtract(shelf_observer_->shelf_bounds_in_screen()); 987 int available_room = CalculateWindowHeightsAndRemainingRoom(work_area, 988 &visible_windows); 989 FanOutChildren(work_area, 990 CalculateIdealWidth(visible_windows), 991 available_room, 992 &visible_windows); 993 994 // After the first Relayout allow the windows to change their order easier 995 // since we know they are docked. 996 is_dragged_from_dock_ = true; 997 UpdateStacking(active_window); 998 } 999 1000 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom( 1001 const gfx::Rect work_area, 1002 std::vector<WindowWithHeight>* visible_windows) { 1003 int available_room = work_area.height() - kMinDockGap; 1004 int remaining_windows = visible_windows->size(); 1005 1006 // Sort windows by their minimum heights and calculate target heights. 1007 std::sort(visible_windows->begin(), visible_windows->end(), 1008 CompareMinimumHeight()); 1009 // Distribute the free space among the docked windows. Since the windows are 1010 // sorted (tall windows first) we can now assume that any window which 1011 // required more space than the current window will have already been 1012 // accounted for previously in this loop, so we can safely give that window 1013 // its proportional share of the remaining space. 1014 for (std::vector<WindowWithHeight>::reverse_iterator iter = 1015 visible_windows->rbegin(); 1016 iter != visible_windows->rend(); ++iter) { 1017 iter->height_ = GetWindowHeightCloseTo( 1018 iter->window(), available_room / remaining_windows - kMinDockGap); 1019 available_room -= (iter->height_ + kMinDockGap); 1020 remaining_windows--; 1021 } 1022 return available_room; 1023 } 1024 1025 int DockedWindowLayoutManager::CalculateIdealWidth( 1026 const std::vector<WindowWithHeight>& visible_windows) { 1027 int smallest_max_width = kMaxDockWidth; 1028 int largest_min_width = kMinDockWidth; 1029 // Ideal width of the docked area is as close to kIdealWidth as possible 1030 // while still respecting the minimum and maximum width restrictions on the 1031 // individual docked windows as well as the width that was possibly set by a 1032 // user (which needs to be preserved when dragging and rearranging windows). 1033 for (std::vector<WindowWithHeight>::const_iterator iter = 1034 visible_windows.begin(); 1035 iter != visible_windows.end(); ++iter) { 1036 const aura::Window* window = iter->window(); 1037 int min_window_width = window->bounds().width(); 1038 int max_window_width = min_window_width; 1039 if (!wm::GetWindowState(window)->bounds_changed_by_user()) { 1040 min_window_width = GetWindowWidthCloseTo(window, kMinDockWidth); 1041 max_window_width = GetWindowWidthCloseTo(window, kMaxDockWidth); 1042 } 1043 largest_min_width = std::max(largest_min_width, min_window_width); 1044 smallest_max_width = std::min(smallest_max_width, max_window_width); 1045 } 1046 int ideal_width = std::max(largest_min_width, 1047 std::min(smallest_max_width, kIdealWidth)); 1048 // Restrict docked area width regardless of window restrictions. 1049 ideal_width = std::max(std::min(ideal_width, kMaxDockWidth), kMinDockWidth); 1050 return ideal_width; 1051 } 1052 1053 void DockedWindowLayoutManager::FanOutChildren( 1054 const gfx::Rect& work_area, 1055 int ideal_docked_width, 1056 int available_room, 1057 std::vector<WindowWithHeight>* visible_windows) { 1058 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen(); 1059 1060 // Calculate initial vertical offset and the gap or overlap between windows. 1061 const int num_windows = visible_windows->size(); 1062 const float delta = kMinDockGap + (float)available_room / 1063 ((available_room > 0 || num_windows <= 1) ? 1064 num_windows + 1 : num_windows - 1); 1065 float y_pos = work_area.y() + ((delta > 0) ? delta : kMinDockGap); 1066 1067 // Docked area is shown only if there is at least one non-dragged visible 1068 // docked window. 1069 int new_width = ideal_docked_width; 1070 if (visible_windows->empty() || 1071 (visible_windows->size() == 1 && 1072 (*visible_windows)[0].window() == dragged_window_)) { 1073 new_width = 0; 1074 } 1075 UpdateDockedWidth(new_width); 1076 // Sort windows by their center positions and fan out overlapping 1077 // windows. 1078 std::sort(visible_windows->begin(), visible_windows->end(), 1079 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : NULL, 1080 delta)); 1081 for (std::vector<WindowWithHeight>::iterator iter = visible_windows->begin(); 1082 iter != visible_windows->end(); ++iter) { 1083 aura::Window* window = iter->window(); 1084 gfx::Rect bounds = ScreenAsh::ConvertRectToScreen( 1085 window->parent(), window->GetTargetBounds()); 1086 // A window is extended or shrunk to be as close as possible to the ideal 1087 // docked area width. Windows that were resized by a user are kept at their 1088 // existing size. 1089 // This also enforces the min / max restrictions on the docked area width. 1090 bounds.set_width(GetWindowWidthCloseTo( 1091 window, 1092 wm::GetWindowState(window)->bounds_changed_by_user() ? 1093 bounds.width() : ideal_docked_width)); 1094 DCHECK_LE(bounds.width(), ideal_docked_width); 1095 1096 DockedAlignment alignment = alignment_; 1097 if (alignment == DOCKED_ALIGNMENT_NONE && window == dragged_window_) { 1098 alignment = GetAlignmentOfWindow(window); 1099 if (alignment == DOCKED_ALIGNMENT_NONE) 1100 bounds.set_size(gfx::Size()); 1101 } 1102 1103 // Fan out windows evenly distributing the overlap or remaining free space. 1104 bounds.set_height(iter->height_); 1105 bounds.set_y(std::max(work_area.y(), 1106 std::min(work_area.bottom() - bounds.height(), 1107 static_cast<int>(y_pos + 0.5)))); 1108 y_pos += bounds.height() + delta; 1109 1110 // All docked windows other than the one currently dragged remain stuck 1111 // to the screen edge (flush with the edge or centered in the dock area). 1112 switch (alignment) { 1113 case DOCKED_ALIGNMENT_LEFT: 1114 bounds.set_x(dock_bounds.x() + 1115 (ideal_docked_width - bounds.width()) / 2); 1116 break; 1117 case DOCKED_ALIGNMENT_RIGHT: 1118 bounds.set_x(dock_bounds.right() - 1119 (ideal_docked_width + bounds.width()) / 2); 1120 break; 1121 case DOCKED_ALIGNMENT_NONE: 1122 break; 1123 } 1124 if (window == dragged_window_) { 1125 dragged_bounds_ = bounds; 1126 continue; 1127 } 1128 // If the following asserts it is probably because not all the children 1129 // have been removed when dock was closed. 1130 DCHECK_NE(alignment_, DOCKED_ALIGNMENT_NONE); 1131 bounds = ScreenAsh::ConvertRectFromScreen(dock_container_, bounds); 1132 if (bounds != window->GetTargetBounds()) { 1133 ui::Layer* layer = window->layer(); 1134 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator()); 1135 slide_settings.SetPreemptionStrategy( 1136 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 1137 slide_settings.SetTransitionDuration( 1138 base::TimeDelta::FromMilliseconds(kSlideDurationMs)); 1139 SetChildBoundsDirect(window, bounds); 1140 } 1141 } 1142 } 1143 1144 void DockedWindowLayoutManager::UpdateDockBounds( 1145 DockedWindowLayoutManagerObserver::Reason reason) { 1146 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0); 1147 const gfx::Rect work_area = 1148 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area(); 1149 gfx::Rect bounds = gfx::Rect( 1150 alignment_ == DOCKED_ALIGNMENT_RIGHT && dock_inset > 0 ? 1151 dock_container_->bounds().right() - dock_inset: 1152 dock_container_->bounds().x(), 1153 dock_container_->bounds().y(), 1154 dock_inset, 1155 work_area.height()); 1156 docked_bounds_ = bounds + 1157 dock_container_->GetBoundsInScreen().OffsetFromOrigin(); 1158 FOR_EACH_OBSERVER( 1159 DockedWindowLayoutManagerObserver, 1160 observer_list_, 1161 OnDockBoundsChanging(bounds, reason)); 1162 // Show or hide background for docked area. 1163 gfx::Rect background_bounds(docked_bounds_); 1164 if (shelf_observer_) 1165 background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen()); 1166 background_widget_->SetBackgroundBounds(background_bounds, alignment_); 1167 if (docked_width_ > 0) 1168 background_widget_->Show(); 1169 else 1170 background_widget_->Hide(); 1171 } 1172 1173 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) { 1174 if (!active_window) { 1175 if (!last_active_window_) 1176 return; 1177 active_window = last_active_window_; 1178 } 1179 1180 // Windows are stacked like a deck of cards: 1181 // ,------. 1182 // |,------.| 1183 // |,------.| 1184 // | active | 1185 // | window | 1186 // |`------'| 1187 // |`------'| 1188 // `------' 1189 // Use the middle of each window to figure out how to stack the window. 1190 // This allows us to update the stacking when a window is being dragged around 1191 // by the titlebar. 1192 std::map<int, aura::Window*> window_ordering; 1193 for (aura::Window::Windows::const_iterator it = 1194 dock_container_->children().begin(); 1195 it != dock_container_->children().end(); ++it) { 1196 if (!IsUsedByLayout(*it) || 1197 ((*it) == dragged_window_ && !is_dragged_window_docked_)) { 1198 continue; 1199 } 1200 gfx::Rect bounds = (*it)->bounds(); 1201 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2, 1202 *it)); 1203 } 1204 int active_center_y = active_window->bounds().CenterPoint().y(); 1205 1206 aura::Window* previous_window = NULL; 1207 for (std::map<int, aura::Window*>::const_iterator it = 1208 window_ordering.begin(); 1209 it != window_ordering.end() && it->first < active_center_y; ++it) { 1210 if (previous_window) 1211 dock_container_->StackChildAbove(it->second, previous_window); 1212 previous_window = it->second; 1213 } 1214 for (std::map<int, aura::Window*>::const_reverse_iterator it = 1215 window_ordering.rbegin(); 1216 it != window_ordering.rend() && it->first > active_center_y; ++it) { 1217 if (previous_window) 1218 dock_container_->StackChildAbove(it->second, previous_window); 1219 previous_window = it->second; 1220 } 1221 1222 if (previous_window && active_window->parent() == dock_container_) 1223 dock_container_->StackChildAbove(active_window, previous_window); 1224 if (active_window != dragged_window_) 1225 last_active_window_ = active_window; 1226 } 1227 1228 //////////////////////////////////////////////////////////////////////////////// 1229 // keyboard::KeyboardControllerObserver implementation: 1230 1231 void DockedWindowLayoutManager::OnKeyboardBoundsChanging( 1232 const gfx::Rect& keyboard_bounds) { 1233 // This bounds change will have caused a change to the Shelf which does not 1234 // propagate automatically to this class, so manually recalculate bounds. 1235 Relayout(); 1236 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING); 1237 } 1238 1239 } // namespace internal 1240 } // namespace ash 1241