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