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