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