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/paint_recorder.h"
35 #include "ui/compositor/scoped_layer_animation_settings.h"
36 #include "ui/gfx/canvas.h"
37 #include "ui/gfx/geometry/rect.h"
38 #include "ui/gfx/image/image_skia_operations.h"
39 #include "ui/views/background.h"
40 #include "ui/wm/core/window_util.h"
41 #include "ui/wm/public/activation_client.h"
45 // Minimum, maximum width of the dock area and a width of the gap
47 const int DockedWindowLayoutManager::kMaxDockWidth
= 360;
49 const int DockedWindowLayoutManager::kMinDockWidth
= 200;
51 const int DockedWindowLayoutManager::kMinDockGap
= 2;
53 const int DockedWindowLayoutManager::kIdealWidth
= 250;
54 const int kMinimumHeight
= 250;
55 const int kSlideDurationMs
= 120;
56 const int kFadeDurationMs
= 60;
57 const int kMinimizeDurationMs
= 720;
59 class DockedBackgroundWidget
: public views::Widget
,
60 public BackgroundAnimatorDelegate
{
62 explicit DockedBackgroundWidget(aura::Window
* container
)
63 : alignment_(DOCKED_ALIGNMENT_NONE
),
64 background_animator_(this, 0, kShelfBackgroundAlpha
),
66 opaque_background_(ui::LAYER_SOLID_COLOR
),
67 visible_background_type_(SHELF_BACKGROUND_DEFAULT
),
68 visible_background_change_type_(BACKGROUND_CHANGE_IMMEDIATE
) {
69 InitWidget(container
);
72 // Sets widget bounds and sizes opaque background layer to fill the widget.
73 void SetBackgroundBounds(const gfx::Rect bounds
, DockedAlignment alignment
) {
75 opaque_background_
.SetBounds(gfx::Rect(bounds
.size()));
76 alignment_
= alignment
;
79 // Sets the background type. Starts an animation to transition to
80 // |background_type| if the widget is visible. If the widget is not visible,
81 // the animation is postponed till the widget becomes visible.
82 void SetBackgroundType(ShelfBackgroundType background_type
,
83 BackgroundAnimatorChangeType change_type
) {
84 visible_background_type_
= background_type
;
85 visible_background_change_type_
= change_type
;
91 void OnNativeWidgetVisibilityChanged(bool visible
) override
{
92 views::Widget::OnNativeWidgetVisibilityChanged(visible
);
96 void OnNativeWidgetPaint(const ui::PaintContext
& context
) override
{
97 ui::PaintRecorder
recorder(context
);
98 const gfx::ImageSkia
& shelf_background(
99 alignment_
== DOCKED_ALIGNMENT_LEFT
?
100 shelf_background_left_
: shelf_background_right_
);
101 gfx::Rect rect
= gfx::Rect(GetWindowBoundsInScreen().size());
103 paint
.setAlpha(alpha_
);
104 recorder
.canvas()->DrawImageInt(
105 shelf_background
, 0, 0, shelf_background
.width(),
106 shelf_background
.height(), alignment_
== DOCKED_ALIGNMENT_LEFT
107 ? rect
.width() - shelf_background
.width()
109 0, shelf_background
.width(), rect
.height(), false, paint
);
110 recorder
.canvas()->DrawImageInt(
112 alignment_
== DOCKED_ALIGNMENT_LEFT
? 0 : shelf_background
.width() - 1,
113 0, 1, shelf_background
.height(),
114 alignment_
== DOCKED_ALIGNMENT_LEFT
? 0 : shelf_background
.width(), 0,
115 rect
.width() - shelf_background
.width(), rect
.height(), false, paint
);
118 // BackgroundAnimatorDelegate:
119 void UpdateBackground(int alpha
) override
{
121 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
125 void InitWidget(aura::Window
* parent
) {
126 views::Widget::InitParams params
;
127 params
.type
= views::Widget::InitParams::TYPE_POPUP
;
128 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
129 params
.keep_on_top
= false;
130 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
131 params
.parent
= parent
;
132 params
.accept_events
= false;
133 set_focus_on_creation(false);
135 SetVisibilityChangedAnimationsEnabled(false);
136 GetNativeWindow()->SetProperty(kStayInSameRootWindowKey
, true);
137 opaque_background_
.SetColor(SK_ColorBLACK
);
138 opaque_background_
.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
139 opaque_background_
.SetOpacity(0.0f
);
140 GetNativeWindow()->layer()->Add(&opaque_background_
);
143 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
144 gfx::ImageSkia shelf_background
=
145 *rb
.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND
);
146 shelf_background_left_
= gfx::ImageSkiaOperations::CreateRotatedImage(
147 shelf_background
, SkBitmapOperations::ROTATION_90_CW
);
148 shelf_background_right_
= gfx::ImageSkiaOperations::CreateRotatedImage(
149 shelf_background
, SkBitmapOperations::ROTATION_270_CW
);
152 // Transitions to |visible_background_type_| if the widget is visible and to
153 // SHELF_BACKGROUND_DEFAULT if it is not.
154 void UpdateBackground() {
155 ShelfBackgroundType background_type
= IsVisible() ?
156 visible_background_type_
: SHELF_BACKGROUND_DEFAULT
;
157 BackgroundAnimatorChangeType change_type
= IsVisible() ?
158 visible_background_change_type_
: BACKGROUND_CHANGE_IMMEDIATE
;
160 float target_opacity
=
161 (background_type
== SHELF_BACKGROUND_MAXIMIZED
) ? 1.0f
: 0.0f
;
162 scoped_ptr
<ui::ScopedLayerAnimationSettings
> opaque_background_animation
;
163 if (change_type
!= BACKGROUND_CHANGE_IMMEDIATE
) {
164 opaque_background_animation
.reset(new ui::ScopedLayerAnimationSettings(
165 opaque_background_
.GetAnimator()));
166 opaque_background_animation
->SetTransitionDuration(
167 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs
));
169 opaque_background_
.SetOpacity(target_opacity
);
171 // TODO(varkha): use ui::Layer on both opaque_background and normal
172 // background retire background_animator_ at all. It would be simpler.
173 // See also ShelfWidget::SetPaintsBackground.
174 background_animator_
.SetPaintsBackground(
175 background_type
!= SHELF_BACKGROUND_DEFAULT
,
177 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
180 DockedAlignment alignment_
;
182 // The animator for the background transitions.
183 BackgroundAnimator background_animator_
;
185 // The alpha to use for drawing image assets covering the docked background.
188 // Solid black background that can be made fully opaque.
189 ui::Layer opaque_background_
;
191 // Backgrounds created from shelf background by 90 or 270 degree rotation.
192 gfx::ImageSkia shelf_background_left_
;
193 gfx::ImageSkia shelf_background_right_
;
195 // The background type to use when the widget is visible. When not visible,
196 // the widget uses SHELF_BACKGROUND_DEFAULT.
197 ShelfBackgroundType visible_background_type_
;
199 // Whether the widget should animate to |visible_background_type_|.
200 BackgroundAnimatorChangeType visible_background_change_type_
;
202 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget
);
207 // Returns true if a window is a popup or a transient child.
208 bool IsPopupOrTransient(const aura::Window
* window
) {
209 return (window
->type() == ui::wm::WINDOW_TYPE_POPUP
||
210 ::wm::GetTransientParent(window
));
213 // Certain windows (minimized, hidden or popups) do not matter to docking.
214 bool IsUsedByLayout(const aura::Window
* window
) {
215 return (window
->IsVisible() &&
216 !wm::GetWindowState(window
)->IsMinimized() &&
217 !IsPopupOrTransient(window
));
220 void UndockWindow(aura::Window
* window
) {
221 gfx::Rect previous_bounds
= window
->bounds();
222 aura::Window
* old_parent
= window
->parent();
223 aura::client::ParentWindowWithContext(window
, window
, gfx::Rect());
224 if (window
->parent() != old_parent
)
225 wm::ReparentTransientChildrenOfChild(window
, old_parent
, window
->parent());
226 // Start maximize or fullscreen (affecting packaged apps) animation from
227 // previous window bounds.
228 window
->layer()->SetBounds(previous_bounds
);
231 // Returns width that is as close as possible to |target_width| while being
232 // consistent with docked min and max restrictions and respects the |window|'s
233 // minimum and maximum size.
234 int GetWindowWidthCloseTo(const aura::Window
* window
, int target_width
) {
235 if (!wm::GetWindowState(window
)->CanResize()) {
236 DCHECK_LE(window
->bounds().width(),
237 DockedWindowLayoutManager::kMaxDockWidth
);
238 return window
->bounds().width();
240 int width
= std::max(DockedWindowLayoutManager::kMinDockWidth
,
241 std::min(target_width
,
242 DockedWindowLayoutManager::kMaxDockWidth
));
243 if (window
->delegate()) {
244 if (window
->delegate()->GetMinimumSize().width() != 0)
245 width
= std::max(width
, window
->delegate()->GetMinimumSize().width());
246 if (window
->delegate()->GetMaximumSize().width() != 0)
247 width
= std::min(width
, window
->delegate()->GetMaximumSize().width());
249 DCHECK_LE(width
, DockedWindowLayoutManager::kMaxDockWidth
);
253 // Returns height that is as close as possible to |target_height| while
254 // respecting the |window|'s minimum and maximum size.
255 int GetWindowHeightCloseTo(const aura::Window
* window
, int target_height
) {
256 if (!wm::GetWindowState(window
)->CanResize())
257 return window
->bounds().height();
258 int minimum_height
= kMinimumHeight
;
259 int maximum_height
= 0;
260 const aura::WindowDelegate
* delegate(window
->delegate());
262 if (delegate
->GetMinimumSize().height() != 0) {
263 minimum_height
= std::max(kMinimumHeight
,
264 delegate
->GetMinimumSize().height());
266 if (delegate
->GetMaximumSize().height() != 0)
267 maximum_height
= delegate
->GetMaximumSize().height();
270 target_height
= std::max(target_height
, minimum_height
);
272 target_height
= std::min(target_height
, maximum_height
);
273 return target_height
;
276 // A functor used to sort the windows in order of their minimum height.
277 struct CompareMinimumHeight
{
278 bool operator()(WindowWithHeight win1
, WindowWithHeight win2
) {
279 return GetWindowHeightCloseTo(win1
.window(), 0) <
280 GetWindowHeightCloseTo(win2
.window(), 0);
284 // A functor used to sort the windows in order of their center Y position.
285 // |delta| is a pre-calculated distance from the bottom of one window to the top
286 // of the next. Its value can be positive (gap) or negative (overlap).
287 // Half of |delta| is used as a transition point at which windows could ideally
289 struct CompareWindowPos
{
290 CompareWindowPos(aura::Window
* dragged_window
,
291 aura::Window
* docked_container
,
293 : dragged_window_(dragged_window
),
294 docked_container_(docked_container
),
297 bool operator()(WindowWithHeight window_with_height1
,
298 WindowWithHeight window_with_height2
) {
299 // Use target coordinates since animations may be active when windows are
301 aura::Window
* win1(window_with_height1
.window());
302 aura::Window
* win2(window_with_height2
.window());
303 gfx::Rect win1_bounds
= ScreenUtil::ConvertRectToScreen(
304 docked_container_
, win1
->GetTargetBounds());
305 gfx::Rect win2_bounds
= ScreenUtil::ConvertRectToScreen(
306 docked_container_
, win2
->GetTargetBounds());
307 win1_bounds
.set_height(window_with_height1
.height_
);
308 win2_bounds
.set_height(window_with_height2
.height_
);
309 // If one of the windows is the |dragged_window_| attempt to make an
310 // earlier swap between the windows than just based on their centers.
311 // This is possible if the dragged window is at least as tall as the other
313 if (win1
== dragged_window_
)
314 return compare_two_windows(win1_bounds
, win2_bounds
);
315 if (win2
== dragged_window_
)
316 return !compare_two_windows(win2_bounds
, win1_bounds
);
317 // Otherwise just compare the centers.
318 return win1_bounds
.CenterPoint().y() < win2_bounds
.CenterPoint().y();
321 // Based on center point tries to deduce where the drag is coming from.
322 // When dragging from below up the transition point is lower.
323 // When dragging from above down the transition point is higher.
324 bool compare_bounds(const gfx::Rect dragged
, const gfx::Rect other
) {
325 if (dragged
.CenterPoint().y() < other
.CenterPoint().y())
326 return dragged
.CenterPoint().y() < other
.y() - delta_
;
327 return dragged
.CenterPoint().y() < other
.bottom() + delta_
;
330 // Performs comparison both ways and selects stable result.
331 bool compare_two_windows(const gfx::Rect bounds1
, const gfx::Rect bounds2
) {
332 // Try comparing windows in both possible orders and see if the comparison
334 bool result1
= compare_bounds(bounds1
, bounds2
);
335 bool result2
= compare_bounds(bounds2
, bounds1
);
336 if (result1
!= result2
)
339 // Otherwise it is not possible to be sure that the windows will not bounce.
340 // In this case just compare the centers.
341 return bounds1
.CenterPoint().y() < bounds2
.CenterPoint().y();
345 aura::Window
* dragged_window_
;
346 aura::Window
* docked_container_
;
352 ////////////////////////////////////////////////////////////////////////////////
353 // A class that observes shelf for bounds changes.
354 class DockedWindowLayoutManager::ShelfWindowObserver
: public WindowObserver
{
356 explicit ShelfWindowObserver(
357 DockedWindowLayoutManager
* docked_layout_manager
)
358 : docked_layout_manager_(docked_layout_manager
) {
359 DCHECK(docked_layout_manager_
->shelf()->shelf_widget());
360 docked_layout_manager_
->shelf()->shelf_widget()->GetNativeView()
364 ~ShelfWindowObserver() override
{
365 if (docked_layout_manager_
->shelf() &&
366 docked_layout_manager_
->shelf()->shelf_widget())
367 docked_layout_manager_
->shelf()->shelf_widget()->GetNativeView()
368 ->RemoveObserver(this);
371 // aura::WindowObserver:
372 void OnWindowBoundsChanged(aura::Window
* window
,
373 const gfx::Rect
& old_bounds
,
374 const gfx::Rect
& new_bounds
) override
{
375 shelf_bounds_in_screen_
= ScreenUtil::ConvertRectToScreen(
376 window
->parent(), new_bounds
);
377 docked_layout_manager_
->OnShelfBoundsChanged();
380 const gfx::Rect
& shelf_bounds_in_screen() const {
381 return shelf_bounds_in_screen_
;
385 DockedWindowLayoutManager
* docked_layout_manager_
;
386 gfx::Rect shelf_bounds_in_screen_
;
388 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver
);
391 ////////////////////////////////////////////////////////////////////////////////
392 // DockedWindowLayoutManager public implementation:
393 DockedWindowLayoutManager::DockedWindowLayoutManager(
394 aura::Window
* dock_container
,
395 WorkspaceController
* workspace_controller
)
396 : SnapToPixelLayoutManager(dock_container
),
397 dock_container_(dock_container
),
399 dragged_window_(NULL
),
400 is_dragged_window_docked_(false),
401 is_dragged_from_dock_(false),
403 workspace_controller_(workspace_controller
),
404 in_fullscreen_(workspace_controller_
->GetWindowState() ==
405 WORKSPACE_WINDOW_STATE_FULL_SCREEN
),
407 alignment_(DOCKED_ALIGNMENT_NONE
),
408 preferred_alignment_(DOCKED_ALIGNMENT_NONE
),
409 event_source_(DOCKED_ACTION_SOURCE_UNKNOWN
),
410 last_active_window_(NULL
),
411 last_action_time_(base::Time::Now()),
412 background_widget_(new DockedBackgroundWidget(dock_container_
)) {
413 DCHECK(dock_container
);
414 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
416 Shell::GetInstance()->AddShellObserver(this);
419 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
423 void DockedWindowLayoutManager::Shutdown() {
424 if (shelf_
&& shelf_
->shelf_widget()) {
425 ShelfLayoutManager
* shelf_layout_manager
= ShelfLayoutManager::ForShelf(
426 shelf_
->shelf_widget()->GetNativeWindow());
427 shelf_layout_manager
->RemoveObserver(this);
428 shelf_observer_
.reset();
431 for (size_t i
= 0; i
< dock_container_
->children().size(); ++i
) {
432 aura::Window
* child
= dock_container_
->children()[i
];
433 child
->RemoveObserver(this);
434 wm::GetWindowState(child
)->RemoveObserver(this);
436 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
437 RemoveObserver(this);
438 Shell::GetInstance()->RemoveShellObserver(this);
441 void DockedWindowLayoutManager::AddObserver(
442 DockedWindowLayoutManagerObserver
* observer
) {
443 observer_list_
.AddObserver(observer
);
446 void DockedWindowLayoutManager::RemoveObserver(
447 DockedWindowLayoutManagerObserver
* observer
) {
448 observer_list_
.RemoveObserver(observer
);
451 void DockedWindowLayoutManager::StartDragging(aura::Window
* window
) {
452 DCHECK(!dragged_window_
);
453 dragged_window_
= window
;
454 DCHECK(!IsPopupOrTransient(window
));
455 // Start observing a window unless it is docked container's child in which
456 // case it is already observed.
457 wm::WindowState
* dragged_state
= wm::GetWindowState(dragged_window_
);
458 if (dragged_window_
->parent() != dock_container_
) {
459 dragged_window_
->AddObserver(this);
460 dragged_state
->AddObserver(this);
461 } else if (!IsAnyWindowDocked() &&
462 dragged_state
->drag_details() &&
463 !(dragged_state
->drag_details()->bounds_change
&
464 WindowResizer::kBoundsChange_Resizes
)) {
465 // If there are no other docked windows clear alignment when a docked window
466 // is moved (but not when it is resized or the window could get undocked
467 // when resized away from the edge while docked).
468 alignment_
= DOCKED_ALIGNMENT_NONE
;
470 is_dragged_from_dock_
= window
->parent() == dock_container_
;
471 DCHECK(!is_dragged_window_docked_
);
473 // Resize all windows that are flush with the dock edge together if one of
474 // them gets resized.
475 if (dragged_window_
->bounds().width() == docked_width_
&&
476 (dragged_state
->drag_details()->bounds_change
&
477 WindowResizer::kBoundsChange_Resizes
) &&
478 (dragged_state
->drag_details()->size_change_direction
&
479 WindowResizer::kBoundsChangeDirection_Horizontal
)) {
480 for (size_t i
= 0; i
< dock_container_
->children().size(); ++i
) {
481 aura::Window
* window1(dock_container_
->children()[i
]);
482 if (IsUsedByLayout(window1
) &&
483 window1
!= dragged_window_
&&
484 window1
->bounds().width() == docked_width_
) {
485 wm::GetWindowState(window1
)->set_bounds_changed_by_user(false);
491 void DockedWindowLayoutManager::DockDraggedWindow(aura::Window
* window
) {
492 DCHECK(!IsPopupOrTransient(window
));
493 OnDraggedWindowDocked(window
);
497 void DockedWindowLayoutManager::UndockDraggedWindow() {
498 DCHECK(!IsPopupOrTransient(dragged_window_
));
499 OnDraggedWindowUndocked();
501 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
502 is_dragged_from_dock_
= false;
505 void DockedWindowLayoutManager::FinishDragging(DockedAction action
,
506 DockedActionSource source
) {
507 DCHECK(dragged_window_
);
508 DCHECK(!IsPopupOrTransient(dragged_window_
));
509 if (is_dragged_window_docked_
)
510 OnDraggedWindowUndocked();
511 DCHECK (!is_dragged_window_docked_
);
512 // Stop observing a window unless it is docked container's child in which
513 // case it needs to keep being observed after the drag completes.
514 if (dragged_window_
->parent() != dock_container_
) {
515 dragged_window_
->RemoveObserver(this);
516 wm::GetWindowState(dragged_window_
)->RemoveObserver(this);
517 if (last_active_window_
== dragged_window_
)
518 last_active_window_
= NULL
;
520 // If this is the first window that got docked by a move update alignment.
521 if (alignment_
== DOCKED_ALIGNMENT_NONE
)
522 alignment_
= GetEdgeNearestWindow(dragged_window_
);
523 // A window is no longer dragged and is a child.
524 // When a window becomes a child at drag start this is
525 // the only opportunity we will have to enforce a window
526 // count limit so do it here.
527 MaybeMinimizeChildrenExcept(dragged_window_
);
529 dragged_window_
= NULL
;
530 dragged_bounds_
= gfx::Rect();
532 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
533 RecordUmaAction(action
, source
);
536 void DockedWindowLayoutManager::SetShelf(Shelf
* shelf
) {
539 if (shelf_
->shelf_widget()) {
540 ShelfLayoutManager
* shelf_layout_manager
= ShelfLayoutManager::ForShelf(
541 shelf_
->shelf_widget()->GetNativeWindow());
542 shelf_layout_manager
->AddObserver(this);
543 shelf_observer_
.reset(new ShelfWindowObserver(this));
547 DockedAlignment
DockedWindowLayoutManager::GetAlignmentOfWindow(
548 const aura::Window
* window
) const {
549 const gfx::Rect
& bounds(window
->GetBoundsInScreen());
551 // Test overlap with an existing docked area first.
552 if (docked_bounds_
.Intersects(bounds
) &&
553 alignment_
!= DOCKED_ALIGNMENT_NONE
) {
554 // A window is being added to other docked windows (on the same side).
558 const gfx::Rect container_bounds
= dock_container_
->GetBoundsInScreen();
559 if (bounds
.x() <= container_bounds
.x() &&
560 bounds
.right() > container_bounds
.x()) {
561 return DOCKED_ALIGNMENT_LEFT
;
562 } else if (bounds
.x() < container_bounds
.right() &&
563 bounds
.right() >= container_bounds
.right()) {
564 return DOCKED_ALIGNMENT_RIGHT
;
566 return DOCKED_ALIGNMENT_NONE
;
569 DockedAlignment
DockedWindowLayoutManager::CalculateAlignment() const {
570 return CalculateAlignmentExcept(dragged_window_
);
573 DockedAlignment
DockedWindowLayoutManager::CalculateAlignmentExcept(
574 const aura::Window
* window
) const {
575 // Find a child that is not the window being queried and is not a popup.
576 // If such exists the current alignment is returned - even if some of the
577 // children are hidden or minimized (so they can be restored without losing
578 // the docked state).
579 for (size_t i
= 0; i
< dock_container_
->children().size(); ++i
) {
580 aura::Window
* child(dock_container_
->children()[i
]);
581 if (window
!= child
&& !IsPopupOrTransient(child
))
584 // No docked windows remain other than possibly the window being queried.
585 // Return |NONE| to indicate that windows may get docked on either side.
586 return DOCKED_ALIGNMENT_NONE
;
589 bool DockedWindowLayoutManager::CanDockWindow(
590 aura::Window
* window
,
591 DockedAlignment desired_alignment
) {
592 // Don't allow interactive docking of windows with transient parents such as
593 // modal browser dialogs. Prevent docking of panels attached to shelf during
595 wm::WindowState
* window_state
= wm::GetWindowState(window
);
596 bool should_attach_to_shelf
= window_state
->drag_details() &&
597 window_state
->drag_details()->should_attach_to_shelf
;
598 if (IsPopupOrTransient(window
) || should_attach_to_shelf
)
600 // If a window is wide and cannot be resized down to maximum width allowed
601 // then it cannot be docked.
602 // TODO(varkha). Prevent windows from changing size programmatically while
603 // they are docked. The size will take effect only once a window is undocked.
604 // See http://crbug.com/307792.
605 if (window
->bounds().width() > kMaxDockWidth
&&
606 (!window_state
->CanResize() ||
607 (window
->delegate() &&
608 window
->delegate()->GetMinimumSize().width() != 0 &&
609 window
->delegate()->GetMinimumSize().width() > kMaxDockWidth
))) {
612 // If a window is tall and cannot be resized down to maximum height allowed
613 // then it cannot be docked.
614 const gfx::Rect work_area
=
615 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_
).work_area();
616 if (GetWindowHeightCloseTo(window
, work_area
.height()) > work_area
.height())
618 // Cannot dock on the other size from an existing dock.
619 const DockedAlignment alignment
= CalculateAlignmentExcept(window
);
620 if (desired_alignment
!= DOCKED_ALIGNMENT_NONE
&&
621 alignment
!= DOCKED_ALIGNMENT_NONE
&&
622 alignment
!= desired_alignment
) {
625 // Do not allow docking on the same side as shelf.
626 return IsDockedAlignmentValid(desired_alignment
);
629 bool DockedWindowLayoutManager::IsDockedAlignmentValid(
630 DockedAlignment alignment
) const {
631 ShelfAlignment shelf_alignment
= shelf_
? shelf_
->alignment() :
632 SHELF_ALIGNMENT_BOTTOM
;
633 if ((alignment
== DOCKED_ALIGNMENT_LEFT
&&
634 shelf_alignment
== SHELF_ALIGNMENT_LEFT
) ||
635 (alignment
== DOCKED_ALIGNMENT_RIGHT
&&
636 shelf_alignment
== SHELF_ALIGNMENT_RIGHT
)) {
642 void DockedWindowLayoutManager::MaybeSetDesiredDockedAlignment(
643 DockedAlignment alignment
) {
644 // If the requested alignment is |NONE| or there are no
645 // docked windows return early as we can't change whether there is a
646 // dock or not. If the requested alignment is the same as the current
647 // alignment return early as an optimization.
648 if (alignment
== DOCKED_ALIGNMENT_NONE
||
649 alignment_
== DOCKED_ALIGNMENT_NONE
||
650 alignment_
== alignment
||
651 !IsDockedAlignmentValid(alignment
)) {
654 alignment_
= alignment
;
657 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
660 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
662 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED
);
665 ////////////////////////////////////////////////////////////////////////////////
666 // DockedWindowLayoutManager, aura::LayoutManager implementation:
667 void DockedWindowLayoutManager::OnWindowResized() {
668 MaybeMinimizeChildrenExcept(dragged_window_
);
670 // When screen resizes update the insets even when dock width or alignment
672 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED
);
675 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window
* child
) {
676 if (IsPopupOrTransient(child
))
678 // Dragged windows are already observed by StartDragging and do not change
679 // docked alignment during the drag.
680 if (child
== dragged_window_
)
682 // If this is the first window getting docked - update alignment.
683 // A window can be added without proper bounds when window is moved to another
684 // display via API or due to display configuration change, so the alignment
685 // is set based on which edge is closer in the new display.
686 if (alignment_
== DOCKED_ALIGNMENT_NONE
) {
687 alignment_
= preferred_alignment_
!= DOCKED_ALIGNMENT_NONE
?
688 preferred_alignment_
: GetEdgeNearestWindow(child
);
690 MaybeMinimizeChildrenExcept(child
);
691 child
->AddObserver(this);
692 wm::GetWindowState(child
)->AddObserver(this);
694 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
696 // Only keyboard-initiated actions are recorded here. Dragging cases
697 // are handled in FinishDragging.
698 if (event_source_
!= DOCKED_ACTION_SOURCE_UNKNOWN
)
699 RecordUmaAction(DOCKED_ACTION_DOCK
, event_source_
);
702 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window
* child
) {
703 if (IsPopupOrTransient(child
))
705 // Dragged windows are stopped being observed by FinishDragging and do not
706 // change alignment during the drag. They also cannot be set to be the
707 // |last_active_window_|.
708 if (child
== dragged_window_
)
710 // If this is the last window, set alignment and maximize the workspace.
711 if (!IsAnyWindowDocked()) {
712 alignment_
= DOCKED_ALIGNMENT_NONE
;
713 UpdateDockedWidth(0);
715 if (last_active_window_
== child
)
716 last_active_window_
= NULL
;
717 child
->RemoveObserver(this);
718 wm::GetWindowState(child
)->RemoveObserver(this);
720 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
723 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
726 if (IsPopupOrTransient(child
))
729 wm::WindowState
* window_state
= wm::GetWindowState(child
);
730 if (visible
&& window_state
->IsMinimized())
731 window_state
->Restore();
733 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
736 void DockedWindowLayoutManager::SetChildBounds(
738 const gfx::Rect
& requested_bounds
) {
739 // The minimum constraints have to be applied first by the layout manager.
740 gfx::Rect
actual_new_bounds(requested_bounds
);
741 if (child
->delegate()) {
742 const gfx::Size
& min_size
= child
->delegate()->GetMinimumSize();
743 actual_new_bounds
.set_width(
744 std::max(min_size
.width(), actual_new_bounds
.width()));
745 actual_new_bounds
.set_height(
746 std::max(min_size
.height(), actual_new_bounds
.height()));
748 SnapToPixelLayoutManager::SetChildBounds(child
, actual_new_bounds
);
749 if (IsPopupOrTransient(child
))
751 // Whenever one of our windows is moved or resized enforce layout.
752 ShelfLayoutManager
* shelf_layout
=
753 ShelfLayoutManager::ForShelf(dock_container_
);
755 shelf_layout
->UpdateVisibilityState();
758 ////////////////////////////////////////////////////////////////////////////////
759 // DockedWindowLayoutManager, ash::ShellObserver implementation:
761 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
763 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED
);
764 MaybeMinimizeChildrenExcept(dragged_window_
);
767 void DockedWindowLayoutManager::OnFullscreenStateChanged(
768 bool is_fullscreen
, aura::Window
* root_window
) {
769 if (dock_container_
->GetRootWindow() != root_window
)
771 // Entering fullscreen mode (including immersive) hides docked windows.
772 in_fullscreen_
= workspace_controller_
->GetWindowState() ==
773 WORKSPACE_WINDOW_STATE_FULL_SCREEN
;
775 // prevent Relayout from getting called multiple times during this
776 base::AutoReset
<bool> auto_reset_in_layout(&in_layout_
, true);
777 // Use a copy of children array because a call to MinimizeDockedWindow or
778 // RestoreDockedWindow can change order.
779 aura::Window::Windows
children(dock_container_
->children());
780 for (aura::Window::Windows::const_iterator iter
= children
.begin();
781 iter
!= children
.end(); ++iter
) {
782 aura::Window
* window(*iter
);
783 if (IsPopupOrTransient(window
))
785 wm::WindowState
* window_state
= wm::GetWindowState(window
);
786 if (in_fullscreen_
) {
787 if (window
->IsVisible())
788 MinimizeDockedWindow(window_state
);
790 if (!window_state
->IsMinimized())
791 RestoreDockedWindow(window_state
);
796 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED
);
799 void DockedWindowLayoutManager::OnShelfAlignmentChanged(
800 aura::Window
* root_window
) {
801 if (dock_container_
->GetRootWindow() != root_window
)
804 if (!shelf_
|| !shelf_
->shelf_widget())
807 if (alignment_
== DOCKED_ALIGNMENT_NONE
)
810 // Do not allow shelf and dock on the same side. Switch side that
811 // the dock is attached to and move all dock windows to that new side.
812 ShelfAlignment shelf_alignment
= shelf_
->shelf_widget()->GetAlignment();
813 if (alignment_
== DOCKED_ALIGNMENT_LEFT
&&
814 shelf_alignment
== SHELF_ALIGNMENT_LEFT
) {
815 alignment_
= DOCKED_ALIGNMENT_RIGHT
;
816 } else if (alignment_
== DOCKED_ALIGNMENT_RIGHT
&&
817 shelf_alignment
== SHELF_ALIGNMENT_RIGHT
) {
818 alignment_
= DOCKED_ALIGNMENT_LEFT
;
821 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED
);
824 /////////////////////////////////////////////////////////////////////////////
825 // DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
826 void DockedWindowLayoutManager::OnBackgroundUpdated(
827 ShelfBackgroundType background_type
,
828 BackgroundAnimatorChangeType change_type
) {
829 background_widget_
->SetBackgroundType(background_type
, change_type
);
832 /////////////////////////////////////////////////////////////////////////////
833 // DockedWindowLayoutManager, WindowStateObserver implementation:
835 void DockedWindowLayoutManager::OnPreWindowStateTypeChange(
836 wm::WindowState
* window_state
,
837 wm::WindowStateType old_type
) {
838 aura::Window
* window
= window_state
->window();
839 if (IsPopupOrTransient(window
))
841 // The window property will still be set, but no actual change will occur
842 // until OnFullscreenStateChange is called when exiting fullscreen.
845 if (!window_state
->IsDocked()) {
846 if (window
!= dragged_window_
) {
847 UndockWindow(window
);
848 if (window_state
->IsMaximizedOrFullscreen())
849 RecordUmaAction(DOCKED_ACTION_MAXIMIZE
, event_source_
);
851 RecordUmaAction(DOCKED_ACTION_UNDOCK
, event_source_
);
853 } else if (window_state
->IsMinimized()) {
854 MinimizeDockedWindow(window_state
);
855 } else if (old_type
== wm::WINDOW_STATE_TYPE_DOCKED_MINIMIZED
) {
856 RestoreDockedWindow(window_state
);
857 } else if (old_type
== wm::WINDOW_STATE_TYPE_MINIMIZED
) {
858 NOTREACHED() << "Minimized window in docked layout manager";
862 /////////////////////////////////////////////////////////////////////////////
863 // DockedWindowLayoutManager, WindowObserver implementation:
865 void DockedWindowLayoutManager::OnWindowBoundsChanged(
866 aura::Window
* window
,
867 const gfx::Rect
& old_bounds
,
868 const gfx::Rect
& new_bounds
) {
869 // Only relayout if the dragged window would get docked.
870 if (window
== dragged_window_
&& is_dragged_window_docked_
)
874 void DockedWindowLayoutManager::OnWindowVisibilityChanging(
875 aura::Window
* window
, bool visible
) {
876 if (IsPopupOrTransient(window
))
878 int animation_type
= ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT
;
880 animation_type
= ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP
;
881 ::wm::SetWindowVisibilityAnimationDuration(
882 window
, base::TimeDelta::FromMilliseconds(kFadeDurationMs
));
883 } else if (wm::GetWindowState(window
)->IsMinimized()) {
884 animation_type
= WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE
;
886 ::wm::SetWindowVisibilityAnimationType(window
, animation_type
);
889 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window
* window
) {
890 if (dragged_window_
== window
) {
891 FinishDragging(DOCKED_ACTION_NONE
, DOCKED_ACTION_SOURCE_UNKNOWN
);
892 DCHECK(!dragged_window_
);
893 DCHECK(!is_dragged_window_docked_
);
895 if (window
== last_active_window_
)
896 last_active_window_
= NULL
;
897 RecordUmaAction(DOCKED_ACTION_CLOSE
, event_source_
);
901 ////////////////////////////////////////////////////////////////////////////////
902 // DockedWindowLayoutManager, aura::client::ActivationChangeObserver
905 void DockedWindowLayoutManager::OnWindowActivated(aura::Window
* gained_active
,
906 aura::Window
* lost_active
) {
907 if (gained_active
&& IsPopupOrTransient(gained_active
))
909 // Ignore if the window that is not managed by this was activated.
910 aura::Window
* ancestor
= NULL
;
911 for (aura::Window
* parent
= gained_active
;
912 parent
; parent
= parent
->parent()) {
913 if (parent
->parent() == dock_container_
) {
919 UpdateStacking(ancestor
);
922 ////////////////////////////////////////////////////////////////////////////////
923 // DockedWindowLayoutManager private implementation:
925 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
926 aura::Window
* child
) {
927 // Minimize any windows that don't fit without overlap.
928 const gfx::Rect work_area
=
929 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_
).work_area();
930 int available_room
= work_area
.height();
931 bool gap_needed
= !!child
;
933 available_room
-= GetWindowHeightCloseTo(child
, 0);
934 // Use a copy of children array because a call to Minimize can change order.
935 aura::Window::Windows
children(dock_container_
->children());
936 aura::Window::Windows::const_reverse_iterator iter
= children
.rbegin();
937 while (iter
!= children
.rend()) {
938 aura::Window
* window(*iter
++);
939 if (window
== child
|| !IsUsedByLayout(window
))
941 int room_needed
= GetWindowHeightCloseTo(window
, 0) +
942 (gap_needed
? kMinDockGap
: 0);
944 if (available_room
> room_needed
) {
945 available_room
-= room_needed
;
947 // Slow down minimizing animations. Lock duration so that it is not
948 // overridden by other ScopedLayerAnimationSettings down the stack.
949 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
950 settings
.SetTransitionDuration(
951 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs
));
952 settings
.LockTransitionDuration();
953 wm::GetWindowState(window
)->Minimize();
958 void DockedWindowLayoutManager::MinimizeDockedWindow(
959 wm::WindowState
* window_state
) {
960 DCHECK(!IsPopupOrTransient(window_state
->window()));
961 window_state
->window()->Hide();
962 if (window_state
->IsActive())
963 window_state
->Deactivate();
964 RecordUmaAction(DOCKED_ACTION_MINIMIZE
, event_source_
);
967 void DockedWindowLayoutManager::RestoreDockedWindow(
968 wm::WindowState
* window_state
) {
969 aura::Window
* window
= window_state
->window();
970 DCHECK(!IsPopupOrTransient(window
));
971 // Always place restored window at the bottom shuffling the other windows up.
972 // TODO(varkha): add a separate container for docked windows to keep track
974 gfx::Display display
= Shell::GetScreen()->GetDisplayNearestWindow(
976 const gfx::Rect work_area
= display
.work_area();
978 // Evict the window if it can no longer be docked because of its height.
979 if (!CanDockWindow(window
, DOCKED_ALIGNMENT_NONE
)) {
980 window_state
->Restore();
981 RecordUmaAction(DOCKED_ACTION_EVICT
, event_source_
);
984 gfx::Rect
bounds(window
->bounds());
985 bounds
.set_y(work_area
.bottom());
986 window
->SetBounds(bounds
);
988 MaybeMinimizeChildrenExcept(window
);
989 RecordUmaAction(DOCKED_ACTION_RESTORE
, event_source_
);
992 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action
,
993 DockedActionSource source
) {
994 if (action
== DOCKED_ACTION_NONE
)
996 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action
, DOCKED_ACTION_COUNT
);
997 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source
,
998 DOCKED_ACTION_SOURCE_COUNT
);
999 base::Time time_now
= base::Time::Now();
1000 base::TimeDelta time_between_use
= time_now
- last_action_time_
;
1001 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse",
1002 time_between_use
.InSeconds(),
1004 base::TimeDelta::FromHours(10).InSeconds(),
1006 last_action_time_
= time_now
;
1007 int docked_all_count
= 0;
1008 int docked_visible_count
= 0;
1009 int docked_panels_count
= 0;
1010 int large_windows_count
= 0;
1011 for (size_t i
= 0; i
< dock_container_
->children().size(); ++i
) {
1012 const aura::Window
* window(dock_container_
->children()[i
]);
1013 if (IsPopupOrTransient(window
))
1016 if (!IsUsedByLayout(window
))
1018 docked_visible_count
++;
1019 if (window
->type() == ui::wm::WINDOW_TYPE_PANEL
)
1020 docked_panels_count
++;
1021 const wm::WindowState
* window_state
= wm::GetWindowState(window
);
1022 if (window_state
->HasRestoreBounds()) {
1023 const gfx::Rect restore_bounds
= window_state
->GetRestoreBoundsInScreen();
1024 if (restore_bounds
.width() > kMaxDockWidth
)
1025 large_windows_count
++;
1028 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count
);
1029 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count
);
1030 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count
);
1031 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count
);
1034 void DockedWindowLayoutManager::UpdateDockedWidth(int width
) {
1035 if (docked_width_
== width
)
1037 docked_width_
= width
;
1038 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_
);
1041 void DockedWindowLayoutManager::OnDraggedWindowDocked(aura::Window
* window
) {
1042 DCHECK(!is_dragged_window_docked_
);
1043 is_dragged_window_docked_
= true;
1046 void DockedWindowLayoutManager::OnDraggedWindowUndocked() {
1047 DCHECK (is_dragged_window_docked_
);
1048 is_dragged_window_docked_
= false;
1051 bool DockedWindowLayoutManager::IsAnyWindowDocked() {
1052 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE
;
1055 DockedAlignment
DockedWindowLayoutManager::GetEdgeNearestWindow(
1056 const aura::Window
* window
) const {
1057 const gfx::Rect
& bounds(window
->GetBoundsInScreen());
1058 const gfx::Rect container_bounds
= dock_container_
->GetBoundsInScreen();
1059 return (abs(bounds
.x() - container_bounds
.x()) <
1060 abs(bounds
.right() - container_bounds
.right())) ?
1061 DOCKED_ALIGNMENT_LEFT
: DOCKED_ALIGNMENT_RIGHT
;
1064 void DockedWindowLayoutManager::Relayout() {
1067 if (alignment_
== DOCKED_ALIGNMENT_NONE
&& !is_dragged_window_docked_
)
1069 base::AutoReset
<bool> auto_reset_in_layout(&in_layout_
, true);
1071 gfx::Rect dock_bounds
= dock_container_
->GetBoundsInScreen();
1072 aura::Window
* active_window
= NULL
;
1073 std::vector
<WindowWithHeight
> visible_windows
;
1074 for (size_t i
= 0; i
< dock_container_
->children().size(); ++i
) {
1075 aura::Window
* window(dock_container_
->children()[i
]);
1077 if (!IsUsedByLayout(window
) || window
== dragged_window_
)
1080 // If the shelf is currently hidden (full-screen mode), hide window until
1081 // full-screen mode is exited.
1082 if (in_fullscreen_
) {
1083 // The call to Hide does not set the minimize property, so the window will
1084 // be restored when the shelf becomes visible again.
1088 if (window
->HasFocus() ||
1090 aura::client::GetFocusClient(window
)->GetFocusedWindow())) {
1091 DCHECK(!active_window
);
1092 active_window
= window
;
1094 visible_windows
.push_back(WindowWithHeight(window
));
1096 // Consider docked dragged_window_ when fanning out other child windows.
1097 if (is_dragged_window_docked_
) {
1098 visible_windows
.push_back(WindowWithHeight(dragged_window_
));
1099 DCHECK(!active_window
);
1100 active_window
= dragged_window_
;
1103 // Position docked windows as well as the window being dragged.
1104 gfx::Rect work_area
=
1105 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_
).work_area();
1106 if (shelf_observer_
)
1107 work_area
.Subtract(shelf_observer_
->shelf_bounds_in_screen());
1108 int available_room
= CalculateWindowHeightsAndRemainingRoom(work_area
,
1110 FanOutChildren(work_area
,
1111 CalculateIdealWidth(visible_windows
),
1115 // After the first Relayout allow the windows to change their order easier
1116 // since we know they are docked.
1117 is_dragged_from_dock_
= true;
1118 UpdateStacking(active_window
);
1121 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom(
1122 const gfx::Rect work_area
,
1123 std::vector
<WindowWithHeight
>* visible_windows
) {
1124 int available_room
= work_area
.height();
1125 int remaining_windows
= visible_windows
->size();
1126 int gap_height
= remaining_windows
> 1 ? kMinDockGap
: 0;
1128 // Sort windows by their minimum heights and calculate target heights.
1129 std::sort(visible_windows
->begin(), visible_windows
->end(),
1130 CompareMinimumHeight());
1131 // Distribute the free space among the docked windows. Since the windows are
1132 // sorted (tall windows first) we can now assume that any window which
1133 // required more space than the current window will have already been
1134 // accounted for previously in this loop, so we can safely give that window
1135 // its proportional share of the remaining space.
1136 for (std::vector
<WindowWithHeight
>::reverse_iterator iter
=
1137 visible_windows
->rbegin();
1138 iter
!= visible_windows
->rend(); ++iter
) {
1139 iter
->height_
= GetWindowHeightCloseTo(
1141 (available_room
+ gap_height
) / remaining_windows
- gap_height
);
1142 available_room
-= (iter
->height_
+ gap_height
);
1143 remaining_windows
--;
1145 return available_room
+ gap_height
;
1148 int DockedWindowLayoutManager::CalculateIdealWidth(
1149 const std::vector
<WindowWithHeight
>& visible_windows
) {
1150 int smallest_max_width
= kMaxDockWidth
;
1151 int largest_min_width
= kMinDockWidth
;
1152 // Ideal width of the docked area is as close to kIdealWidth as possible
1153 // while still respecting the minimum and maximum width restrictions on the
1154 // individual docked windows as well as the width that was possibly set by a
1155 // user (which needs to be preserved when dragging and rearranging windows).
1156 for (std::vector
<WindowWithHeight
>::const_iterator iter
=
1157 visible_windows
.begin();
1158 iter
!= visible_windows
.end(); ++iter
) {
1159 const aura::Window
* window
= iter
->window();
1160 int min_window_width
= window
->bounds().width();
1161 int max_window_width
= min_window_width
;
1162 if (!wm::GetWindowState(window
)->bounds_changed_by_user()) {
1163 min_window_width
= GetWindowWidthCloseTo(window
, kMinDockWidth
);
1164 max_window_width
= GetWindowWidthCloseTo(window
, kMaxDockWidth
);
1166 largest_min_width
= std::max(largest_min_width
, min_window_width
);
1167 smallest_max_width
= std::min(smallest_max_width
, max_window_width
);
1169 int ideal_width
= std::max(largest_min_width
,
1170 std::min(smallest_max_width
, kIdealWidth
));
1171 // Restrict docked area width regardless of window restrictions.
1172 ideal_width
= std::max(std::min(ideal_width
, kMaxDockWidth
), kMinDockWidth
);
1176 void DockedWindowLayoutManager::FanOutChildren(
1177 const gfx::Rect
& work_area
,
1178 int ideal_docked_width
,
1180 std::vector
<WindowWithHeight
>* visible_windows
) {
1181 gfx::Rect dock_bounds
= dock_container_
->GetBoundsInScreen();
1183 // Calculate initial vertical offset and the gap or overlap between windows.
1184 const int num_windows
= visible_windows
->size();
1185 const float delta
= static_cast<float>(available_room
) /
1186 ((available_room
> 0 || num_windows
<= 1) ?
1187 num_windows
+ 1 : num_windows
- 1);
1188 float y_pos
= work_area
.y() + ((delta
> 0) ? delta
: 0);
1190 // Docked area is shown only if there is at least one non-dragged visible
1192 int new_width
= ideal_docked_width
;
1193 if (visible_windows
->empty() ||
1194 (visible_windows
->size() == 1 &&
1195 (*visible_windows
)[0].window() == dragged_window_
)) {
1198 UpdateDockedWidth(new_width
);
1199 // Sort windows by their center positions and fan out overlapping
1201 std::sort(visible_windows
->begin(), visible_windows
->end(),
1202 CompareWindowPos(is_dragged_from_dock_
? dragged_window_
: NULL
,
1205 for (std::vector
<WindowWithHeight
>::iterator iter
= visible_windows
->begin();
1206 iter
!= visible_windows
->end(); ++iter
) {
1207 aura::Window
* window
= iter
->window();
1208 gfx::Rect bounds
= ScreenUtil::ConvertRectToScreen(
1209 dock_container_
, window
->GetTargetBounds());
1210 // A window is extended or shrunk to be as close as possible to the ideal
1211 // docked area width. Windows that were resized by a user are kept at their
1213 // This also enforces the min / max restrictions on the docked area width.
1214 bounds
.set_width(GetWindowWidthCloseTo(
1216 wm::GetWindowState(window
)->bounds_changed_by_user() ?
1217 bounds
.width() : ideal_docked_width
));
1218 DCHECK_LE(bounds
.width(), ideal_docked_width
);
1220 DockedAlignment alignment
= alignment_
;
1221 if (alignment
== DOCKED_ALIGNMENT_NONE
&& window
== dragged_window_
)
1222 alignment
= GetEdgeNearestWindow(window
);
1224 // Fan out windows evenly distributing the overlap or remaining free space.
1225 bounds
.set_height(iter
->height_
);
1226 bounds
.set_y(std::max(work_area
.y(),
1227 std::min(work_area
.bottom() - bounds
.height(),
1228 static_cast<int>(y_pos
+ 0.5))));
1229 y_pos
+= bounds
.height() + delta
+ kMinDockGap
;
1231 // All docked windows other than the one currently dragged remain stuck
1232 // to the screen edge (flush with the edge or centered in the dock area).
1233 switch (alignment
) {
1234 case DOCKED_ALIGNMENT_LEFT
:
1235 bounds
.set_x(dock_bounds
.x() +
1236 (ideal_docked_width
- bounds
.width()) / 2);
1238 case DOCKED_ALIGNMENT_RIGHT
:
1239 bounds
.set_x(dock_bounds
.right() -
1240 (ideal_docked_width
+ bounds
.width()) / 2);
1242 case DOCKED_ALIGNMENT_NONE
:
1245 if (window
== dragged_window_
) {
1246 dragged_bounds_
= bounds
;
1249 // If the following asserts it is probably because not all the children
1250 // have been removed when dock was closed.
1251 DCHECK_NE(alignment_
, DOCKED_ALIGNMENT_NONE
);
1252 bounds
= ScreenUtil::ConvertRectFromScreen(dock_container_
, bounds
);
1253 if (bounds
!= window
->GetTargetBounds()) {
1254 ui::Layer
* layer
= window
->layer();
1255 ui::ScopedLayerAnimationSettings
slide_settings(layer
->GetAnimator());
1256 slide_settings
.SetPreemptionStrategy(
1257 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
1258 slide_settings
.SetTransitionDuration(
1259 base::TimeDelta::FromMilliseconds(kSlideDurationMs
));
1260 SetChildBoundsDirect(window
, bounds
);
1265 void DockedWindowLayoutManager::UpdateDockBounds(
1266 DockedWindowLayoutManagerObserver::Reason reason
) {
1267 int dock_inset
= docked_width_
+ (docked_width_
> 0 ? kMinDockGap
: 0);
1268 const gfx::Rect work_area
=
1269 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_
).work_area();
1270 gfx::Rect bounds
= gfx::Rect(
1271 alignment_
== DOCKED_ALIGNMENT_RIGHT
&& dock_inset
> 0 ?
1272 dock_container_
->bounds().right() - dock_inset
:
1273 dock_container_
->bounds().x(),
1274 dock_container_
->bounds().y(),
1276 work_area
.height());
1277 docked_bounds_
= bounds
+
1278 dock_container_
->GetBoundsInScreen().OffsetFromOrigin();
1280 DockedWindowLayoutManagerObserver
,
1282 OnDockBoundsChanging(bounds
, reason
));
1283 // Show or hide background for docked area.
1284 gfx::Rect
background_bounds(docked_bounds_
);
1285 if (shelf_observer_
)
1286 background_bounds
.Subtract(shelf_observer_
->shelf_bounds_in_screen());
1287 background_widget_
->SetBackgroundBounds(background_bounds
, alignment_
);
1288 if (docked_width_
> 0)
1289 background_widget_
->Show();
1291 background_widget_
->Hide();
1294 void DockedWindowLayoutManager::UpdateStacking(aura::Window
* active_window
) {
1295 if (!active_window
) {
1296 if (!last_active_window_
)
1298 active_window
= last_active_window_
;
1301 // Windows are stacked like a deck of cards:
1310 // Use the middle of each window to figure out how to stack the window.
1311 // This allows us to update the stacking when a window is being dragged around
1313 std::map
<int, aura::Window
*> window_ordering
;
1314 for (aura::Window::Windows::const_iterator it
=
1315 dock_container_
->children().begin();
1316 it
!= dock_container_
->children().end(); ++it
) {
1317 if (!IsUsedByLayout(*it
) ||
1318 ((*it
) == dragged_window_
&& !is_dragged_window_docked_
)) {
1321 gfx::Rect bounds
= (*it
)->bounds();
1322 window_ordering
.insert(std::make_pair(bounds
.y() + bounds
.height() / 2,
1325 int active_center_y
= active_window
->bounds().CenterPoint().y();
1327 aura::Window
* previous_window
= NULL
;
1328 for (std::map
<int, aura::Window
*>::const_iterator it
=
1329 window_ordering
.begin();
1330 it
!= window_ordering
.end() && it
->first
< active_center_y
; ++it
) {
1331 if (previous_window
)
1332 dock_container_
->StackChildAbove(it
->second
, previous_window
);
1333 previous_window
= it
->second
;
1335 for (std::map
<int, aura::Window
*>::const_reverse_iterator it
=
1336 window_ordering
.rbegin();
1337 it
!= window_ordering
.rend() && it
->first
> active_center_y
; ++it
) {
1338 if (previous_window
)
1339 dock_container_
->StackChildAbove(it
->second
, previous_window
);
1340 previous_window
= it
->second
;
1343 if (previous_window
&& active_window
->parent() == dock_container_
)
1344 dock_container_
->StackChildAbove(active_window
, previous_window
);
1345 if (active_window
!= dragged_window_
)
1346 last_active_window_
= active_window
;
1349 ////////////////////////////////////////////////////////////////////////////////
1350 // keyboard::KeyboardControllerObserver implementation:
1352 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
1353 const gfx::Rect
& keyboard_bounds
) {
1354 // This bounds change will have caused a change to the Shelf which does not
1355 // propagate automatically to this class, so manually recalculate bounds.
1357 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING
);