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