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