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/ash_switches.h"
8 #include "ash/screen_util.h"
9 #include "ash/shelf/shelf.h"
10 #include "ash/shelf/shelf_constants.h"
11 #include "ash/shelf/shelf_layout_manager.h"
12 #include "ash/shelf/shelf_types.h"
13 #include "ash/shelf/shelf_widget.h"
14 #include "ash/shell.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/wm/coordinate_conversion.h"
17 #include "ash/wm/window_animations.h"
18 #include "ash/wm/window_properties.h"
19 #include "ash/wm/window_resizer.h"
20 #include "ash/wm/window_state.h"
21 #include "ash/wm/window_util.h"
22 #include "ash/wm/workspace_controller.h"
23 #include "base/auto_reset.h"
24 #include "base/command_line.h"
25 #include "base/metrics/histogram.h"
26 #include "grit/ash_resources.h"
27 #include "third_party/skia/include/core/SkColor.h"
28 #include "third_party/skia/include/core/SkPaint.h"
29 #include "ui/aura/client/activation_client.h"
30 #include "ui/aura/client/focus_client.h"
31 #include "ui/aura/client/window_tree_client.h"
32 #include "ui/aura/root_window.h"
33 #include "ui/aura/window.h"
34 #include "ui/aura/window_delegate.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/compositor/scoped_layer_animation_settings.h"
37 #include "ui/gfx/canvas.h"
38 #include "ui/gfx/image/image_skia_operations.h"
39 #include "ui/gfx/rect.h"
40 #include "ui/views/background.h"
41 #include "ui/views/corewm/window_util.h"
46 // Minimum, maximum width of the dock area and a width of the gap
48 const int DockedWindowLayoutManager::kMaxDockWidth = 360;
50 const int DockedWindowLayoutManager::kMinDockWidth = 200;
52 const int DockedWindowLayoutManager::kMinDockGap = 2;
54 const int DockedWindowLayoutManager::kIdealWidth = 250;
55 const int kMinimumHeight = 250;
56 const int kSlideDurationMs = 120;
57 const int kFadeDurationMs = 60;
58 const int kMinimizeDurationMs = 720;
60 class DockedBackgroundWidget : public views::Widget,
61 public internal::BackgroundAnimatorDelegate {
63 explicit DockedBackgroundWidget(aura::Window* container)
64 : alignment_(DOCKED_ALIGNMENT_NONE),
65 background_animator_(this, 0, kShelfBackgroundAlpha),
67 opaque_background_(ui::LAYER_SOLID_COLOR),
68 visible_background_type_(SHELF_BACKGROUND_DEFAULT),
69 visible_background_change_type_(BACKGROUND_CHANGE_IMMEDIATE) {
70 InitWidget(container);
73 // Sets widget bounds and sizes opaque background layer to fill the widget.
74 void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) {
76 opaque_background_.SetBounds(gfx::Rect(bounds.size()));
77 alignment_ = alignment;
80 // Sets the background type. Starts an animation to transition to
81 // |background_type| if the widget is visible. If the widget is not visible,
82 // the animation is postponed till the widget becomes visible.
83 void SetBackgroundType(ShelfBackgroundType background_type,
84 BackgroundAnimatorChangeType change_type) {
85 visible_background_type_ = background_type;
86 visible_background_change_type_ = change_type;
92 virtual void OnNativeWidgetVisibilityChanged(bool visible) OVERRIDE {
93 views::Widget::OnNativeWidgetVisibilityChanged(visible);
97 virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE {
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 canvas->DrawImageInt(shelf_background,
107 shelf_background.width(),
108 shelf_background.height(),
109 alignment_ == DOCKED_ALIGNMENT_LEFT
110 ? rect.width() - shelf_background.width()
113 shelf_background.width(),
117 canvas->DrawImageInt(
119 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width() - 1,
122 shelf_background.height(),
123 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width(),
125 rect.width() - shelf_background.width(),
131 // BackgroundAnimatorDelegate:
132 virtual void UpdateBackground(int alpha) OVERRIDE {
134 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
138 void InitWidget(aura::Window* parent) {
139 views::Widget::InitParams params;
140 params.type = views::Widget::InitParams::TYPE_POPUP;
141 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
142 params.can_activate = false;
143 params.keep_on_top = false;
144 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
145 params.parent = parent;
146 params.accept_events = false;
147 set_focus_on_creation(false);
149 SetVisibilityChangedAnimationsEnabled(false);
150 GetNativeWindow()->SetProperty(internal::kStayInSameRootWindowKey, true);
151 opaque_background_.SetColor(SK_ColorBLACK);
152 opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
153 opaque_background_.SetOpacity(0.0f);
154 GetNativeWindow()->layer()->Add(&opaque_background_);
157 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
158 gfx::ImageSkia shelf_background =
159 *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_BACKGROUND);
160 shelf_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage(
161 shelf_background, SkBitmapOperations::ROTATION_90_CW);
162 shelf_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage(
163 shelf_background, SkBitmapOperations::ROTATION_270_CW);
166 // Transitions to |visible_background_type_| if the widget is visible and to
167 // SHELF_BACKGROUND_DEFAULT if it is not.
168 void UpdateBackground() {
169 ShelfBackgroundType background_type = IsVisible() ?
170 visible_background_type_ : SHELF_BACKGROUND_DEFAULT;
171 BackgroundAnimatorChangeType change_type = IsVisible() ?
172 visible_background_change_type_ : BACKGROUND_CHANGE_IMMEDIATE;
174 float target_opacity =
175 (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
176 scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
177 if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
178 opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
179 opaque_background_.GetAnimator()));
180 opaque_background_animation->SetTransitionDuration(
181 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
183 opaque_background_.SetOpacity(target_opacity);
185 // TODO(varkha): use ui::Layer on both opaque_background and normal
186 // background retire background_animator_ at all. It would be simpler.
187 // See also ShelfWidget::SetPaintsBackground.
188 background_animator_.SetPaintsBackground(
189 background_type != SHELF_BACKGROUND_DEFAULT,
191 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
194 DockedAlignment alignment_;
196 // The animator for the background transitions.
197 internal::BackgroundAnimator background_animator_;
199 // The alpha to use for drawing image assets covering the docked background.
202 // Solid black background that can be made fully opaque.
203 ui::Layer opaque_background_;
205 // Backgrounds created from shelf background by 90 or 270 degree rotation.
206 gfx::ImageSkia shelf_background_left_;
207 gfx::ImageSkia shelf_background_right_;
209 // The background type to use when the widget is visible. When not visible,
210 // the widget uses SHELF_BACKGROUND_DEFAULT.
211 ShelfBackgroundType visible_background_type_;
213 // Whether the widget should animate to |visible_background_type_|.
214 BackgroundAnimatorChangeType visible_background_change_type_;
216 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
221 // Returns true if a window is a popup or a transient child.
222 bool IsPopupOrTransient(const aura::Window* window) {
223 return (window->type() == ui::wm::WINDOW_TYPE_POPUP ||
224 views::corewm::GetTransientParent(window));
227 // Certain windows (minimized, hidden or popups) do not matter to docking.
228 bool IsUsedByLayout(const aura::Window* window) {
229 return (window->IsVisible() &&
230 !wm::GetWindowState(window)->IsMinimized() &&
231 !IsPopupOrTransient(window));
234 void UndockWindow(aura::Window* window) {
235 gfx::Rect previous_bounds = window->bounds();
236 aura::Window* old_parent = window->parent();
237 aura::client::ParentWindowWithContext(window, window, gfx::Rect());
238 if (window->parent() != old_parent)
239 wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent());
240 // Start maximize or fullscreen (affecting packaged apps) animation from
241 // previous window bounds.
242 window->layer()->SetBounds(previous_bounds);
245 // Returns width that is as close as possible to |target_width| while being
246 // consistent with docked min and max restrictions and respects the |window|'s
247 // minimum and maximum size.
248 int GetWindowWidthCloseTo(const aura::Window* window, int target_width) {
249 if (!wm::GetWindowState(window)->CanResize()) {
250 DCHECK_LE(window->bounds().width(),
251 DockedWindowLayoutManager::kMaxDockWidth);
252 return window->bounds().width();
254 int width = std::max(DockedWindowLayoutManager::kMinDockWidth,
255 std::min(target_width,
256 DockedWindowLayoutManager::kMaxDockWidth));
257 if (window->delegate()) {
258 if (window->delegate()->GetMinimumSize().width() != 0)
259 width = std::max(width, window->delegate()->GetMinimumSize().width());
260 if (window->delegate()->GetMaximumSize().width() != 0)
261 width = std::min(width, window->delegate()->GetMaximumSize().width());
263 DCHECK_LE(width, DockedWindowLayoutManager::kMaxDockWidth);
267 // Returns height that is as close as possible to |target_height| while
268 // respecting the |window|'s minimum and maximum size.
269 int GetWindowHeightCloseTo(const aura::Window* window, int target_height) {
270 if (!wm::GetWindowState(window)->CanResize())
271 return window->bounds().height();
272 int minimum_height = kMinimumHeight;
273 int maximum_height = 0;
274 const aura::WindowDelegate* delegate(window->delegate());
276 if (delegate->GetMinimumSize().height() != 0) {
277 minimum_height = std::max(kMinimumHeight,
278 delegate->GetMinimumSize().height());
280 if (delegate->GetMaximumSize().height() != 0)
281 maximum_height = delegate->GetMaximumSize().height();
284 target_height = std::max(target_height, minimum_height);
286 target_height = std::min(target_height, maximum_height);
287 return target_height;
290 // A functor used to sort the windows in order of their minimum height.
291 struct CompareMinimumHeight {
292 bool operator()(WindowWithHeight win1, WindowWithHeight win2) {
293 return GetWindowHeightCloseTo(win1.window(), 0) <
294 GetWindowHeightCloseTo(win2.window(), 0);
298 // A functor used to sort the windows in order of their center Y position.
299 // |delta| is a pre-calculated distance from the bottom of one window to the top
300 // of the next. Its value can be positive (gap) or negative (overlap).
301 // Half of |delta| is used as a transition point at which windows could ideally
303 struct CompareWindowPos {
304 CompareWindowPos(aura::Window* dragged_window, float delta)
305 : dragged_window_(dragged_window),
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 win1->parent(), win1->GetTargetBounds());
316 gfx::Rect win2_bounds = ScreenUtil::ConvertRectToScreen(
317 win2->parent(), 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_;
362 ////////////////////////////////////////////////////////////////////////////////
363 // A class that observes shelf for bounds changes.
364 class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
366 explicit ShelfWindowObserver(
367 DockedWindowLayoutManager* docked_layout_manager)
368 : docked_layout_manager_(docked_layout_manager) {
369 DCHECK(docked_layout_manager_->shelf()->shelf_widget());
370 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
374 virtual ~ShelfWindowObserver() {
375 if (docked_layout_manager_->shelf() &&
376 docked_layout_manager_->shelf()->shelf_widget())
377 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
378 ->RemoveObserver(this);
381 // aura::WindowObserver:
382 virtual void OnWindowBoundsChanged(aura::Window* window,
383 const gfx::Rect& old_bounds,
384 const gfx::Rect& new_bounds) OVERRIDE {
385 shelf_bounds_in_screen_ = ScreenUtil::ConvertRectToScreen(
386 window->parent(), new_bounds);
387 docked_layout_manager_->OnShelfBoundsChanged();
390 const gfx::Rect& shelf_bounds_in_screen() const {
391 return shelf_bounds_in_screen_;
395 DockedWindowLayoutManager* docked_layout_manager_;
396 gfx::Rect shelf_bounds_in_screen_;
398 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
401 ////////////////////////////////////////////////////////////////////////////////
402 // DockedWindowLayoutManager public implementation:
403 DockedWindowLayoutManager::DockedWindowLayoutManager(
404 aura::Window* dock_container, WorkspaceController* workspace_controller)
405 : dock_container_(dock_container),
407 dragged_window_(NULL),
408 is_dragged_window_docked_(false),
409 is_dragged_from_dock_(false),
411 workspace_controller_(workspace_controller),
412 in_fullscreen_(workspace_controller_->GetWindowState() ==
413 WORKSPACE_WINDOW_STATE_FULL_SCREEN),
415 alignment_(DOCKED_ALIGNMENT_NONE),
416 last_active_window_(NULL),
417 last_action_time_(base::Time::Now()),
418 background_widget_(new DockedBackgroundWidget(dock_container_)) {
419 DCHECK(dock_container);
420 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
422 Shell::GetInstance()->AddShellObserver(this);
425 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
429 void DockedWindowLayoutManager::Shutdown() {
430 if (shelf_ && shelf_->shelf_widget()) {
431 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
432 shelf_->shelf_widget()->GetNativeWindow());
433 shelf_layout_manager->RemoveObserver(this);
434 shelf_observer_.reset();
437 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
438 aura::Window* child = dock_container_->children()[i];
439 child->RemoveObserver(this);
440 wm::GetWindowState(child)->RemoveObserver(this);
442 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
443 RemoveObserver(this);
444 Shell::GetInstance()->RemoveShellObserver(this);
447 void DockedWindowLayoutManager::AddObserver(
448 DockedWindowLayoutManagerObserver* observer) {
449 observer_list_.AddObserver(observer);
452 void DockedWindowLayoutManager::RemoveObserver(
453 DockedWindowLayoutManagerObserver* observer) {
454 observer_list_.RemoveObserver(observer);
457 void DockedWindowLayoutManager::StartDragging(aura::Window* window) {
458 DCHECK(!dragged_window_);
459 dragged_window_ = window;
460 DCHECK(!IsPopupOrTransient(window));
461 // Start observing a window unless it is docked container's child in which
462 // case it is already observed.
463 wm::WindowState* dragged_state = wm::GetWindowState(dragged_window_);
464 if (dragged_window_->parent() != dock_container_) {
465 dragged_window_->AddObserver(this);
466 dragged_state->AddObserver(this);
467 } else if (!IsAnyWindowDocked() &&
468 dragged_state->drag_details() &&
469 !(dragged_state->drag_details()->bounds_change &
470 WindowResizer::kBoundsChange_Resizes)) {
471 // If there are no other docked windows clear alignment when a docked window
472 // is moved (but not when it is resized or the window could get undocked
473 // when resized away from the edge while docked).
474 alignment_ = DOCKED_ALIGNMENT_NONE;
476 is_dragged_from_dock_ = window->parent() == dock_container_;
477 DCHECK(!is_dragged_window_docked_);
479 // Resize all windows that are flush with the dock edge together if one of
480 // them gets resized.
481 if (dragged_window_->bounds().width() == docked_width_ &&
482 (dragged_state->drag_details()->bounds_change &
483 WindowResizer::kBoundsChange_Resizes) &&
484 (dragged_state->drag_details()->size_change_direction &
485 WindowResizer::kBoundsChangeDirection_Horizontal)) {
486 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
487 aura::Window* window1(dock_container_->children()[i]);
488 if (IsUsedByLayout(window1) &&
489 window1 != dragged_window_ &&
490 window1->bounds().width() == docked_width_) {
491 wm::GetWindowState(window1)->set_bounds_changed_by_user(false);
497 void DockedWindowLayoutManager::DockDraggedWindow(aura::Window* window) {
498 DCHECK(!IsPopupOrTransient(window));
499 OnDraggedWindowDocked(window);
503 void DockedWindowLayoutManager::UndockDraggedWindow() {
504 DCHECK(!IsPopupOrTransient(dragged_window_));
505 OnDraggedWindowUndocked();
507 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
508 is_dragged_from_dock_ = false;
511 void DockedWindowLayoutManager::FinishDragging(DockedAction action,
512 DockedActionSource source) {
513 DCHECK(dragged_window_);
514 DCHECK(!IsPopupOrTransient(dragged_window_));
515 if (is_dragged_window_docked_)
516 OnDraggedWindowUndocked();
517 DCHECK (!is_dragged_window_docked_);
518 // Stop observing a window unless it is docked container's child in which
519 // case it needs to keep being observed after the drag completes.
520 if (dragged_window_->parent() != dock_container_) {
521 dragged_window_->RemoveObserver(this);
522 wm::GetWindowState(dragged_window_)->RemoveObserver(this);
523 if (last_active_window_ == dragged_window_)
524 last_active_window_ = NULL;
526 // If this is the first window that got docked by a move update alignment.
527 if (alignment_ == DOCKED_ALIGNMENT_NONE)
528 alignment_ = GetEdgeNearestWindow(dragged_window_);
529 // A window is no longer dragged and is a child.
530 // When a window becomes a child at drag start this is
531 // the only opportunity we will have to enforce a window
532 // count limit so do it here.
533 MaybeMinimizeChildrenExcept(dragged_window_);
535 dragged_window_ = NULL;
536 dragged_bounds_ = gfx::Rect();
538 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
539 RecordUmaAction(action, source);
542 void DockedWindowLayoutManager::SetShelf(Shelf* shelf) {
545 if (shelf_->shelf_widget()) {
546 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
547 shelf_->shelf_widget()->GetNativeWindow());
548 shelf_layout_manager->AddObserver(this);
549 shelf_observer_.reset(new ShelfWindowObserver(this));
553 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
554 const aura::Window* window) const {
555 const gfx::Rect& bounds(window->GetBoundsInScreen());
557 // Test overlap with an existing docked area first.
558 if (docked_bounds_.Intersects(bounds) &&
559 alignment_ != DOCKED_ALIGNMENT_NONE) {
560 // A window is being added to other docked windows (on the same side).
564 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
565 if (bounds.x() <= container_bounds.x() &&
566 bounds.right() > container_bounds.x()) {
567 return DOCKED_ALIGNMENT_LEFT;
568 } else if (bounds.x() < container_bounds.right() &&
569 bounds.right() >= container_bounds.right()) {
570 return DOCKED_ALIGNMENT_RIGHT;
572 return DOCKED_ALIGNMENT_NONE;
575 DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const {
576 // Find a child that is not being dragged and is not a popup.
577 // If such exists the current alignment is returned - even if some of the
578 // children are hidden or minimized (so they can be restored without losing
579 // the docked state).
580 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
581 aura::Window* window(dock_container_->children()[i]);
582 if (window != dragged_window_ && !IsPopupOrTransient(window))
585 // No docked windows remain other than possibly the window being dragged.
586 // Return |NONE| to indicate that windows may get docked on either side.
587 return DOCKED_ALIGNMENT_NONE;
590 bool DockedWindowLayoutManager::CanDockWindow(aura::Window* window,
592 if (!switches::UseDockedWindows())
594 // Don't allow interactive docking of windows with transient parents such as
595 // modal browser dialogs. Prevent docking of panels attached to shelf during
597 wm::WindowState* window_state = wm::GetWindowState(window);
598 bool should_attach_to_shelf = window_state->drag_details() &&
599 window_state->drag_details()->should_attach_to_shelf;
600 if (IsPopupOrTransient(window) || should_attach_to_shelf)
602 // If a window is wide and cannot be resized down to maximum width allowed
603 // then it cannot be docked.
604 // TODO(varkha). Prevent windows from changing size programmatically while
605 // they are docked. The size will take effect only once a window is undocked.
606 // See http://crbug.com/307792.
607 if (window->bounds().width() > kMaxDockWidth &&
608 (!window_state->CanResize() ||
609 (window->delegate() &&
610 window->delegate()->GetMinimumSize().width() != 0 &&
611 window->delegate()->GetMinimumSize().width() > kMaxDockWidth))) {
614 // If a window is tall and cannot be resized down to maximum height allowed
615 // then it cannot be docked.
616 const gfx::Rect work_area =
617 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
618 if (GetWindowHeightCloseTo(window, work_area.height()) > work_area.height())
620 // Cannot dock on the other size from an existing dock.
621 const DockedAlignment alignment = CalculateAlignment();
622 if ((edge == SNAP_LEFT && alignment == DOCKED_ALIGNMENT_RIGHT) ||
623 (edge == SNAP_RIGHT && alignment == DOCKED_ALIGNMENT_LEFT)) {
626 // Do not allow docking on the same side as shelf.
627 ShelfAlignment shelf_alignment = SHELF_ALIGNMENT_BOTTOM;
629 shelf_alignment = shelf_->alignment();
630 if ((edge == SNAP_LEFT && shelf_alignment == SHELF_ALIGNMENT_LEFT) ||
631 (edge == SNAP_RIGHT && shelf_alignment == SHELF_ALIGNMENT_RIGHT)) {
637 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
639 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
642 ////////////////////////////////////////////////////////////////////////////////
643 // DockedWindowLayoutManager, aura::LayoutManager implementation:
644 void DockedWindowLayoutManager::OnWindowResized() {
645 MaybeMinimizeChildrenExcept(dragged_window_);
647 // When screen resizes update the insets even when dock width or alignment
649 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED);
652 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
653 if (IsPopupOrTransient(child))
655 // Dragged windows are already observed by StartDragging and do not change
656 // docked alignment during the drag.
657 if (child == dragged_window_)
659 // If this is the first window getting docked - update alignment.
660 // A window can be added without proper bounds when window is moved to another
661 // display via API or due to display configuration change, so the alignment
662 // is set based on which edge is closer in the new display.
663 if (alignment_ == DOCKED_ALIGNMENT_NONE)
664 alignment_ = GetEdgeNearestWindow(child);
665 MaybeMinimizeChildrenExcept(child);
666 child->AddObserver(this);
667 wm::GetWindowState(child)->AddObserver(this);
669 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
672 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
673 if (IsPopupOrTransient(child))
675 // Dragged windows are stopped being observed by FinishDragging and do not
676 // change alignment during the drag. They also cannot be set to be the
677 // |last_active_window_|.
678 if (child == dragged_window_)
680 // If this is the last window, set alignment and maximize the workspace.
681 if (!IsAnyWindowDocked()) {
682 alignment_ = DOCKED_ALIGNMENT_NONE;
683 UpdateDockedWidth(0);
685 if (last_active_window_ == child)
686 last_active_window_ = NULL;
687 child->RemoveObserver(this);
688 wm::GetWindowState(child)->RemoveObserver(this);
690 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
693 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
696 if (IsPopupOrTransient(child))
699 wm::GetWindowState(child)->Restore();
701 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
704 void DockedWindowLayoutManager::SetChildBounds(
706 const gfx::Rect& requested_bounds) {
707 // Whenever one of our windows is moved or resized enforce layout.
708 SetChildBoundsDirect(child, requested_bounds);
709 if (IsPopupOrTransient(child))
711 ShelfLayoutManager* shelf_layout = internal::ShelfLayoutManager::ForShelf(
714 shelf_layout->UpdateVisibilityState();
717 ////////////////////////////////////////////////////////////////////////////////
718 // DockedWindowLayoutManager, ash::ShellObserver implementation:
720 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
722 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
723 MaybeMinimizeChildrenExcept(dragged_window_);
726 void DockedWindowLayoutManager::OnFullscreenStateChanged(
727 bool is_fullscreen, aura::Window* root_window) {
728 if (dock_container_->GetRootWindow() != root_window)
730 // Entering fullscreen mode (including immersive) hides docked windows.
731 in_fullscreen_ = workspace_controller_->GetWindowState() ==
732 WORKSPACE_WINDOW_STATE_FULL_SCREEN;
734 // prevent Relayout from getting called multiple times during this
735 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
736 // Use a copy of children array because a call to MinimizeDockedWindow or
737 // RestoreDockedWindow can change order.
738 aura::Window::Windows children(dock_container_->children());
739 for (aura::Window::Windows::const_iterator iter = children.begin();
740 iter != children.end(); ++iter) {
741 aura::Window* window(*iter);
742 if (IsPopupOrTransient(window))
744 wm::WindowState* window_state = wm::GetWindowState(window);
745 if (in_fullscreen_) {
746 if (window->IsVisible())
747 MinimizeDockedWindow(window_state);
749 if (!window_state->IsMinimized())
750 RestoreDockedWindow(window_state);
755 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
758 void DockedWindowLayoutManager::OnShelfAlignmentChanged(
759 aura::Window* root_window) {
760 if (dock_container_->GetRootWindow() != root_window)
763 if (!shelf_ || !shelf_->shelf_widget())
766 if (alignment_ == DOCKED_ALIGNMENT_NONE)
769 // Do not allow shelf and dock on the same side. Switch side that
770 // the dock is attached to and move all dock windows to that new side.
771 ShelfAlignment shelf_alignment = shelf_->shelf_widget()->GetAlignment();
772 if (alignment_ == DOCKED_ALIGNMENT_LEFT &&
773 shelf_alignment == SHELF_ALIGNMENT_LEFT) {
774 alignment_ = DOCKED_ALIGNMENT_RIGHT;
775 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT &&
776 shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
777 alignment_ = DOCKED_ALIGNMENT_LEFT;
780 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED);
783 /////////////////////////////////////////////////////////////////////////////
784 // DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
785 void DockedWindowLayoutManager::OnBackgroundUpdated(
786 ShelfBackgroundType background_type,
787 BackgroundAnimatorChangeType change_type) {
788 background_widget_->SetBackgroundType(background_type, change_type);
791 /////////////////////////////////////////////////////////////////////////////
792 // DockedWindowLayoutManager, WindowStateObserver implementation:
794 void DockedWindowLayoutManager::OnWindowShowTypeChanged(
795 wm::WindowState* window_state,
796 wm::WindowShowType old_type) {
797 aura::Window* window = window_state->window();
798 if (IsPopupOrTransient(window))
800 // The window property will still be set, but no actual change will occur
801 // until OnFullscreenStateChange is called when exiting fullscreen.
804 if (window_state->IsMinimized()) {
805 MinimizeDockedWindow(window_state);
806 } else if (window_state->IsMaximizedOrFullscreen() ||
807 window_state->IsSnapped()) {
808 if (window != dragged_window_) {
809 UndockWindow(window);
810 RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
812 } else if (old_type == wm::SHOW_TYPE_MINIMIZED) {
813 RestoreDockedWindow(window_state);
817 /////////////////////////////////////////////////////////////////////////////
818 // DockedWindowLayoutManager, WindowObserver implementation:
820 void DockedWindowLayoutManager::OnWindowBoundsChanged(
821 aura::Window* window,
822 const gfx::Rect& old_bounds,
823 const gfx::Rect& new_bounds) {
824 // Only relayout if the dragged window would get docked.
825 if (window == dragged_window_ && is_dragged_window_docked_)
829 void DockedWindowLayoutManager::OnWindowVisibilityChanging(
830 aura::Window* window, bool visible) {
831 if (IsPopupOrTransient(window))
833 int animation_type = views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT;
835 animation_type = views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
836 views::corewm::SetWindowVisibilityAnimationDuration(
837 window, base::TimeDelta::FromMilliseconds(kFadeDurationMs));
838 } else if (wm::GetWindowState(window)->IsMinimized()) {
839 animation_type = WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE;
841 views::corewm::SetWindowVisibilityAnimationType(window, animation_type);
844 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
845 if (dragged_window_ == window) {
846 FinishDragging(DOCKED_ACTION_NONE, DOCKED_ACTION_SOURCE_UNKNOWN);
847 DCHECK(!dragged_window_);
848 DCHECK(!is_dragged_window_docked_);
850 if (window == last_active_window_)
851 last_active_window_ = NULL;
852 RecordUmaAction(DOCKED_ACTION_CLOSE, DOCKED_ACTION_SOURCE_UNKNOWN);
856 ////////////////////////////////////////////////////////////////////////////////
857 // DockedWindowLayoutManager, aura::client::ActivationChangeObserver
860 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
861 aura::Window* lost_active) {
862 if (gained_active && IsPopupOrTransient(gained_active))
864 // Ignore if the window that is not managed by this was activated.
865 aura::Window* ancestor = NULL;
866 for (aura::Window* parent = gained_active;
867 parent; parent = parent->parent()) {
868 if (parent->parent() == dock_container_) {
874 UpdateStacking(ancestor);
877 ////////////////////////////////////////////////////////////////////////////////
878 // DockedWindowLayoutManager private implementation:
880 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
881 aura::Window* child) {
882 // Minimize any windows that don't fit without overlap.
883 const gfx::Rect work_area =
884 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
885 int available_room = work_area.height();
886 bool gap_needed = !!child;
888 available_room -= GetWindowHeightCloseTo(child, 0);
889 // Use a copy of children array because a call to Minimize can change order.
890 aura::Window::Windows children(dock_container_->children());
891 aura::Window::Windows::const_reverse_iterator iter = children.rbegin();
892 while (iter != children.rend()) {
893 aura::Window* window(*iter++);
894 if (window == child || !IsUsedByLayout(window))
896 int room_needed = GetWindowHeightCloseTo(window, 0) +
897 (gap_needed ? kMinDockGap : 0);
899 if (available_room > room_needed) {
900 available_room -= room_needed;
902 // Slow down minimizing animations. Lock duration so that it is not
903 // overridden by other ScopedLayerAnimationSettings down the stack.
904 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
905 settings.SetTransitionDuration(
906 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
907 settings.LockTransitionDuration();
908 wm::GetWindowState(window)->Minimize();
913 void DockedWindowLayoutManager::MinimizeDockedWindow(
914 wm::WindowState* window_state) {
915 DCHECK(!IsPopupOrTransient(window_state->window()));
916 window_state->window()->Hide();
917 if (window_state->IsActive())
918 window_state->Deactivate();
919 RecordUmaAction(DOCKED_ACTION_MINIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
922 void DockedWindowLayoutManager::RestoreDockedWindow(
923 wm::WindowState* window_state) {
924 aura::Window* window = window_state->window();
925 DCHECK(!IsPopupOrTransient(window));
926 // Always place restored window at the bottom shuffling the other windows up.
927 // TODO(varkha): add a separate container for docked windows to keep track
929 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
931 const gfx::Rect work_area = display.work_area();
933 // Evict the window if it can no longer be docked because of its height.
934 if (!CanDockWindow(window, SNAP_NONE)) {
935 UndockWindow(window);
936 RecordUmaAction(DOCKED_ACTION_EVICT, DOCKED_ACTION_SOURCE_UNKNOWN);
939 gfx::Rect bounds(window->bounds());
940 bounds.set_y(work_area.bottom());
941 window->SetBounds(bounds);
943 MaybeMinimizeChildrenExcept(window);
944 RecordUmaAction(DOCKED_ACTION_RESTORE, DOCKED_ACTION_SOURCE_UNKNOWN);
947 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action,
948 DockedActionSource source) {
949 if (action == DOCKED_ACTION_NONE)
951 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action, DOCKED_ACTION_COUNT);
952 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source,
953 DOCKED_ACTION_SOURCE_COUNT);
954 base::Time time_now = base::Time::Now();
955 base::TimeDelta time_between_use = time_now - last_action_time_;
956 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse",
957 time_between_use.InSeconds(),
959 base::TimeDelta::FromHours(10).InSeconds(),
961 last_action_time_ = time_now;
962 int docked_all_count = 0;
963 int docked_visible_count = 0;
964 int docked_panels_count = 0;
965 int large_windows_count = 0;
966 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
967 const aura::Window* window(dock_container_->children()[i]);
968 if (IsPopupOrTransient(window))
971 if (!IsUsedByLayout(window))
973 docked_visible_count++;
974 if (window->type() == ui::wm::WINDOW_TYPE_PANEL)
975 docked_panels_count++;
976 const wm::WindowState* window_state = wm::GetWindowState(window);
977 if (window_state->HasRestoreBounds()) {
978 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen();
979 if (restore_bounds.width() > kMaxDockWidth)
980 large_windows_count++;
983 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count);
984 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count);
985 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count);
986 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count);
989 void DockedWindowLayoutManager::UpdateDockedWidth(int width) {
990 if (docked_width_ == width)
992 docked_width_ = width;
993 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_);
996 void DockedWindowLayoutManager::OnDraggedWindowDocked(aura::Window* window) {
997 DCHECK(!is_dragged_window_docked_);
998 is_dragged_window_docked_ = true;
1001 void DockedWindowLayoutManager::OnDraggedWindowUndocked() {
1002 DCHECK (is_dragged_window_docked_);
1003 is_dragged_window_docked_ = false;
1006 bool DockedWindowLayoutManager::IsAnyWindowDocked() {
1007 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE;
1010 DockedAlignment DockedWindowLayoutManager::GetEdgeNearestWindow(
1011 const aura::Window* window) const {
1012 const gfx::Rect& bounds(window->GetBoundsInScreen());
1013 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
1014 return (abs(bounds.x() - container_bounds.x()) <
1015 abs(bounds.right() - container_bounds.right())) ?
1016 DOCKED_ALIGNMENT_LEFT : DOCKED_ALIGNMENT_RIGHT;
1019 void DockedWindowLayoutManager::Relayout() {
1022 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_)
1024 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
1026 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1027 aura::Window* active_window = NULL;
1028 std::vector<WindowWithHeight> visible_windows;
1029 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
1030 aura::Window* window(dock_container_->children()[i]);
1032 if (!IsUsedByLayout(window) || window == dragged_window_)
1035 // If the shelf is currently hidden (full-screen mode), hide window until
1036 // full-screen mode is exited.
1037 if (in_fullscreen_) {
1038 // The call to Hide does not set the minimize property, so the window will
1039 // be restored when the shelf becomes visible again.
1043 if (window->HasFocus() ||
1045 aura::client::GetFocusClient(window)->GetFocusedWindow())) {
1046 DCHECK(!active_window);
1047 active_window = window;
1049 visible_windows.push_back(WindowWithHeight(window));
1051 // Consider docked dragged_window_ when fanning out other child windows.
1052 if (is_dragged_window_docked_) {
1053 visible_windows.push_back(WindowWithHeight(dragged_window_));
1054 DCHECK(!active_window);
1055 active_window = dragged_window_;
1058 // Position docked windows as well as the window being dragged.
1059 gfx::Rect work_area =
1060 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1061 if (shelf_observer_)
1062 work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
1063 int available_room = CalculateWindowHeightsAndRemainingRoom(work_area,
1065 FanOutChildren(work_area,
1066 CalculateIdealWidth(visible_windows),
1070 // After the first Relayout allow the windows to change their order easier
1071 // since we know they are docked.
1072 is_dragged_from_dock_ = true;
1073 UpdateStacking(active_window);
1076 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom(
1077 const gfx::Rect work_area,
1078 std::vector<WindowWithHeight>* visible_windows) {
1079 int available_room = work_area.height();
1080 int remaining_windows = visible_windows->size();
1081 int gap_height = remaining_windows > 1 ? kMinDockGap : 0;
1083 // Sort windows by their minimum heights and calculate target heights.
1084 std::sort(visible_windows->begin(), visible_windows->end(),
1085 CompareMinimumHeight());
1086 // Distribute the free space among the docked windows. Since the windows are
1087 // sorted (tall windows first) we can now assume that any window which
1088 // required more space than the current window will have already been
1089 // accounted for previously in this loop, so we can safely give that window
1090 // its proportional share of the remaining space.
1091 for (std::vector<WindowWithHeight>::reverse_iterator iter =
1092 visible_windows->rbegin();
1093 iter != visible_windows->rend(); ++iter) {
1094 iter->height_ = GetWindowHeightCloseTo(
1096 (available_room + gap_height) / remaining_windows - gap_height);
1097 available_room -= (iter->height_ + gap_height);
1098 remaining_windows--;
1100 return available_room + gap_height;
1103 int DockedWindowLayoutManager::CalculateIdealWidth(
1104 const std::vector<WindowWithHeight>& visible_windows) {
1105 int smallest_max_width = kMaxDockWidth;
1106 int largest_min_width = kMinDockWidth;
1107 // Ideal width of the docked area is as close to kIdealWidth as possible
1108 // while still respecting the minimum and maximum width restrictions on the
1109 // individual docked windows as well as the width that was possibly set by a
1110 // user (which needs to be preserved when dragging and rearranging windows).
1111 for (std::vector<WindowWithHeight>::const_iterator iter =
1112 visible_windows.begin();
1113 iter != visible_windows.end(); ++iter) {
1114 const aura::Window* window = iter->window();
1115 int min_window_width = window->bounds().width();
1116 int max_window_width = min_window_width;
1117 if (!wm::GetWindowState(window)->bounds_changed_by_user()) {
1118 min_window_width = GetWindowWidthCloseTo(window, kMinDockWidth);
1119 max_window_width = GetWindowWidthCloseTo(window, kMaxDockWidth);
1121 largest_min_width = std::max(largest_min_width, min_window_width);
1122 smallest_max_width = std::min(smallest_max_width, max_window_width);
1124 int ideal_width = std::max(largest_min_width,
1125 std::min(smallest_max_width, kIdealWidth));
1126 // Restrict docked area width regardless of window restrictions.
1127 ideal_width = std::max(std::min(ideal_width, kMaxDockWidth), kMinDockWidth);
1131 void DockedWindowLayoutManager::FanOutChildren(
1132 const gfx::Rect& work_area,
1133 int ideal_docked_width,
1135 std::vector<WindowWithHeight>* visible_windows) {
1136 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1138 // Calculate initial vertical offset and the gap or overlap between windows.
1139 const int num_windows = visible_windows->size();
1140 const float delta = static_cast<float>(available_room) /
1141 ((available_room > 0 || num_windows <= 1) ?
1142 num_windows + 1 : num_windows - 1);
1143 float y_pos = work_area.y() + ((delta > 0) ? delta : 0);
1145 // Docked area is shown only if there is at least one non-dragged visible
1147 int new_width = ideal_docked_width;
1148 if (visible_windows->empty() ||
1149 (visible_windows->size() == 1 &&
1150 (*visible_windows)[0].window() == dragged_window_)) {
1153 UpdateDockedWidth(new_width);
1154 // Sort windows by their center positions and fan out overlapping
1156 std::sort(visible_windows->begin(), visible_windows->end(),
1157 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : NULL,
1159 for (std::vector<WindowWithHeight>::iterator iter = visible_windows->begin();
1160 iter != visible_windows->end(); ++iter) {
1161 aura::Window* window = iter->window();
1162 gfx::Rect bounds = ScreenUtil::ConvertRectToScreen(
1163 window->parent(), window->GetTargetBounds());
1164 // A window is extended or shrunk to be as close as possible to the ideal
1165 // docked area width. Windows that were resized by a user are kept at their
1167 // This also enforces the min / max restrictions on the docked area width.
1168 bounds.set_width(GetWindowWidthCloseTo(
1170 wm::GetWindowState(window)->bounds_changed_by_user() ?
1171 bounds.width() : ideal_docked_width));
1172 DCHECK_LE(bounds.width(), ideal_docked_width);
1174 DockedAlignment alignment = alignment_;
1175 if (alignment == DOCKED_ALIGNMENT_NONE && window == dragged_window_) {
1176 alignment = GetAlignmentOfWindow(window);
1177 if (alignment == DOCKED_ALIGNMENT_NONE)
1178 bounds.set_size(gfx::Size());
1181 // Fan out windows evenly distributing the overlap or remaining free space.
1182 bounds.set_height(iter->height_);
1183 bounds.set_y(std::max(work_area.y(),
1184 std::min(work_area.bottom() - bounds.height(),
1185 static_cast<int>(y_pos + 0.5))));
1186 y_pos += bounds.height() + delta + kMinDockGap;
1188 // All docked windows other than the one currently dragged remain stuck
1189 // to the screen edge (flush with the edge or centered in the dock area).
1190 switch (alignment) {
1191 case DOCKED_ALIGNMENT_LEFT:
1192 bounds.set_x(dock_bounds.x() +
1193 (ideal_docked_width - bounds.width()) / 2);
1195 case DOCKED_ALIGNMENT_RIGHT:
1196 bounds.set_x(dock_bounds.right() -
1197 (ideal_docked_width + bounds.width()) / 2);
1199 case DOCKED_ALIGNMENT_NONE:
1202 if (window == dragged_window_) {
1203 dragged_bounds_ = bounds;
1206 // If the following asserts it is probably because not all the children
1207 // have been removed when dock was closed.
1208 DCHECK_NE(alignment_, DOCKED_ALIGNMENT_NONE);
1209 bounds = ScreenUtil::ConvertRectFromScreen(dock_container_, bounds);
1210 if (bounds != window->GetTargetBounds()) {
1211 ui::Layer* layer = window->layer();
1212 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
1213 slide_settings.SetPreemptionStrategy(
1214 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
1215 slide_settings.SetTransitionDuration(
1216 base::TimeDelta::FromMilliseconds(kSlideDurationMs));
1217 SetChildBoundsDirect(window, bounds);
1222 void DockedWindowLayoutManager::UpdateDockBounds(
1223 DockedWindowLayoutManagerObserver::Reason reason) {
1224 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0);
1225 const gfx::Rect work_area =
1226 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1227 gfx::Rect bounds = gfx::Rect(
1228 alignment_ == DOCKED_ALIGNMENT_RIGHT && dock_inset > 0 ?
1229 dock_container_->bounds().right() - dock_inset:
1230 dock_container_->bounds().x(),
1231 dock_container_->bounds().y(),
1233 work_area.height());
1234 docked_bounds_ = bounds +
1235 dock_container_->GetBoundsInScreen().OffsetFromOrigin();
1237 DockedWindowLayoutManagerObserver,
1239 OnDockBoundsChanging(bounds, reason));
1240 // Show or hide background for docked area.
1241 gfx::Rect background_bounds(docked_bounds_);
1242 if (shelf_observer_)
1243 background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
1244 background_widget_->SetBackgroundBounds(background_bounds, alignment_);
1245 if (docked_width_ > 0)
1246 background_widget_->Show();
1248 background_widget_->Hide();
1251 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
1252 if (!active_window) {
1253 if (!last_active_window_)
1255 active_window = last_active_window_;
1258 // Windows are stacked like a deck of cards:
1267 // Use the middle of each window to figure out how to stack the window.
1268 // This allows us to update the stacking when a window is being dragged around
1270 std::map<int, aura::Window*> window_ordering;
1271 for (aura::Window::Windows::const_iterator it =
1272 dock_container_->children().begin();
1273 it != dock_container_->children().end(); ++it) {
1274 if (!IsUsedByLayout(*it) ||
1275 ((*it) == dragged_window_ && !is_dragged_window_docked_)) {
1278 gfx::Rect bounds = (*it)->bounds();
1279 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2,
1282 int active_center_y = active_window->bounds().CenterPoint().y();
1284 aura::Window* previous_window = NULL;
1285 for (std::map<int, aura::Window*>::const_iterator it =
1286 window_ordering.begin();
1287 it != window_ordering.end() && it->first < active_center_y; ++it) {
1288 if (previous_window)
1289 dock_container_->StackChildAbove(it->second, previous_window);
1290 previous_window = it->second;
1292 for (std::map<int, aura::Window*>::const_reverse_iterator it =
1293 window_ordering.rbegin();
1294 it != window_ordering.rend() && it->first > active_center_y; ++it) {
1295 if (previous_window)
1296 dock_container_->StackChildAbove(it->second, previous_window);
1297 previous_window = it->second;
1300 if (previous_window && active_window->parent() == dock_container_)
1301 dock_container_->StackChildAbove(active_window, previous_window);
1302 if (active_window != dragged_window_)
1303 last_active_window_ = active_window;
1306 ////////////////////////////////////////////////////////////////////////////////
1307 // keyboard::KeyboardControllerObserver implementation:
1309 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
1310 const gfx::Rect& keyboard_bounds) {
1311 // This bounds change will have caused a change to the Shelf which does not
1312 // propagate automatically to this class, so manually recalculate bounds.
1314 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING);
1317 } // namespace internal