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
40namespace ash {
41namespace internal {
42
43// Minimum, maximum width of the dock area and a width of the gap
44// static
45const int DockedWindowLayoutManager::kMaxDockWidth = 360;
46// static
47const int DockedWindowLayoutManager::kMinDockWidth = 200;
48// static
49const int DockedWindowLayoutManager::kMinDockGap = 2;
50// static
51const int DockedWindowLayoutManager::kIdealWidth = 250;
52const int kMinimumHeight = 250;
53const int kSlideDurationMs = 120;
54const int kFadeDurationMs = 60;
55const int kMinimizeDurationMs = 720;
56
57class 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
179namespace {
180
181// Returns true if a window is a popup or a transient child.
182bool 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.
188bool IsUsedByLayout(const aura::Window* window) {
189  return (window->IsVisible() &&
190          !wm::GetWindowState(window)->IsMinimized() &&
191          !IsPopupOrTransient(window));
192}
193
194void 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.
208int 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.
229int 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.
251struct 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.
263struct 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.
324class 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:
363DockedWindowLayoutManager::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
385DockedWindowLayoutManager::~DockedWindowLayoutManager() {
386  Shutdown();
387}
388
389void 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
407void DockedWindowLayoutManager::AddObserver(
408    DockedWindowLayoutManagerObserver* observer) {
409  observer_list_.AddObserver(observer);
410}
411
412void DockedWindowLayoutManager::RemoveObserver(
413    DockedWindowLayoutManagerObserver* observer) {
414  observer_list_.RemoveObserver(observer);
415}
416
417void 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
431void DockedWindowLayoutManager::DockDraggedWindow(aura::Window* window) {
432  DCHECK(!IsPopupOrTransient(window));
433  OnDraggedWindowDocked(window);
434  Relayout();
435}
436
437void DockedWindowLayoutManager::UndockDraggedWindow() {
438  DCHECK(!IsPopupOrTransient(dragged_window_));
439  OnDraggedWindowUndocked();
440  Relayout();
441  UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
442  is_dragged_from_dock_ = false;
443}
444
445void 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
473void 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
484DockedAlignment 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
506DockedAlignment 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
521bool 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
566void DockedWindowLayoutManager::OnShelfBoundsChanged() {
567  Relayout();
568  UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
569}
570
571////////////////////////////////////////////////////////////////////////////////
572// DockedWindowLayoutManager, aura::LayoutManager implementation:
573void 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
581void 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
600void 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
621void 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
632void 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
648void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
649  Relayout();
650  UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
651  MaybeMinimizeChildrenExcept(dragged_window_);
652}
653
654void 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
686void 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:
713void 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
722void 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
748void 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
757void 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
772void 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
788void 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
808void 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
838void 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
847void 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
872void 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
914void 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
921void 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
930void 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
939bool DockedWindowLayoutManager::IsAnyWindowDocked() {
940  return CalculateAlignment() != DOCKED_ALIGNMENT_NONE;
941}
942
943void 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
1000int 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
1025int 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
1053void 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
1144void 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
1173void 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
1231void 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