Roll src/third_party/skia fd0ecf4:d803cda
[chromium-blink-merge.git] / ash / wm / dock / docked_window_layout_manager.cc
blob91fe6c83ab798a45f4560b725463bf5d9ffe2bfe
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"
43 namespace ash {
45 // Minimum, maximum width of the dock area and a width of the gap
46 // static
47 const int DockedWindowLayoutManager::kMaxDockWidth = 360;
48 // static
49 const int DockedWindowLayoutManager::kMinDockWidth = 200;
50 // static
51 const int DockedWindowLayoutManager::kMinDockGap = 2;
52 // static
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 {
61 public:
62 explicit DockedBackgroundWidget(aura::Window* container)
63 : alignment_(DOCKED_ALIGNMENT_NONE),
64 background_animator_(this, 0, kShelfBackgroundAlpha),
65 alpha_(0),
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) {
74 SetBounds(bounds);
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;
86 if (IsVisible())
87 UpdateBackground();
90 // views::Widget:
91 void OnNativeWidgetVisibilityChanged(bool visible) override {
92 views::Widget::OnNativeWidgetVisibilityChanged(visible);
93 UpdateBackground();
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());
102 SkPaint paint;
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()
108 : 0,
109 0, shelf_background.width(), rect.height(), false, paint);
110 recorder.canvas()->DrawImageInt(
111 shelf_background,
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 {
120 alpha_ = alpha;
121 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
124 private:
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);
134 Init(params);
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_);
141 Hide();
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,
176 change_type);
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.
186 int alpha_;
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);
205 namespace {
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);
250 return width;
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());
261 if (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();
269 if (minimum_height)
270 target_height = std::max(target_height, minimum_height);
271 if (maximum_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
288 // swap positions.
289 struct CompareWindowPos {
290 CompareWindowPos(aura::Window* dragged_window,
291 aura::Window* docked_container,
292 float delta)
293 : dragged_window_(dragged_window),
294 docked_container_(docked_container),
295 delta_(delta / 2) {}
297 bool operator()(WindowWithHeight window_with_height1,
298 WindowWithHeight window_with_height2) {
299 // Use target coordinates since animations may be active when windows are
300 // reordered.
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
312 // window.
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
333 // is stable.
334 bool result1 = compare_bounds(bounds1, bounds2);
335 bool result2 = compare_bounds(bounds2, bounds1);
336 if (result1 != result2)
337 return result1;
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();
344 private:
345 aura::Window* dragged_window_;
346 aura::Window* docked_container_;
347 float delta_;
350 } // namespace
352 ////////////////////////////////////////////////////////////////////////////////
353 // A class that observes shelf for bounds changes.
354 class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
355 public:
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()
361 ->AddObserver(this);
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_;
384 private:
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),
398 in_layout_(false),
399 dragged_window_(NULL),
400 is_dragged_window_docked_(false),
401 is_dragged_from_dock_(false),
402 shelf_(NULL),
403 workspace_controller_(workspace_controller),
404 in_fullscreen_(workspace_controller_->GetWindowState() ==
405 WORKSPACE_WINDOW_STATE_FULL_SCREEN),
406 docked_width_(0),
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())->
415 AddObserver(this);
416 Shell::GetInstance()->AddShellObserver(this);
419 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
420 Shutdown();
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();
430 shelf_ = NULL;
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);
494 Relayout();
497 void DockedWindowLayoutManager::UndockDraggedWindow() {
498 DCHECK(!IsPopupOrTransient(dragged_window_));
499 OnDraggedWindowUndocked();
500 Relayout();
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;
519 } else {
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();
531 Relayout();
532 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
533 RecordUmaAction(action, source);
536 void DockedWindowLayoutManager::SetShelf(Shelf* shelf) {
537 DCHECK(!shelf_);
538 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).
555 return alignment_;
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))
582 return alignment_;
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
594 // the drag.
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)
599 return false;
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))) {
610 return false;
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())
617 return false;
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) {
623 return false;
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)) {
637 return false;
639 return true;
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)) {
652 return;
654 alignment_ = alignment;
656 Relayout();
657 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
660 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
661 Relayout();
662 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
665 ////////////////////////////////////////////////////////////////////////////////
666 // DockedWindowLayoutManager, aura::LayoutManager implementation:
667 void DockedWindowLayoutManager::OnWindowResized() {
668 MaybeMinimizeChildrenExcept(dragged_window_);
669 Relayout();
670 // When screen resizes update the insets even when dock width or alignment
671 // does not change.
672 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED);
675 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
676 if (IsPopupOrTransient(child))
677 return;
678 // Dragged windows are already observed by StartDragging and do not change
679 // docked alignment during the drag.
680 if (child == dragged_window_)
681 return;
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);
693 Relayout();
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))
704 return;
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_)
709 return;
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);
719 Relayout();
720 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
723 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
724 aura::Window* child,
725 bool visible) {
726 if (IsPopupOrTransient(child))
727 return;
729 wm::WindowState* window_state = wm::GetWindowState(child);
730 if (visible && window_state->IsMinimized())
731 window_state->Restore();
732 Relayout();
733 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
736 void DockedWindowLayoutManager::SetChildBounds(
737 aura::Window* child,
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))
750 return;
751 // Whenever one of our windows is moved or resized enforce layout.
752 ShelfLayoutManager* shelf_layout =
753 ShelfLayoutManager::ForShelf(dock_container_);
754 if (shelf_layout)
755 shelf_layout->UpdateVisibilityState();
758 ////////////////////////////////////////////////////////////////////////////////
759 // DockedWindowLayoutManager, ash::ShellObserver implementation:
761 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
762 Relayout();
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)
770 return;
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))
784 continue;
785 wm::WindowState* window_state = wm::GetWindowState(window);
786 if (in_fullscreen_) {
787 if (window->IsVisible())
788 MinimizeDockedWindow(window_state);
789 } else {
790 if (!window_state->IsMinimized())
791 RestoreDockedWindow(window_state);
795 Relayout();
796 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
799 void DockedWindowLayoutManager::OnShelfAlignmentChanged(
800 aura::Window* root_window) {
801 if (dock_container_->GetRootWindow() != root_window)
802 return;
804 if (!shelf_ || !shelf_->shelf_widget())
805 return;
807 if (alignment_ == DOCKED_ALIGNMENT_NONE)
808 return;
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;
820 Relayout();
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))
840 return;
841 // The window property will still be set, but no actual change will occur
842 // until OnFullscreenStateChange is called when exiting fullscreen.
843 if (in_fullscreen_)
844 return;
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_);
850 else
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_)
871 Relayout();
874 void DockedWindowLayoutManager::OnWindowVisibilityChanging(
875 aura::Window* window, bool visible) {
876 if (IsPopupOrTransient(window))
877 return;
878 int animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT;
879 if (visible) {
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
903 // implementation:
905 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
906 aura::Window* lost_active) {
907 if (gained_active && IsPopupOrTransient(gained_active))
908 return;
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_) {
914 ancestor = parent;
915 break;
918 if (ancestor)
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;
932 if (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))
940 continue;
941 int room_needed = GetWindowHeightCloseTo(window, 0) +
942 (gap_needed ? kMinDockGap : 0);
943 gap_needed = true;
944 if (available_room > room_needed) {
945 available_room -= room_needed;
946 } else {
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
973 // of ordering.
974 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
975 dock_container_);
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_);
982 return;
984 gfx::Rect bounds(window->bounds());
985 bounds.set_y(work_area.bottom());
986 window->SetBounds(bounds);
987 window->Show();
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)
995 return;
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(),
1005 100);
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))
1014 continue;
1015 docked_all_count++;
1016 if (!IsUsedByLayout(window))
1017 continue;
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)
1036 return;
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() {
1065 if (in_layout_)
1066 return;
1067 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_)
1068 return;
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_)
1078 continue;
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.
1085 window->Hide();
1086 continue;
1088 if (window->HasFocus() ||
1089 window->Contains(
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,
1109 &visible_windows);
1110 FanOutChildren(work_area,
1111 CalculateIdealWidth(visible_windows),
1112 available_room,
1113 &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(
1140 iter->window(),
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);
1173 return ideal_width;
1176 void DockedWindowLayoutManager::FanOutChildren(
1177 const gfx::Rect& work_area,
1178 int ideal_docked_width,
1179 int available_room,
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
1191 // docked window.
1192 int new_width = ideal_docked_width;
1193 if (visible_windows->empty() ||
1194 (visible_windows->size() == 1 &&
1195 (*visible_windows)[0].window() == dragged_window_)) {
1196 new_width = 0;
1198 UpdateDockedWidth(new_width);
1199 // Sort windows by their center positions and fan out overlapping
1200 // windows.
1201 std::sort(visible_windows->begin(), visible_windows->end(),
1202 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : NULL,
1203 dock_container_,
1204 delta));
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
1212 // existing size.
1213 // This also enforces the min / max restrictions on the docked area width.
1214 bounds.set_width(GetWindowWidthCloseTo(
1215 window,
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);
1237 break;
1238 case DOCKED_ALIGNMENT_RIGHT:
1239 bounds.set_x(dock_bounds.right() -
1240 (ideal_docked_width + bounds.width()) / 2);
1241 break;
1242 case DOCKED_ALIGNMENT_NONE:
1243 break;
1245 if (window == dragged_window_) {
1246 dragged_bounds_ = bounds;
1247 continue;
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(),
1275 dock_inset,
1276 work_area.height());
1277 docked_bounds_ = bounds +
1278 dock_container_->GetBoundsInScreen().OffsetFromOrigin();
1279 FOR_EACH_OBSERVER(
1280 DockedWindowLayoutManagerObserver,
1281 observer_list_,
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();
1290 else
1291 background_widget_->Hide();
1294 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
1295 if (!active_window) {
1296 if (!last_active_window_)
1297 return;
1298 active_window = last_active_window_;
1301 // Windows are stacked like a deck of cards:
1302 // ,------.
1303 // |,------.|
1304 // |,------.|
1305 // | active |
1306 // | window |
1307 // |`------'|
1308 // |`------'|
1309 // `------'
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
1312 // by the titlebar.
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_)) {
1319 continue;
1321 gfx::Rect bounds = (*it)->bounds();
1322 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2,
1323 *it));
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.
1356 Relayout();
1357 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING);
1360 } // namespace ash