Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / ash / wm / dock / docked_window_layout_manager.cc
blobada90f1dfa1829b2011d79c41d9fc1f8d9047671
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 gfx::Rect local_window_bounds(GetWindowBoundsInScreen().size());
98 ui::PaintRecorder recorder(context, local_window_bounds.size());
99 const gfx::ImageSkia& shelf_background(
100 alignment_ == DOCKED_ALIGNMENT_LEFT ?
101 shelf_background_left_ : shelf_background_right_);
102 SkPaint paint;
103 paint.setAlpha(alpha_);
104 recorder.canvas()->DrawImageInt(
105 shelf_background, 0, 0, shelf_background.width(),
106 shelf_background.height(),
107 alignment_ == DOCKED_ALIGNMENT_LEFT
108 ? local_window_bounds.width() - shelf_background.width()
109 : 0,
110 0, shelf_background.width(), local_window_bounds.height(), false,
111 paint);
112 recorder.canvas()->DrawImageInt(
113 shelf_background,
114 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width() - 1,
115 0, 1, shelf_background.height(),
116 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width(), 0,
117 local_window_bounds.width() - shelf_background.width(),
118 local_window_bounds.height(), false, paint);
121 // BackgroundAnimatorDelegate:
122 void UpdateBackground(int alpha) override {
123 alpha_ = alpha;
124 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
127 private:
128 void InitWidget(aura::Window* parent) {
129 views::Widget::InitParams params;
130 params.type = views::Widget::InitParams::TYPE_POPUP;
131 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
132 params.keep_on_top = false;
133 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
134 params.parent = parent;
135 params.accept_events = false;
136 set_focus_on_creation(false);
137 Init(params);
138 SetVisibilityChangedAnimationsEnabled(false);
139 GetNativeWindow()->SetProperty(kStayInSameRootWindowKey, true);
140 opaque_background_.SetColor(SK_ColorBLACK);
141 opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
142 opaque_background_.SetOpacity(0.0f);
143 GetNativeWindow()->layer()->Add(&opaque_background_);
144 Hide();
146 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
147 gfx::ImageSkia shelf_background =
148 *rb.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND);
149 shelf_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage(
150 shelf_background, SkBitmapOperations::ROTATION_90_CW);
151 shelf_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage(
152 shelf_background, SkBitmapOperations::ROTATION_270_CW);
155 // Transitions to |visible_background_type_| if the widget is visible and to
156 // SHELF_BACKGROUND_DEFAULT if it is not.
157 void UpdateBackground() {
158 ShelfBackgroundType background_type = IsVisible() ?
159 visible_background_type_ : SHELF_BACKGROUND_DEFAULT;
160 BackgroundAnimatorChangeType change_type = IsVisible() ?
161 visible_background_change_type_ : BACKGROUND_CHANGE_IMMEDIATE;
163 float target_opacity =
164 (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
165 scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
166 if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
167 opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
168 opaque_background_.GetAnimator()));
169 opaque_background_animation->SetTransitionDuration(
170 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
172 opaque_background_.SetOpacity(target_opacity);
174 // TODO(varkha): use ui::Layer on both opaque_background and normal
175 // background retire background_animator_ at all. It would be simpler.
176 // See also ShelfWidget::SetPaintsBackground.
177 background_animator_.SetPaintsBackground(
178 background_type != SHELF_BACKGROUND_DEFAULT,
179 change_type);
180 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
183 DockedAlignment alignment_;
185 // The animator for the background transitions.
186 BackgroundAnimator background_animator_;
188 // The alpha to use for drawing image assets covering the docked background.
189 int alpha_;
191 // Solid black background that can be made fully opaque.
192 ui::Layer opaque_background_;
194 // Backgrounds created from shelf background by 90 or 270 degree rotation.
195 gfx::ImageSkia shelf_background_left_;
196 gfx::ImageSkia shelf_background_right_;
198 // The background type to use when the widget is visible. When not visible,
199 // the widget uses SHELF_BACKGROUND_DEFAULT.
200 ShelfBackgroundType visible_background_type_;
202 // Whether the widget should animate to |visible_background_type_|.
203 BackgroundAnimatorChangeType visible_background_change_type_;
205 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
208 namespace {
210 // Returns true if a window is a popup or a transient child.
211 bool IsPopupOrTransient(const aura::Window* window) {
212 return (window->type() == ui::wm::WINDOW_TYPE_POPUP ||
213 ::wm::GetTransientParent(window));
216 // Certain windows (minimized, hidden or popups) are not docked and are ignored
217 // by layout logic even when they are children of a docked container.
218 bool IsWindowDocked(const aura::Window* window) {
219 return (window->IsVisible() &&
220 !wm::GetWindowState(window)->IsMinimized() &&
221 !IsPopupOrTransient(window));
224 void UndockWindow(aura::Window* window) {
225 gfx::Rect previous_bounds = window->bounds();
226 aura::Window* old_parent = window->parent();
227 aura::client::ParentWindowWithContext(window, window, gfx::Rect());
228 if (window->parent() != old_parent)
229 wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent());
230 // Start maximize or fullscreen (affecting packaged apps) animation from
231 // previous window bounds.
232 window->layer()->SetBounds(previous_bounds);
235 // Returns width that is as close as possible to |target_width| while being
236 // consistent with docked min and max restrictions and respects the |window|'s
237 // minimum and maximum size.
238 int GetWindowWidthCloseTo(const aura::Window* window, int target_width) {
239 if (!wm::GetWindowState(window)->CanResize()) {
240 DCHECK_LE(window->bounds().width(),
241 DockedWindowLayoutManager::kMaxDockWidth);
242 return window->bounds().width();
244 int width = std::max(DockedWindowLayoutManager::kMinDockWidth,
245 std::min(target_width,
246 DockedWindowLayoutManager::kMaxDockWidth));
247 if (window->delegate()) {
248 if (window->delegate()->GetMinimumSize().width() != 0)
249 width = std::max(width, window->delegate()->GetMinimumSize().width());
250 if (window->delegate()->GetMaximumSize().width() != 0)
251 width = std::min(width, window->delegate()->GetMaximumSize().width());
253 DCHECK_LE(width, DockedWindowLayoutManager::kMaxDockWidth);
254 return width;
257 // Returns height that is as close as possible to |target_height| while
258 // respecting the |window|'s minimum and maximum size.
259 int GetWindowHeightCloseTo(const aura::Window* window, int target_height) {
260 if (!wm::GetWindowState(window)->CanResize())
261 return window->bounds().height();
262 int minimum_height = kMinimumHeight;
263 int maximum_height = 0;
264 const aura::WindowDelegate* delegate(window->delegate());
265 if (delegate) {
266 if (delegate->GetMinimumSize().height() != 0) {
267 minimum_height = std::max(kMinimumHeight,
268 delegate->GetMinimumSize().height());
270 if (delegate->GetMaximumSize().height() != 0)
271 maximum_height = delegate->GetMaximumSize().height();
273 if (minimum_height)
274 target_height = std::max(target_height, minimum_height);
275 if (maximum_height)
276 target_height = std::min(target_height, maximum_height);
277 return target_height;
280 // A functor used to sort the windows in order of their minimum height.
281 struct CompareMinimumHeight {
282 bool operator()(WindowWithHeight win1, WindowWithHeight win2) {
283 return GetWindowHeightCloseTo(win1.window(), 0) <
284 GetWindowHeightCloseTo(win2.window(), 0);
288 // A functor used to sort the windows in order of their center Y position.
289 // |delta| is a pre-calculated distance from the bottom of one window to the top
290 // of the next. Its value can be positive (gap) or negative (overlap).
291 // Half of |delta| is used as a transition point at which windows could ideally
292 // swap positions.
293 struct CompareWindowPos {
294 CompareWindowPos(aura::Window* dragged_window,
295 aura::Window* docked_container,
296 float delta)
297 : dragged_window_(dragged_window),
298 docked_container_(docked_container),
299 delta_(delta / 2) {}
301 bool operator()(WindowWithHeight window_with_height1,
302 WindowWithHeight window_with_height2) {
303 // Use target coordinates since animations may be active when windows are
304 // reordered.
305 aura::Window* win1(window_with_height1.window());
306 aura::Window* win2(window_with_height2.window());
307 gfx::Rect win1_bounds = ScreenUtil::ConvertRectToScreen(
308 docked_container_, win1->GetTargetBounds());
309 gfx::Rect win2_bounds = ScreenUtil::ConvertRectToScreen(
310 docked_container_, win2->GetTargetBounds());
311 win1_bounds.set_height(window_with_height1.height_);
312 win2_bounds.set_height(window_with_height2.height_);
313 // If one of the windows is the |dragged_window_| attempt to make an
314 // earlier swap between the windows than just based on their centers.
315 // This is possible if the dragged window is at least as tall as the other
316 // window.
317 if (win1 == dragged_window_)
318 return compare_two_windows(win1_bounds, win2_bounds);
319 if (win2 == dragged_window_)
320 return !compare_two_windows(win2_bounds, win1_bounds);
321 // Otherwise just compare the centers.
322 return win1_bounds.CenterPoint().y() < win2_bounds.CenterPoint().y();
325 // Based on center point tries to deduce where the drag is coming from.
326 // When dragging from below up the transition point is lower.
327 // When dragging from above down the transition point is higher.
328 bool compare_bounds(const gfx::Rect dragged, const gfx::Rect other) {
329 if (dragged.CenterPoint().y() < other.CenterPoint().y())
330 return dragged.CenterPoint().y() < other.y() - delta_;
331 return dragged.CenterPoint().y() < other.bottom() + delta_;
334 // Performs comparison both ways and selects stable result.
335 bool compare_two_windows(const gfx::Rect bounds1, const gfx::Rect bounds2) {
336 // Try comparing windows in both possible orders and see if the comparison
337 // is stable.
338 bool result1 = compare_bounds(bounds1, bounds2);
339 bool result2 = compare_bounds(bounds2, bounds1);
340 if (result1 != result2)
341 return result1;
343 // Otherwise it is not possible to be sure that the windows will not bounce.
344 // In this case just compare the centers.
345 return bounds1.CenterPoint().y() < bounds2.CenterPoint().y();
348 private:
349 aura::Window* dragged_window_;
350 aura::Window* docked_container_;
351 float delta_;
354 } // namespace
356 ////////////////////////////////////////////////////////////////////////////////
357 // A class that observes shelf for bounds changes.
358 class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
359 public:
360 explicit ShelfWindowObserver(
361 DockedWindowLayoutManager* docked_layout_manager)
362 : docked_layout_manager_(docked_layout_manager) {
363 DCHECK(docked_layout_manager_->shelf()->shelf_widget());
364 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
365 ->AddObserver(this);
368 ~ShelfWindowObserver() override {
369 if (docked_layout_manager_->shelf() &&
370 docked_layout_manager_->shelf()->shelf_widget())
371 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
372 ->RemoveObserver(this);
375 // aura::WindowObserver:
376 void OnWindowBoundsChanged(aura::Window* window,
377 const gfx::Rect& old_bounds,
378 const gfx::Rect& new_bounds) override {
379 shelf_bounds_in_screen_ = ScreenUtil::ConvertRectToScreen(
380 window->parent(), new_bounds);
381 docked_layout_manager_->OnShelfBoundsChanged();
384 const gfx::Rect& shelf_bounds_in_screen() const {
385 return shelf_bounds_in_screen_;
388 private:
389 DockedWindowLayoutManager* docked_layout_manager_;
390 gfx::Rect shelf_bounds_in_screen_;
392 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
395 ////////////////////////////////////////////////////////////////////////////////
396 // DockedWindowLayoutManager public implementation:
397 DockedWindowLayoutManager::DockedWindowLayoutManager(
398 aura::Window* dock_container,
399 WorkspaceController* workspace_controller)
400 : SnapToPixelLayoutManager(dock_container),
401 dock_container_(dock_container),
402 in_layout_(false),
403 dragged_window_(NULL),
404 is_dragged_window_docked_(false),
405 is_dragged_from_dock_(false),
406 shelf_(NULL),
407 workspace_controller_(workspace_controller),
408 in_fullscreen_(workspace_controller_->GetWindowState() ==
409 WORKSPACE_WINDOW_STATE_FULL_SCREEN),
410 docked_width_(0),
411 alignment_(DOCKED_ALIGNMENT_NONE),
412 preferred_alignment_(DOCKED_ALIGNMENT_NONE),
413 event_source_(DOCKED_ACTION_SOURCE_UNKNOWN),
414 last_active_window_(NULL),
415 last_action_time_(base::Time::Now()),
416 background_widget_(new DockedBackgroundWidget(dock_container_)) {
417 DCHECK(dock_container);
418 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
419 AddObserver(this);
420 Shell::GetInstance()->AddShellObserver(this);
423 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
424 Shutdown();
427 void DockedWindowLayoutManager::Shutdown() {
428 if (shelf_ && shelf_->shelf_widget()) {
429 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
430 shelf_->shelf_widget()->GetNativeWindow());
431 shelf_layout_manager->RemoveObserver(this);
432 shelf_observer_.reset();
434 shelf_ = NULL;
435 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
436 aura::Window* child = dock_container_->children()[i];
437 child->RemoveObserver(this);
438 wm::GetWindowState(child)->RemoveObserver(this);
440 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
441 RemoveObserver(this);
442 Shell::GetInstance()->RemoveShellObserver(this);
445 void DockedWindowLayoutManager::AddObserver(
446 DockedWindowLayoutManagerObserver* observer) {
447 observer_list_.AddObserver(observer);
450 void DockedWindowLayoutManager::RemoveObserver(
451 DockedWindowLayoutManagerObserver* observer) {
452 observer_list_.RemoveObserver(observer);
455 void DockedWindowLayoutManager::StartDragging(aura::Window* window) {
456 DCHECK(!dragged_window_);
457 dragged_window_ = window;
458 DCHECK(!IsPopupOrTransient(window));
459 // Start observing a window unless it is docked container's child in which
460 // case it is already observed.
461 wm::WindowState* dragged_state = wm::GetWindowState(dragged_window_);
462 if (dragged_window_->parent() != dock_container_) {
463 dragged_window_->AddObserver(this);
464 dragged_state->AddObserver(this);
465 } else if (!IsAnyWindowDocked() &&
466 dragged_state->drag_details() &&
467 !(dragged_state->drag_details()->bounds_change &
468 WindowResizer::kBoundsChange_Resizes)) {
469 // If there are no other docked windows clear alignment when a docked window
470 // is moved (but not when it is resized or the window could get undocked
471 // when resized away from the edge while docked).
472 alignment_ = DOCKED_ALIGNMENT_NONE;
474 is_dragged_from_dock_ = window->parent() == dock_container_;
475 DCHECK(!is_dragged_window_docked_);
477 // Resize all windows that are flush with the dock edge together if one of
478 // them gets resized.
479 if (dragged_window_->bounds().width() == docked_width_ &&
480 (dragged_state->drag_details()->bounds_change &
481 WindowResizer::kBoundsChange_Resizes) &&
482 (dragged_state->drag_details()->size_change_direction &
483 WindowResizer::kBoundsChangeDirection_Horizontal)) {
484 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
485 aura::Window* window1(dock_container_->children()[i]);
486 if (IsWindowDocked(window1) && window1 != dragged_window_ &&
487 window1->bounds().width() == docked_width_) {
488 wm::GetWindowState(window1)->set_bounds_changed_by_user(false);
494 void DockedWindowLayoutManager::DockDraggedWindow(aura::Window* window) {
495 DCHECK(!IsPopupOrTransient(window));
496 OnDraggedWindowDocked(window);
497 Relayout();
500 void DockedWindowLayoutManager::UndockDraggedWindow() {
501 DCHECK(!IsPopupOrTransient(dragged_window_));
502 OnDraggedWindowUndocked();
503 Relayout();
504 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
505 is_dragged_from_dock_ = false;
508 void DockedWindowLayoutManager::FinishDragging(DockedAction action,
509 DockedActionSource source) {
510 DCHECK(dragged_window_);
511 DCHECK(!IsPopupOrTransient(dragged_window_));
512 if (is_dragged_window_docked_)
513 OnDraggedWindowUndocked();
514 DCHECK (!is_dragged_window_docked_);
515 // Stop observing a window unless it is docked container's child in which
516 // case it needs to keep being observed after the drag completes.
517 if (dragged_window_->parent() != dock_container_) {
518 dragged_window_->RemoveObserver(this);
519 wm::GetWindowState(dragged_window_)->RemoveObserver(this);
520 if (last_active_window_ == dragged_window_)
521 last_active_window_ = NULL;
522 } else {
523 // If this is the first window that got docked by a move update alignment.
524 if (alignment_ == DOCKED_ALIGNMENT_NONE)
525 alignment_ = GetEdgeNearestWindow(dragged_window_);
526 // A window is no longer dragged and is a child.
527 // When a window becomes a child at drag start this is
528 // the only opportunity we will have to enforce a window
529 // count limit so do it here.
530 MaybeMinimizeChildrenExcept(dragged_window_);
532 dragged_window_ = NULL;
533 dragged_bounds_ = gfx::Rect();
534 Relayout();
535 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
536 RecordUmaAction(action, source);
539 void DockedWindowLayoutManager::SetShelf(Shelf* shelf) {
540 DCHECK(!shelf_);
541 shelf_ = shelf;
542 if (shelf_->shelf_widget()) {
543 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
544 shelf_->shelf_widget()->GetNativeWindow());
545 shelf_layout_manager->AddObserver(this);
546 shelf_observer_.reset(new ShelfWindowObserver(this));
550 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
551 const aura::Window* window) const {
552 const gfx::Rect& bounds(window->GetBoundsInScreen());
554 // Test overlap with an existing docked area first.
555 if (docked_bounds_.Intersects(bounds) &&
556 alignment_ != DOCKED_ALIGNMENT_NONE) {
557 // A window is being added to other docked windows (on the same side).
558 return alignment_;
561 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
562 if (bounds.x() <= container_bounds.x() &&
563 bounds.right() > container_bounds.x()) {
564 return DOCKED_ALIGNMENT_LEFT;
565 } else if (bounds.x() < container_bounds.right() &&
566 bounds.right() >= container_bounds.right()) {
567 return DOCKED_ALIGNMENT_RIGHT;
569 return DOCKED_ALIGNMENT_NONE;
572 DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const {
573 return CalculateAlignmentExcept(dragged_window_);
576 DockedAlignment DockedWindowLayoutManager::CalculateAlignmentExcept(
577 const aura::Window* window) const {
578 // Find a child that is not the window being queried and is not a popup.
579 // If such exists the current alignment is returned - even if some of the
580 // children are hidden or minimized (so they can be restored without losing
581 // the docked state).
582 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
583 aura::Window* child(dock_container_->children()[i]);
584 if (window != child && !IsPopupOrTransient(child))
585 return alignment_;
587 // No docked windows remain other than possibly the window being queried.
588 // Return |NONE| to indicate that windows may get docked on either side.
589 return DOCKED_ALIGNMENT_NONE;
592 bool DockedWindowLayoutManager::CanDockWindow(
593 aura::Window* window,
594 DockedAlignment desired_alignment) {
595 // Don't allow interactive docking of windows with transient parents such as
596 // modal browser dialogs. Prevent docking of panels attached to shelf during
597 // the drag.
598 wm::WindowState* window_state = wm::GetWindowState(window);
599 bool should_attach_to_shelf = window_state->drag_details() &&
600 window_state->drag_details()->should_attach_to_shelf;
601 if (IsPopupOrTransient(window) || should_attach_to_shelf)
602 return false;
603 // If a window is wide and cannot be resized down to maximum width allowed
604 // then it cannot be docked.
605 // TODO(varkha). Prevent windows from changing size programmatically while
606 // they are docked. The size will take effect only once a window is undocked.
607 // See http://crbug.com/307792.
608 if (window->bounds().width() > kMaxDockWidth &&
609 (!window_state->CanResize() ||
610 (window->delegate() &&
611 window->delegate()->GetMinimumSize().width() != 0 &&
612 window->delegate()->GetMinimumSize().width() > kMaxDockWidth))) {
613 return false;
615 // If a window is tall and cannot be resized down to maximum height allowed
616 // then it cannot be docked.
617 const gfx::Rect work_area =
618 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
619 if (GetWindowHeightCloseTo(window, work_area.height()) > work_area.height())
620 return false;
621 // Cannot dock on the other size from an existing dock.
622 const DockedAlignment alignment = CalculateAlignmentExcept(window);
623 if (desired_alignment != DOCKED_ALIGNMENT_NONE &&
624 alignment != DOCKED_ALIGNMENT_NONE &&
625 alignment != desired_alignment) {
626 return false;
628 // Do not allow docking on the same side as shelf.
629 return IsDockedAlignmentValid(desired_alignment);
632 bool DockedWindowLayoutManager::IsDockedAlignmentValid(
633 DockedAlignment alignment) const {
634 ShelfAlignment shelf_alignment = shelf_ ? shelf_->alignment() :
635 SHELF_ALIGNMENT_BOTTOM;
636 if ((alignment == DOCKED_ALIGNMENT_LEFT &&
637 shelf_alignment == SHELF_ALIGNMENT_LEFT) ||
638 (alignment == DOCKED_ALIGNMENT_RIGHT &&
639 shelf_alignment == SHELF_ALIGNMENT_RIGHT)) {
640 return false;
642 return true;
645 void DockedWindowLayoutManager::MaybeSetDesiredDockedAlignment(
646 DockedAlignment alignment) {
647 // If the requested alignment is |NONE| or there are no
648 // docked windows return early as we can't change whether there is a
649 // dock or not. If the requested alignment is the same as the current
650 // alignment return early as an optimization.
651 if (alignment == DOCKED_ALIGNMENT_NONE ||
652 alignment_ == DOCKED_ALIGNMENT_NONE ||
653 alignment_ == alignment ||
654 !IsDockedAlignmentValid(alignment)) {
655 return;
657 alignment_ = alignment;
659 Relayout();
660 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
663 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
664 Relayout();
665 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
668 ////////////////////////////////////////////////////////////////////////////////
669 // DockedWindowLayoutManager, aura::LayoutManager implementation:
670 void DockedWindowLayoutManager::OnWindowResized() {
671 MaybeMinimizeChildrenExcept(dragged_window_);
672 Relayout();
673 // When screen resizes update the insets even when dock width or alignment
674 // does not change.
675 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED);
678 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
679 if (IsPopupOrTransient(child))
680 return;
681 // Dragged windows are already observed by StartDragging and do not change
682 // docked alignment during the drag.
683 if (child == dragged_window_)
684 return;
685 // If this is the first window getting docked - update alignment.
686 // A window can be added without proper bounds when window is moved to another
687 // display via API or due to display configuration change, so the alignment
688 // is set based on which edge is closer in the new display.
689 if (alignment_ == DOCKED_ALIGNMENT_NONE) {
690 alignment_ = preferred_alignment_ != DOCKED_ALIGNMENT_NONE ?
691 preferred_alignment_ : GetEdgeNearestWindow(child);
693 MaybeMinimizeChildrenExcept(child);
694 child->AddObserver(this);
695 wm::GetWindowState(child)->AddObserver(this);
696 Relayout();
697 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
699 // Only keyboard-initiated actions are recorded here. Dragging cases
700 // are handled in FinishDragging.
701 if (event_source_ != DOCKED_ACTION_SOURCE_UNKNOWN)
702 RecordUmaAction(DOCKED_ACTION_DOCK, event_source_);
705 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
706 if (IsPopupOrTransient(child))
707 return;
708 // Dragged windows are stopped being observed by FinishDragging and do not
709 // change alignment during the drag. They also cannot be set to be the
710 // |last_active_window_|.
711 if (child == dragged_window_)
712 return;
713 // If this is the last window, set alignment and maximize the workspace.
714 if (!IsAnyWindowDocked()) {
715 alignment_ = DOCKED_ALIGNMENT_NONE;
716 UpdateDockedWidth(0);
718 if (last_active_window_ == child)
719 last_active_window_ = NULL;
720 child->RemoveObserver(this);
721 wm::GetWindowState(child)->RemoveObserver(this);
722 Relayout();
723 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
726 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
727 aura::Window* child,
728 bool visible) {
729 if (IsPopupOrTransient(child))
730 return;
732 wm::WindowState* window_state = wm::GetWindowState(child);
733 if (visible && window_state->IsMinimized())
734 window_state->Restore();
735 Relayout();
736 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
739 void DockedWindowLayoutManager::SetChildBounds(
740 aura::Window* child,
741 const gfx::Rect& requested_bounds) {
742 // The minimum constraints have to be applied first by the layout manager.
743 gfx::Rect actual_new_bounds(requested_bounds);
744 if (child->delegate()) {
745 const gfx::Size& min_size = child->delegate()->GetMinimumSize();
746 actual_new_bounds.set_width(
747 std::max(min_size.width(), actual_new_bounds.width()));
748 actual_new_bounds.set_height(
749 std::max(min_size.height(), actual_new_bounds.height()));
751 if (IsWindowDocked(child) && child != dragged_window_)
752 return;
753 SnapToPixelLayoutManager::SetChildBounds(child, actual_new_bounds);
754 if (IsPopupOrTransient(child))
755 return;
756 // Whenever one of our windows is moved or resized enforce layout.
757 ShelfLayoutManager* shelf_layout =
758 ShelfLayoutManager::ForShelf(dock_container_);
759 if (shelf_layout)
760 shelf_layout->UpdateVisibilityState();
763 ////////////////////////////////////////////////////////////////////////////////
764 // DockedWindowLayoutManager, ash::ShellObserver implementation:
766 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
767 Relayout();
768 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
769 MaybeMinimizeChildrenExcept(dragged_window_);
772 void DockedWindowLayoutManager::OnFullscreenStateChanged(
773 bool is_fullscreen, aura::Window* root_window) {
774 if (dock_container_->GetRootWindow() != root_window)
775 return;
776 // Entering fullscreen mode (including immersive) hides docked windows.
777 in_fullscreen_ = workspace_controller_->GetWindowState() ==
778 WORKSPACE_WINDOW_STATE_FULL_SCREEN;
780 // prevent Relayout from getting called multiple times during this
781 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
782 // Use a copy of children array because a call to MinimizeDockedWindow or
783 // RestoreDockedWindow can change order.
784 aura::Window::Windows children(dock_container_->children());
785 for (aura::Window::Windows::const_iterator iter = children.begin();
786 iter != children.end(); ++iter) {
787 aura::Window* window(*iter);
788 if (IsPopupOrTransient(window))
789 continue;
790 wm::WindowState* window_state = wm::GetWindowState(window);
791 if (in_fullscreen_) {
792 if (window->IsVisible())
793 MinimizeDockedWindow(window_state);
794 } else {
795 if (!window_state->IsMinimized())
796 RestoreDockedWindow(window_state);
800 Relayout();
801 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
804 void DockedWindowLayoutManager::OnShelfAlignmentChanged(
805 aura::Window* root_window) {
806 if (dock_container_->GetRootWindow() != root_window)
807 return;
809 if (!shelf_ || !shelf_->shelf_widget())
810 return;
812 if (alignment_ == DOCKED_ALIGNMENT_NONE)
813 return;
815 // Do not allow shelf and dock on the same side. Switch side that
816 // the dock is attached to and move all dock windows to that new side.
817 ShelfAlignment shelf_alignment = shelf_->shelf_widget()->GetAlignment();
818 if (alignment_ == DOCKED_ALIGNMENT_LEFT &&
819 shelf_alignment == SHELF_ALIGNMENT_LEFT) {
820 alignment_ = DOCKED_ALIGNMENT_RIGHT;
821 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT &&
822 shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
823 alignment_ = DOCKED_ALIGNMENT_LEFT;
825 Relayout();
826 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED);
829 /////////////////////////////////////////////////////////////////////////////
830 // DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
831 void DockedWindowLayoutManager::OnBackgroundUpdated(
832 ShelfBackgroundType background_type,
833 BackgroundAnimatorChangeType change_type) {
834 background_widget_->SetBackgroundType(background_type, change_type);
837 /////////////////////////////////////////////////////////////////////////////
838 // DockedWindowLayoutManager, WindowStateObserver implementation:
840 void DockedWindowLayoutManager::OnPreWindowStateTypeChange(
841 wm::WindowState* window_state,
842 wm::WindowStateType old_type) {
843 aura::Window* window = window_state->window();
844 if (IsPopupOrTransient(window))
845 return;
846 // The window property will still be set, but no actual change will occur
847 // until OnFullscreenStateChange is called when exiting fullscreen.
848 if (in_fullscreen_)
849 return;
850 if (!window_state->IsDocked()) {
851 if (window != dragged_window_) {
852 UndockWindow(window);
853 if (window_state->IsMaximizedOrFullscreen())
854 RecordUmaAction(DOCKED_ACTION_MAXIMIZE, event_source_);
855 else
856 RecordUmaAction(DOCKED_ACTION_UNDOCK, event_source_);
858 } else if (window_state->IsMinimized()) {
859 MinimizeDockedWindow(window_state);
860 } else if (old_type == wm::WINDOW_STATE_TYPE_DOCKED_MINIMIZED) {
861 RestoreDockedWindow(window_state);
862 } else if (old_type == wm::WINDOW_STATE_TYPE_MINIMIZED) {
863 NOTREACHED() << "Minimized window in docked layout manager";
867 /////////////////////////////////////////////////////////////////////////////
868 // DockedWindowLayoutManager, WindowObserver implementation:
870 void DockedWindowLayoutManager::OnWindowBoundsChanged(
871 aura::Window* window,
872 const gfx::Rect& old_bounds,
873 const gfx::Rect& new_bounds) {
874 // Only relayout if the dragged window would get docked.
875 if (window == dragged_window_ && is_dragged_window_docked_)
876 Relayout();
879 void DockedWindowLayoutManager::OnWindowVisibilityChanging(
880 aura::Window* window, bool visible) {
881 if (IsPopupOrTransient(window))
882 return;
883 int animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT;
884 if (visible) {
885 animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
886 ::wm::SetWindowVisibilityAnimationDuration(
887 window, base::TimeDelta::FromMilliseconds(kFadeDurationMs));
888 } else if (wm::GetWindowState(window)->IsMinimized()) {
889 animation_type = WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE;
891 ::wm::SetWindowVisibilityAnimationType(window, animation_type);
894 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
895 if (dragged_window_ == window) {
896 FinishDragging(DOCKED_ACTION_NONE, DOCKED_ACTION_SOURCE_UNKNOWN);
897 DCHECK(!dragged_window_);
898 DCHECK(!is_dragged_window_docked_);
900 if (window == last_active_window_)
901 last_active_window_ = NULL;
902 RecordUmaAction(DOCKED_ACTION_CLOSE, event_source_);
906 ////////////////////////////////////////////////////////////////////////////////
907 // DockedWindowLayoutManager, aura::client::ActivationChangeObserver
908 // implementation:
910 void DockedWindowLayoutManager::OnWindowActivated(
911 aura::client::ActivationChangeObserver::ActivationReason reason,
912 aura::Window* gained_active,
913 aura::Window* lost_active) {
914 if (gained_active && IsPopupOrTransient(gained_active))
915 return;
916 // Ignore if the window that is not managed by this was activated.
917 aura::Window* ancestor = NULL;
918 for (aura::Window* parent = gained_active;
919 parent; parent = parent->parent()) {
920 if (parent->parent() == dock_container_) {
921 ancestor = parent;
922 break;
925 if (ancestor)
926 UpdateStacking(ancestor);
929 ////////////////////////////////////////////////////////////////////////////////
930 // DockedWindowLayoutManager private implementation:
932 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
933 aura::Window* child) {
934 // Minimize any windows that don't fit without overlap.
935 const gfx::Rect work_area =
936 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
937 int available_room = work_area.height();
938 bool gap_needed = !!child;
939 if (child)
940 available_room -= GetWindowHeightCloseTo(child, 0);
941 // Use a copy of children array because a call to Minimize can change order.
942 aura::Window::Windows children(dock_container_->children());
943 aura::Window::Windows::const_reverse_iterator iter = children.rbegin();
944 while (iter != children.rend()) {
945 aura::Window* window(*iter++);
946 if (window == child || !IsWindowDocked(window))
947 continue;
948 int room_needed = GetWindowHeightCloseTo(window, 0) +
949 (gap_needed ? kMinDockGap : 0);
950 gap_needed = true;
951 if (available_room > room_needed) {
952 available_room -= room_needed;
953 } else {
954 // Slow down minimizing animations. Lock duration so that it is not
955 // overridden by other ScopedLayerAnimationSettings down the stack.
956 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
957 settings.SetTransitionDuration(
958 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
959 settings.LockTransitionDuration();
960 wm::GetWindowState(window)->Minimize();
965 void DockedWindowLayoutManager::MinimizeDockedWindow(
966 wm::WindowState* window_state) {
967 DCHECK(!IsPopupOrTransient(window_state->window()));
968 window_state->window()->Hide();
969 if (window_state->IsActive())
970 window_state->Deactivate();
971 RecordUmaAction(DOCKED_ACTION_MINIMIZE, event_source_);
974 void DockedWindowLayoutManager::RestoreDockedWindow(
975 wm::WindowState* window_state) {
976 aura::Window* window = window_state->window();
977 DCHECK(!IsPopupOrTransient(window));
978 // Always place restored window at the bottom shuffling the other windows up.
979 // TODO(varkha): add a separate container for docked windows to keep track
980 // of ordering.
981 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
982 dock_container_);
983 const gfx::Rect work_area = display.work_area();
985 // Evict the window if it can no longer be docked because of its height.
986 if (!CanDockWindow(window, DOCKED_ALIGNMENT_NONE)) {
987 window_state->Restore();
988 RecordUmaAction(DOCKED_ACTION_EVICT, event_source_);
989 return;
991 gfx::Rect bounds(window->bounds());
992 bounds.set_y(work_area.bottom());
993 window->SetBounds(bounds);
994 window->Show();
995 MaybeMinimizeChildrenExcept(window);
996 RecordUmaAction(DOCKED_ACTION_RESTORE, event_source_);
999 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action,
1000 DockedActionSource source) {
1001 if (action == DOCKED_ACTION_NONE)
1002 return;
1003 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action, DOCKED_ACTION_COUNT);
1004 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source,
1005 DOCKED_ACTION_SOURCE_COUNT);
1006 base::Time time_now = base::Time::Now();
1007 base::TimeDelta time_between_use = time_now - last_action_time_;
1008 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse",
1009 time_between_use.InSeconds(),
1011 base::TimeDelta::FromHours(10).InSeconds(),
1012 100);
1013 last_action_time_ = time_now;
1014 int docked_all_count = 0;
1015 int docked_visible_count = 0;
1016 int docked_panels_count = 0;
1017 int large_windows_count = 0;
1018 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
1019 const aura::Window* window(dock_container_->children()[i]);
1020 if (IsPopupOrTransient(window))
1021 continue;
1022 docked_all_count++;
1023 if (!IsWindowDocked(window))
1024 continue;
1025 docked_visible_count++;
1026 if (window->type() == ui::wm::WINDOW_TYPE_PANEL)
1027 docked_panels_count++;
1028 const wm::WindowState* window_state = wm::GetWindowState(window);
1029 if (window_state->HasRestoreBounds()) {
1030 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen();
1031 if (restore_bounds.width() > kMaxDockWidth)
1032 large_windows_count++;
1035 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count);
1036 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count);
1037 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count);
1038 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count);
1041 void DockedWindowLayoutManager::UpdateDockedWidth(int width) {
1042 if (docked_width_ == width)
1043 return;
1044 docked_width_ = width;
1045 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_);
1048 void DockedWindowLayoutManager::OnDraggedWindowDocked(aura::Window* window) {
1049 DCHECK(!is_dragged_window_docked_);
1050 is_dragged_window_docked_ = true;
1053 void DockedWindowLayoutManager::OnDraggedWindowUndocked() {
1054 DCHECK (is_dragged_window_docked_);
1055 is_dragged_window_docked_ = false;
1058 bool DockedWindowLayoutManager::IsAnyWindowDocked() {
1059 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE;
1062 DockedAlignment DockedWindowLayoutManager::GetEdgeNearestWindow(
1063 const aura::Window* window) const {
1064 const gfx::Rect& bounds(window->GetBoundsInScreen());
1065 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
1066 return (abs(bounds.x() - container_bounds.x()) <
1067 abs(bounds.right() - container_bounds.right())) ?
1068 DOCKED_ALIGNMENT_LEFT : DOCKED_ALIGNMENT_RIGHT;
1071 void DockedWindowLayoutManager::Relayout() {
1072 if (in_layout_)
1073 return;
1074 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_)
1075 return;
1076 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
1078 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1079 aura::Window* active_window = NULL;
1080 std::vector<WindowWithHeight> visible_windows;
1081 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
1082 aura::Window* window(dock_container_->children()[i]);
1084 if (!IsWindowDocked(window) || window == dragged_window_)
1085 continue;
1087 // If the shelf is currently hidden (full-screen mode), hide window until
1088 // full-screen mode is exited.
1089 if (in_fullscreen_) {
1090 // The call to Hide does not set the minimize property, so the window will
1091 // be restored when the shelf becomes visible again.
1092 window->Hide();
1093 continue;
1095 if (window->HasFocus() ||
1096 window->Contains(
1097 aura::client::GetFocusClient(window)->GetFocusedWindow())) {
1098 DCHECK(!active_window);
1099 active_window = window;
1101 visible_windows.push_back(WindowWithHeight(window));
1103 // Consider docked dragged_window_ when fanning out other child windows.
1104 if (is_dragged_window_docked_) {
1105 visible_windows.push_back(WindowWithHeight(dragged_window_));
1106 DCHECK(!active_window);
1107 active_window = dragged_window_;
1110 // Position docked windows as well as the window being dragged.
1111 gfx::Rect work_area =
1112 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1113 if (shelf_observer_)
1114 work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
1115 int available_room = CalculateWindowHeightsAndRemainingRoom(work_area,
1116 &visible_windows);
1117 FanOutChildren(work_area,
1118 CalculateIdealWidth(visible_windows),
1119 available_room,
1120 &visible_windows);
1122 // After the first Relayout allow the windows to change their order easier
1123 // since we know they are docked.
1124 is_dragged_from_dock_ = true;
1125 UpdateStacking(active_window);
1128 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom(
1129 const gfx::Rect work_area,
1130 std::vector<WindowWithHeight>* visible_windows) {
1131 int available_room = work_area.height();
1132 int remaining_windows = visible_windows->size();
1133 int gap_height = remaining_windows > 1 ? kMinDockGap : 0;
1135 // Sort windows by their minimum heights and calculate target heights.
1136 std::sort(visible_windows->begin(), visible_windows->end(),
1137 CompareMinimumHeight());
1138 // Distribute the free space among the docked windows. Since the windows are
1139 // sorted (tall windows first) we can now assume that any window which
1140 // required more space than the current window will have already been
1141 // accounted for previously in this loop, so we can safely give that window
1142 // its proportional share of the remaining space.
1143 for (std::vector<WindowWithHeight>::reverse_iterator iter =
1144 visible_windows->rbegin();
1145 iter != visible_windows->rend(); ++iter) {
1146 iter->height_ = GetWindowHeightCloseTo(
1147 iter->window(),
1148 (available_room + gap_height) / remaining_windows - gap_height);
1149 available_room -= (iter->height_ + gap_height);
1150 remaining_windows--;
1152 return available_room + gap_height;
1155 int DockedWindowLayoutManager::CalculateIdealWidth(
1156 const std::vector<WindowWithHeight>& visible_windows) {
1157 int smallest_max_width = kMaxDockWidth;
1158 int largest_min_width = kMinDockWidth;
1159 // Ideal width of the docked area is as close to kIdealWidth as possible
1160 // while still respecting the minimum and maximum width restrictions on the
1161 // individual docked windows as well as the width that was possibly set by a
1162 // user (which needs to be preserved when dragging and rearranging windows).
1163 for (std::vector<WindowWithHeight>::const_iterator iter =
1164 visible_windows.begin();
1165 iter != visible_windows.end(); ++iter) {
1166 const aura::Window* window = iter->window();
1167 int min_window_width = window->bounds().width();
1168 int max_window_width = min_window_width;
1169 if (!wm::GetWindowState(window)->bounds_changed_by_user()) {
1170 min_window_width = GetWindowWidthCloseTo(window, kMinDockWidth);
1171 max_window_width = GetWindowWidthCloseTo(window, kMaxDockWidth);
1173 largest_min_width = std::max(largest_min_width, min_window_width);
1174 smallest_max_width = std::min(smallest_max_width, max_window_width);
1176 int ideal_width = std::max(largest_min_width,
1177 std::min(smallest_max_width, kIdealWidth));
1178 // Restrict docked area width regardless of window restrictions.
1179 ideal_width = std::max(std::min(ideal_width, kMaxDockWidth), kMinDockWidth);
1180 return ideal_width;
1183 void DockedWindowLayoutManager::FanOutChildren(
1184 const gfx::Rect& work_area,
1185 int ideal_docked_width,
1186 int available_room,
1187 std::vector<WindowWithHeight>* visible_windows) {
1188 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1190 // Calculate initial vertical offset and the gap or overlap between windows.
1191 const int num_windows = visible_windows->size();
1192 const float delta = static_cast<float>(available_room) /
1193 ((available_room > 0 || num_windows <= 1) ?
1194 num_windows + 1 : num_windows - 1);
1195 float y_pos = work_area.y() + ((delta > 0) ? delta : 0);
1197 // Docked area is shown only if there is at least one non-dragged visible
1198 // docked window.
1199 int new_width = ideal_docked_width;
1200 if (visible_windows->empty() ||
1201 (visible_windows->size() == 1 &&
1202 (*visible_windows)[0].window() == dragged_window_)) {
1203 new_width = 0;
1205 UpdateDockedWidth(new_width);
1206 // Sort windows by their center positions and fan out overlapping
1207 // windows.
1208 std::sort(visible_windows->begin(), visible_windows->end(),
1209 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : NULL,
1210 dock_container_,
1211 delta));
1212 for (std::vector<WindowWithHeight>::iterator iter = visible_windows->begin();
1213 iter != visible_windows->end(); ++iter) {
1214 aura::Window* window = iter->window();
1215 gfx::Rect bounds = ScreenUtil::ConvertRectToScreen(
1216 dock_container_, window->GetTargetBounds());
1217 // A window is extended or shrunk to be as close as possible to the ideal
1218 // docked area width. Windows that were resized by a user are kept at their
1219 // existing size.
1220 // This also enforces the min / max restrictions on the docked area width.
1221 bounds.set_width(GetWindowWidthCloseTo(
1222 window,
1223 wm::GetWindowState(window)->bounds_changed_by_user() ?
1224 bounds.width() : ideal_docked_width));
1225 DCHECK_LE(bounds.width(), ideal_docked_width);
1227 DockedAlignment alignment = alignment_;
1228 if (alignment == DOCKED_ALIGNMENT_NONE && window == dragged_window_)
1229 alignment = GetEdgeNearestWindow(window);
1231 // Fan out windows evenly distributing the overlap or remaining free space.
1232 bounds.set_height(iter->height_);
1233 bounds.set_y(std::max(work_area.y(),
1234 std::min(work_area.bottom() - bounds.height(),
1235 static_cast<int>(y_pos + 0.5))));
1236 y_pos += bounds.height() + delta + kMinDockGap;
1238 // All docked windows other than the one currently dragged remain stuck
1239 // to the screen edge (flush with the edge or centered in the dock area).
1240 switch (alignment) {
1241 case DOCKED_ALIGNMENT_LEFT:
1242 bounds.set_x(dock_bounds.x() +
1243 (ideal_docked_width - bounds.width()) / 2);
1244 break;
1245 case DOCKED_ALIGNMENT_RIGHT:
1246 bounds.set_x(dock_bounds.right() -
1247 (ideal_docked_width + bounds.width()) / 2);
1248 break;
1249 case DOCKED_ALIGNMENT_NONE:
1250 break;
1252 if (window == dragged_window_) {
1253 dragged_bounds_ = bounds;
1254 continue;
1256 // If the following asserts it is probably because not all the children
1257 // have been removed when dock was closed.
1258 DCHECK_NE(alignment_, DOCKED_ALIGNMENT_NONE);
1259 bounds = ScreenUtil::ConvertRectFromScreen(dock_container_, bounds);
1260 if (bounds != window->GetTargetBounds()) {
1261 ui::Layer* layer = window->layer();
1262 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
1263 slide_settings.SetPreemptionStrategy(
1264 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
1265 slide_settings.SetTransitionDuration(
1266 base::TimeDelta::FromMilliseconds(kSlideDurationMs));
1267 SetChildBoundsDirect(window, bounds);
1272 void DockedWindowLayoutManager::UpdateDockBounds(
1273 DockedWindowLayoutManagerObserver::Reason reason) {
1274 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0);
1275 const gfx::Rect work_area =
1276 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1277 gfx::Rect bounds = gfx::Rect(
1278 alignment_ == DOCKED_ALIGNMENT_RIGHT && dock_inset > 0 ?
1279 dock_container_->bounds().right() - dock_inset:
1280 dock_container_->bounds().x(),
1281 dock_container_->bounds().y(),
1282 dock_inset,
1283 work_area.height());
1284 docked_bounds_ = bounds +
1285 dock_container_->GetBoundsInScreen().OffsetFromOrigin();
1286 FOR_EACH_OBSERVER(
1287 DockedWindowLayoutManagerObserver,
1288 observer_list_,
1289 OnDockBoundsChanging(bounds, reason));
1290 // Show or hide background for docked area.
1291 gfx::Rect background_bounds(docked_bounds_);
1292 if (shelf_observer_)
1293 background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
1294 background_widget_->SetBackgroundBounds(background_bounds, alignment_);
1295 if (docked_width_ > 0)
1296 background_widget_->Show();
1297 else
1298 background_widget_->Hide();
1301 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
1302 if (!active_window) {
1303 if (!last_active_window_)
1304 return;
1305 active_window = last_active_window_;
1308 // Windows are stacked like a deck of cards:
1309 // ,------.
1310 // |,------.|
1311 // |,------.|
1312 // | active |
1313 // | window |
1314 // |`------'|
1315 // |`------'|
1316 // `------'
1317 // Use the middle of each window to figure out how to stack the window.
1318 // This allows us to update the stacking when a window is being dragged around
1319 // by the titlebar.
1320 std::map<int, aura::Window*> window_ordering;
1321 for (aura::Window::Windows::const_iterator it =
1322 dock_container_->children().begin();
1323 it != dock_container_->children().end(); ++it) {
1324 if (!IsWindowDocked(*it) ||
1325 ((*it) == dragged_window_ && !is_dragged_window_docked_)) {
1326 continue;
1328 gfx::Rect bounds = (*it)->bounds();
1329 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2,
1330 *it));
1332 int active_center_y = active_window->bounds().CenterPoint().y();
1334 aura::Window* previous_window = NULL;
1335 for (std::map<int, aura::Window*>::const_iterator it =
1336 window_ordering.begin();
1337 it != window_ordering.end() && it->first < active_center_y; ++it) {
1338 if (previous_window)
1339 dock_container_->StackChildAbove(it->second, previous_window);
1340 previous_window = it->second;
1342 for (std::map<int, aura::Window*>::const_reverse_iterator it =
1343 window_ordering.rbegin();
1344 it != window_ordering.rend() && it->first > active_center_y; ++it) {
1345 if (previous_window)
1346 dock_container_->StackChildAbove(it->second, previous_window);
1347 previous_window = it->second;
1350 if (previous_window && active_window->parent() == dock_container_)
1351 dock_container_->StackChildAbove(active_window, previous_window);
1352 if (active_window != dragged_window_)
1353 last_active_window_ = active_window;
1356 ////////////////////////////////////////////////////////////////////////////////
1357 // keyboard::KeyboardControllerObserver implementation:
1359 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
1360 const gfx::Rect& keyboard_bounds) {
1361 // This bounds change will have caused a change to the Shelf which does not
1362 // propagate automatically to this class, so manually recalculate bounds.
1363 Relayout();
1364 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING);
1367 } // namespace ash