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