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