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