platformKeys: Add per-extension sign permissions.
[chromium-blink-merge.git] / ash / wm / dock / docked_window_layout_manager.cc
blobdbf6d55c38e18bb912ecf4c851a6bdc0a40d450e
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/wm/dock/docked_window_layout_manager.h"
7 #include "ash/screen_util.h"
8 #include "ash/shelf/shelf.h"
9 #include "ash/shelf/shelf_constants.h"
10 #include "ash/shelf/shelf_layout_manager.h"
11 #include "ash/shelf/shelf_types.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/shell_window_ids.h"
15 #include "ash/wm/coordinate_conversion.h"
16 #include "ash/wm/window_animations.h"
17 #include "ash/wm/window_properties.h"
18 #include "ash/wm/window_resizer.h"
19 #include "ash/wm/window_state.h"
20 #include "ash/wm/window_util.h"
21 #include "ash/wm/workspace_controller.h"
22 #include "base/auto_reset.h"
23 #include "base/command_line.h"
24 #include "base/metrics/histogram.h"
25 #include "grit/ash_resources.h"
26 #include "third_party/skia/include/core/SkColor.h"
27 #include "third_party/skia/include/core/SkPaint.h"
28 #include "ui/aura/client/focus_client.h"
29 #include "ui/aura/client/window_tree_client.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_delegate.h"
32 #include "ui/aura/window_event_dispatcher.h"
33 #include "ui/base/resource/resource_bundle.h"
34 #include "ui/compositor/scoped_layer_animation_settings.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/gfx/geometry/rect.h"
37 #include "ui/gfx/image/image_skia_operations.h"
38 #include "ui/views/background.h"
39 #include "ui/wm/core/window_util.h"
40 #include "ui/wm/public/activation_client.h"
42 namespace ash {
44 // Minimum, maximum width of the dock area and a width of the gap
45 // static
46 const int DockedWindowLayoutManager::kMaxDockWidth = 360;
47 // static
48 const int DockedWindowLayoutManager::kMinDockWidth = 200;
49 // static
50 const int DockedWindowLayoutManager::kMinDockGap = 2;
51 // static
52 const int DockedWindowLayoutManager::kIdealWidth = 250;
53 const int kMinimumHeight = 250;
54 const int kSlideDurationMs = 120;
55 const int kFadeDurationMs = 60;
56 const int kMinimizeDurationMs = 720;
58 class DockedBackgroundWidget : public views::Widget,
59 public BackgroundAnimatorDelegate {
60 public:
61 explicit DockedBackgroundWidget(aura::Window* container)
62 : alignment_(DOCKED_ALIGNMENT_NONE),
63 background_animator_(this, 0, kShelfBackgroundAlpha),
64 alpha_(0),
65 opaque_background_(ui::LAYER_SOLID_COLOR),
66 visible_background_type_(SHELF_BACKGROUND_DEFAULT),
67 visible_background_change_type_(BACKGROUND_CHANGE_IMMEDIATE) {
68 InitWidget(container);
71 // Sets widget bounds and sizes opaque background layer to fill the widget.
72 void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) {
73 SetBounds(bounds);
74 opaque_background_.SetBounds(gfx::Rect(bounds.size()));
75 alignment_ = alignment;
78 // Sets the background type. Starts an animation to transition to
79 // |background_type| if the widget is visible. If the widget is not visible,
80 // the animation is postponed till the widget becomes visible.
81 void SetBackgroundType(ShelfBackgroundType background_type,
82 BackgroundAnimatorChangeType change_type) {
83 visible_background_type_ = background_type;
84 visible_background_change_type_ = change_type;
85 if (IsVisible())
86 UpdateBackground();
89 // views::Widget:
90 void OnNativeWidgetVisibilityChanged(bool visible) override {
91 views::Widget::OnNativeWidgetVisibilityChanged(visible);
92 UpdateBackground();
95 void OnNativeWidgetPaint(gfx::Canvas* canvas) override {
96 const gfx::ImageSkia& shelf_background(
97 alignment_ == DOCKED_ALIGNMENT_LEFT ?
98 shelf_background_left_ : shelf_background_right_);
99 gfx::Rect rect = gfx::Rect(GetWindowBoundsInScreen().size());
100 SkPaint paint;
101 paint.setAlpha(alpha_);
102 canvas->DrawImageInt(shelf_background,
105 shelf_background.width(),
106 shelf_background.height(),
107 alignment_ == DOCKED_ALIGNMENT_LEFT
108 ? rect.width() - shelf_background.width()
109 : 0,
111 shelf_background.width(),
112 rect.height(),
113 false,
114 paint);
115 canvas->DrawImageInt(
116 shelf_background,
117 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width() - 1,
120 shelf_background.height(),
121 alignment_ == DOCKED_ALIGNMENT_LEFT ? 0 : shelf_background.width(),
123 rect.width() - shelf_background.width(),
124 rect.height(),
125 false,
126 paint);
129 // BackgroundAnimatorDelegate:
130 void UpdateBackground(int alpha) override {
131 alpha_ = alpha;
132 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
135 private:
136 void InitWidget(aura::Window* parent) {
137 views::Widget::InitParams params;
138 params.type = views::Widget::InitParams::TYPE_POPUP;
139 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
140 params.keep_on_top = false;
141 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
142 params.parent = parent;
143 params.accept_events = false;
144 set_focus_on_creation(false);
145 Init(params);
146 SetVisibilityChangedAnimationsEnabled(false);
147 GetNativeWindow()->SetProperty(kStayInSameRootWindowKey, true);
148 opaque_background_.SetColor(SK_ColorBLACK);
149 opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
150 opaque_background_.SetOpacity(0.0f);
151 GetNativeWindow()->layer()->Add(&opaque_background_);
152 Hide();
154 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
155 gfx::ImageSkia shelf_background =
156 *rb.GetImageSkiaNamed(IDR_ASH_SHELF_BACKGROUND);
157 shelf_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage(
158 shelf_background, SkBitmapOperations::ROTATION_90_CW);
159 shelf_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage(
160 shelf_background, SkBitmapOperations::ROTATION_270_CW);
163 // Transitions to |visible_background_type_| if the widget is visible and to
164 // SHELF_BACKGROUND_DEFAULT if it is not.
165 void UpdateBackground() {
166 ShelfBackgroundType background_type = IsVisible() ?
167 visible_background_type_ : SHELF_BACKGROUND_DEFAULT;
168 BackgroundAnimatorChangeType change_type = IsVisible() ?
169 visible_background_change_type_ : BACKGROUND_CHANGE_IMMEDIATE;
171 float target_opacity =
172 (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
173 scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
174 if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
175 opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
176 opaque_background_.GetAnimator()));
177 opaque_background_animation->SetTransitionDuration(
178 base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
180 opaque_background_.SetOpacity(target_opacity);
182 // TODO(varkha): use ui::Layer on both opaque_background and normal
183 // background retire background_animator_ at all. It would be simpler.
184 // See also ShelfWidget::SetPaintsBackground.
185 background_animator_.SetPaintsBackground(
186 background_type != SHELF_BACKGROUND_DEFAULT,
187 change_type);
188 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
191 DockedAlignment alignment_;
193 // The animator for the background transitions.
194 BackgroundAnimator background_animator_;
196 // The alpha to use for drawing image assets covering the docked background.
197 int alpha_;
199 // Solid black background that can be made fully opaque.
200 ui::Layer opaque_background_;
202 // Backgrounds created from shelf background by 90 or 270 degree rotation.
203 gfx::ImageSkia shelf_background_left_;
204 gfx::ImageSkia shelf_background_right_;
206 // The background type to use when the widget is visible. When not visible,
207 // the widget uses SHELF_BACKGROUND_DEFAULT.
208 ShelfBackgroundType visible_background_type_;
210 // Whether the widget should animate to |visible_background_type_|.
211 BackgroundAnimatorChangeType visible_background_change_type_;
213 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
216 namespace {
218 // Returns true if a window is a popup or a transient child.
219 bool IsPopupOrTransient(const aura::Window* window) {
220 return (window->type() == ui::wm::WINDOW_TYPE_POPUP ||
221 ::wm::GetTransientParent(window));
224 // Certain windows (minimized, hidden or popups) do not matter to docking.
225 bool IsUsedByLayout(const aura::Window* window) {
226 return (window->IsVisible() &&
227 !wm::GetWindowState(window)->IsMinimized() &&
228 !IsPopupOrTransient(window));
231 void UndockWindow(aura::Window* window) {
232 gfx::Rect previous_bounds = window->bounds();
233 aura::Window* old_parent = window->parent();
234 aura::client::ParentWindowWithContext(window, window, gfx::Rect());
235 if (window->parent() != old_parent)
236 wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent());
237 // Start maximize or fullscreen (affecting packaged apps) animation from
238 // previous window bounds.
239 window->layer()->SetBounds(previous_bounds);
242 // Returns width that is as close as possible to |target_width| while being
243 // consistent with docked min and max restrictions and respects the |window|'s
244 // minimum and maximum size.
245 int GetWindowWidthCloseTo(const aura::Window* window, int target_width) {
246 if (!wm::GetWindowState(window)->CanResize()) {
247 DCHECK_LE(window->bounds().width(),
248 DockedWindowLayoutManager::kMaxDockWidth);
249 return window->bounds().width();
251 int width = std::max(DockedWindowLayoutManager::kMinDockWidth,
252 std::min(target_width,
253 DockedWindowLayoutManager::kMaxDockWidth));
254 if (window->delegate()) {
255 if (window->delegate()->GetMinimumSize().width() != 0)
256 width = std::max(width, window->delegate()->GetMinimumSize().width());
257 if (window->delegate()->GetMaximumSize().width() != 0)
258 width = std::min(width, window->delegate()->GetMaximumSize().width());
260 DCHECK_LE(width, DockedWindowLayoutManager::kMaxDockWidth);
261 return width;
264 // Returns height that is as close as possible to |target_height| while
265 // respecting the |window|'s minimum and maximum size.
266 int GetWindowHeightCloseTo(const aura::Window* window, int target_height) {
267 if (!wm::GetWindowState(window)->CanResize())
268 return window->bounds().height();
269 int minimum_height = kMinimumHeight;
270 int maximum_height = 0;
271 const aura::WindowDelegate* delegate(window->delegate());
272 if (delegate) {
273 if (delegate->GetMinimumSize().height() != 0) {
274 minimum_height = std::max(kMinimumHeight,
275 delegate->GetMinimumSize().height());
277 if (delegate->GetMaximumSize().height() != 0)
278 maximum_height = delegate->GetMaximumSize().height();
280 if (minimum_height)
281 target_height = std::max(target_height, minimum_height);
282 if (maximum_height)
283 target_height = std::min(target_height, maximum_height);
284 return target_height;
287 // A functor used to sort the windows in order of their minimum height.
288 struct CompareMinimumHeight {
289 bool operator()(WindowWithHeight win1, WindowWithHeight win2) {
290 return GetWindowHeightCloseTo(win1.window(), 0) <
291 GetWindowHeightCloseTo(win2.window(), 0);
295 // A functor used to sort the windows in order of their center Y position.
296 // |delta| is a pre-calculated distance from the bottom of one window to the top
297 // of the next. Its value can be positive (gap) or negative (overlap).
298 // Half of |delta| is used as a transition point at which windows could ideally
299 // swap positions.
300 struct CompareWindowPos {
301 CompareWindowPos(aura::Window* dragged_window,
302 aura::Window* docked_container,
303 float delta)
304 : dragged_window_(dragged_window),
305 docked_container_(docked_container),
306 delta_(delta / 2) {}
308 bool operator()(WindowWithHeight window_with_height1,
309 WindowWithHeight window_with_height2) {
310 // Use target coordinates since animations may be active when windows are
311 // reordered.
312 aura::Window* win1(window_with_height1.window());
313 aura::Window* win2(window_with_height2.window());
314 gfx::Rect win1_bounds = ScreenUtil::ConvertRectToScreen(
315 docked_container_, win1->GetTargetBounds());
316 gfx::Rect win2_bounds = ScreenUtil::ConvertRectToScreen(
317 docked_container_, win2->GetTargetBounds());
318 win1_bounds.set_height(window_with_height1.height_);
319 win2_bounds.set_height(window_with_height2.height_);
320 // If one of the windows is the |dragged_window_| attempt to make an
321 // earlier swap between the windows than just based on their centers.
322 // This is possible if the dragged window is at least as tall as the other
323 // window.
324 if (win1 == dragged_window_)
325 return compare_two_windows(win1_bounds, win2_bounds);
326 if (win2 == dragged_window_)
327 return !compare_two_windows(win2_bounds, win1_bounds);
328 // Otherwise just compare the centers.
329 return win1_bounds.CenterPoint().y() < win2_bounds.CenterPoint().y();
332 // Based on center point tries to deduce where the drag is coming from.
333 // When dragging from below up the transition point is lower.
334 // When dragging from above down the transition point is higher.
335 bool compare_bounds(const gfx::Rect dragged, const gfx::Rect other) {
336 if (dragged.CenterPoint().y() < other.CenterPoint().y())
337 return dragged.CenterPoint().y() < other.y() - delta_;
338 return dragged.CenterPoint().y() < other.bottom() + delta_;
341 // Performs comparison both ways and selects stable result.
342 bool compare_two_windows(const gfx::Rect bounds1, const gfx::Rect bounds2) {
343 // Try comparing windows in both possible orders and see if the comparison
344 // is stable.
345 bool result1 = compare_bounds(bounds1, bounds2);
346 bool result2 = compare_bounds(bounds2, bounds1);
347 if (result1 != result2)
348 return result1;
350 // Otherwise it is not possible to be sure that the windows will not bounce.
351 // In this case just compare the centers.
352 return bounds1.CenterPoint().y() < bounds2.CenterPoint().y();
355 private:
356 aura::Window* dragged_window_;
357 aura::Window* docked_container_;
358 float delta_;
361 } // namespace
363 ////////////////////////////////////////////////////////////////////////////////
364 // A class that observes shelf for bounds changes.
365 class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
366 public:
367 explicit ShelfWindowObserver(
368 DockedWindowLayoutManager* docked_layout_manager)
369 : docked_layout_manager_(docked_layout_manager) {
370 DCHECK(docked_layout_manager_->shelf()->shelf_widget());
371 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
372 ->AddObserver(this);
375 ~ShelfWindowObserver() override {
376 if (docked_layout_manager_->shelf() &&
377 docked_layout_manager_->shelf()->shelf_widget())
378 docked_layout_manager_->shelf()->shelf_widget()->GetNativeView()
379 ->RemoveObserver(this);
382 // aura::WindowObserver:
383 void OnWindowBoundsChanged(aura::Window* window,
384 const gfx::Rect& old_bounds,
385 const gfx::Rect& new_bounds) override {
386 shelf_bounds_in_screen_ = ScreenUtil::ConvertRectToScreen(
387 window->parent(), new_bounds);
388 docked_layout_manager_->OnShelfBoundsChanged();
391 const gfx::Rect& shelf_bounds_in_screen() const {
392 return shelf_bounds_in_screen_;
395 private:
396 DockedWindowLayoutManager* docked_layout_manager_;
397 gfx::Rect shelf_bounds_in_screen_;
399 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
402 ////////////////////////////////////////////////////////////////////////////////
403 // DockedWindowLayoutManager public implementation:
404 DockedWindowLayoutManager::DockedWindowLayoutManager(
405 aura::Window* dock_container,
406 WorkspaceController* workspace_controller)
407 : SnapToPixelLayoutManager(dock_container),
408 dock_container_(dock_container),
409 in_layout_(false),
410 dragged_window_(NULL),
411 is_dragged_window_docked_(false),
412 is_dragged_from_dock_(false),
413 shelf_(NULL),
414 workspace_controller_(workspace_controller),
415 in_fullscreen_(workspace_controller_->GetWindowState() ==
416 WORKSPACE_WINDOW_STATE_FULL_SCREEN),
417 docked_width_(0),
418 alignment_(DOCKED_ALIGNMENT_NONE),
419 preferred_alignment_(DOCKED_ALIGNMENT_NONE),
420 event_source_(DOCKED_ACTION_SOURCE_UNKNOWN),
421 last_active_window_(NULL),
422 last_action_time_(base::Time::Now()),
423 background_widget_(new DockedBackgroundWidget(dock_container_)) {
424 DCHECK(dock_container);
425 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
426 AddObserver(this);
427 Shell::GetInstance()->AddShellObserver(this);
430 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
431 Shutdown();
434 void DockedWindowLayoutManager::Shutdown() {
435 if (shelf_ && shelf_->shelf_widget()) {
436 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
437 shelf_->shelf_widget()->GetNativeWindow());
438 shelf_layout_manager->RemoveObserver(this);
439 shelf_observer_.reset();
441 shelf_ = NULL;
442 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
443 aura::Window* child = dock_container_->children()[i];
444 child->RemoveObserver(this);
445 wm::GetWindowState(child)->RemoveObserver(this);
447 aura::client::GetActivationClient(Shell::GetPrimaryRootWindow())->
448 RemoveObserver(this);
449 Shell::GetInstance()->RemoveShellObserver(this);
452 void DockedWindowLayoutManager::AddObserver(
453 DockedWindowLayoutManagerObserver* observer) {
454 observer_list_.AddObserver(observer);
457 void DockedWindowLayoutManager::RemoveObserver(
458 DockedWindowLayoutManagerObserver* observer) {
459 observer_list_.RemoveObserver(observer);
462 void DockedWindowLayoutManager::StartDragging(aura::Window* window) {
463 DCHECK(!dragged_window_);
464 dragged_window_ = window;
465 DCHECK(!IsPopupOrTransient(window));
466 // Start observing a window unless it is docked container's child in which
467 // case it is already observed.
468 wm::WindowState* dragged_state = wm::GetWindowState(dragged_window_);
469 if (dragged_window_->parent() != dock_container_) {
470 dragged_window_->AddObserver(this);
471 dragged_state->AddObserver(this);
472 } else if (!IsAnyWindowDocked() &&
473 dragged_state->drag_details() &&
474 !(dragged_state->drag_details()->bounds_change &
475 WindowResizer::kBoundsChange_Resizes)) {
476 // If there are no other docked windows clear alignment when a docked window
477 // is moved (but not when it is resized or the window could get undocked
478 // when resized away from the edge while docked).
479 alignment_ = DOCKED_ALIGNMENT_NONE;
481 is_dragged_from_dock_ = window->parent() == dock_container_;
482 DCHECK(!is_dragged_window_docked_);
484 // Resize all windows that are flush with the dock edge together if one of
485 // them gets resized.
486 if (dragged_window_->bounds().width() == docked_width_ &&
487 (dragged_state->drag_details()->bounds_change &
488 WindowResizer::kBoundsChange_Resizes) &&
489 (dragged_state->drag_details()->size_change_direction &
490 WindowResizer::kBoundsChangeDirection_Horizontal)) {
491 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
492 aura::Window* window1(dock_container_->children()[i]);
493 if (IsUsedByLayout(window1) &&
494 window1 != dragged_window_ &&
495 window1->bounds().width() == docked_width_) {
496 wm::GetWindowState(window1)->set_bounds_changed_by_user(false);
502 void DockedWindowLayoutManager::DockDraggedWindow(aura::Window* window) {
503 DCHECK(!IsPopupOrTransient(window));
504 OnDraggedWindowDocked(window);
505 Relayout();
508 void DockedWindowLayoutManager::UndockDraggedWindow() {
509 DCHECK(!IsPopupOrTransient(dragged_window_));
510 OnDraggedWindowUndocked();
511 Relayout();
512 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
513 is_dragged_from_dock_ = false;
516 void DockedWindowLayoutManager::FinishDragging(DockedAction action,
517 DockedActionSource source) {
518 DCHECK(dragged_window_);
519 DCHECK(!IsPopupOrTransient(dragged_window_));
520 if (is_dragged_window_docked_)
521 OnDraggedWindowUndocked();
522 DCHECK (!is_dragged_window_docked_);
523 // Stop observing a window unless it is docked container's child in which
524 // case it needs to keep being observed after the drag completes.
525 if (dragged_window_->parent() != dock_container_) {
526 dragged_window_->RemoveObserver(this);
527 wm::GetWindowState(dragged_window_)->RemoveObserver(this);
528 if (last_active_window_ == dragged_window_)
529 last_active_window_ = NULL;
530 } else {
531 // If this is the first window that got docked by a move update alignment.
532 if (alignment_ == DOCKED_ALIGNMENT_NONE)
533 alignment_ = GetEdgeNearestWindow(dragged_window_);
534 // A window is no longer dragged and is a child.
535 // When a window becomes a child at drag start this is
536 // the only opportunity we will have to enforce a window
537 // count limit so do it here.
538 MaybeMinimizeChildrenExcept(dragged_window_);
540 dragged_window_ = NULL;
541 dragged_bounds_ = gfx::Rect();
542 Relayout();
543 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
544 RecordUmaAction(action, source);
547 void DockedWindowLayoutManager::SetShelf(Shelf* shelf) {
548 DCHECK(!shelf_);
549 shelf_ = shelf;
550 if (shelf_->shelf_widget()) {
551 ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForShelf(
552 shelf_->shelf_widget()->GetNativeWindow());
553 shelf_layout_manager->AddObserver(this);
554 shelf_observer_.reset(new ShelfWindowObserver(this));
558 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
559 const aura::Window* window) const {
560 const gfx::Rect& bounds(window->GetBoundsInScreen());
562 // Test overlap with an existing docked area first.
563 if (docked_bounds_.Intersects(bounds) &&
564 alignment_ != DOCKED_ALIGNMENT_NONE) {
565 // A window is being added to other docked windows (on the same side).
566 return alignment_;
569 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
570 if (bounds.x() <= container_bounds.x() &&
571 bounds.right() > container_bounds.x()) {
572 return DOCKED_ALIGNMENT_LEFT;
573 } else if (bounds.x() < container_bounds.right() &&
574 bounds.right() >= container_bounds.right()) {
575 return DOCKED_ALIGNMENT_RIGHT;
577 return DOCKED_ALIGNMENT_NONE;
580 DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const {
581 return CalculateAlignmentExcept(dragged_window_);
584 DockedAlignment DockedWindowLayoutManager::CalculateAlignmentExcept(
585 const aura::Window* window) const {
586 // Find a child that is not the window being queried and is not a popup.
587 // If such exists the current alignment is returned - even if some of the
588 // children are hidden or minimized (so they can be restored without losing
589 // the docked state).
590 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
591 aura::Window* child(dock_container_->children()[i]);
592 if (window != child && !IsPopupOrTransient(child))
593 return alignment_;
595 // No docked windows remain other than possibly the window being queried.
596 // Return |NONE| to indicate that windows may get docked on either side.
597 return DOCKED_ALIGNMENT_NONE;
600 bool DockedWindowLayoutManager::CanDockWindow(
601 aura::Window* window,
602 DockedAlignment desired_alignment) {
603 // Don't allow interactive docking of windows with transient parents such as
604 // modal browser dialogs. Prevent docking of panels attached to shelf during
605 // the drag.
606 wm::WindowState* window_state = wm::GetWindowState(window);
607 bool should_attach_to_shelf = window_state->drag_details() &&
608 window_state->drag_details()->should_attach_to_shelf;
609 if (IsPopupOrTransient(window) || should_attach_to_shelf)
610 return false;
611 // If a window is wide and cannot be resized down to maximum width allowed
612 // then it cannot be docked.
613 // TODO(varkha). Prevent windows from changing size programmatically while
614 // they are docked. The size will take effect only once a window is undocked.
615 // See http://crbug.com/307792.
616 if (window->bounds().width() > kMaxDockWidth &&
617 (!window_state->CanResize() ||
618 (window->delegate() &&
619 window->delegate()->GetMinimumSize().width() != 0 &&
620 window->delegate()->GetMinimumSize().width() > kMaxDockWidth))) {
621 return false;
623 // If a window is tall and cannot be resized down to maximum height allowed
624 // then it cannot be docked.
625 const gfx::Rect work_area =
626 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
627 if (GetWindowHeightCloseTo(window, work_area.height()) > work_area.height())
628 return false;
629 // Cannot dock on the other size from an existing dock.
630 const DockedAlignment alignment = CalculateAlignmentExcept(window);
631 if (desired_alignment != DOCKED_ALIGNMENT_NONE &&
632 alignment != DOCKED_ALIGNMENT_NONE &&
633 alignment != desired_alignment) {
634 return false;
636 // Do not allow docking on the same side as shelf.
637 return IsDockedAlignmentValid(desired_alignment);
640 bool DockedWindowLayoutManager::IsDockedAlignmentValid(
641 DockedAlignment alignment) const {
642 ShelfAlignment shelf_alignment = shelf_ ? shelf_->alignment() :
643 SHELF_ALIGNMENT_BOTTOM;
644 if ((alignment == DOCKED_ALIGNMENT_LEFT &&
645 shelf_alignment == SHELF_ALIGNMENT_LEFT) ||
646 (alignment == DOCKED_ALIGNMENT_RIGHT &&
647 shelf_alignment == SHELF_ALIGNMENT_RIGHT)) {
648 return false;
650 return true;
653 void DockedWindowLayoutManager::MaybeSetDesiredDockedAlignment(
654 DockedAlignment alignment) {
655 // If the requested alignment is |NONE| or there are no
656 // docked windows return early as we can't change whether there is a
657 // dock or not. If the requested alignment is the same as the current
658 // alignment return early as an optimization.
659 if (alignment == DOCKED_ALIGNMENT_NONE ||
660 alignment_ == DOCKED_ALIGNMENT_NONE ||
661 alignment_ == alignment ||
662 !IsDockedAlignmentValid(alignment)) {
663 return;
665 alignment_ = alignment;
667 Relayout();
668 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
671 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
672 Relayout();
673 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
676 ////////////////////////////////////////////////////////////////////////////////
677 // DockedWindowLayoutManager, aura::LayoutManager implementation:
678 void DockedWindowLayoutManager::OnWindowResized() {
679 MaybeMinimizeChildrenExcept(dragged_window_);
680 Relayout();
681 // When screen resizes update the insets even when dock width or alignment
682 // does not change.
683 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED);
686 void DockedWindowLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
687 if (IsPopupOrTransient(child))
688 return;
689 // Dragged windows are already observed by StartDragging and do not change
690 // docked alignment during the drag.
691 if (child == dragged_window_)
692 return;
693 // If this is the first window getting docked - update alignment.
694 // A window can be added without proper bounds when window is moved to another
695 // display via API or due to display configuration change, so the alignment
696 // is set based on which edge is closer in the new display.
697 if (alignment_ == DOCKED_ALIGNMENT_NONE) {
698 alignment_ = preferred_alignment_ != DOCKED_ALIGNMENT_NONE ?
699 preferred_alignment_ : GetEdgeNearestWindow(child);
701 MaybeMinimizeChildrenExcept(child);
702 child->AddObserver(this);
703 wm::GetWindowState(child)->AddObserver(this);
704 Relayout();
705 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
707 // Only keyboard-initiated actions are recorded here. Dragging cases
708 // are handled in FinishDragging.
709 if (event_source_ != DOCKED_ACTION_SOURCE_UNKNOWN)
710 RecordUmaAction(DOCKED_ACTION_DOCK, event_source_);
713 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
714 if (IsPopupOrTransient(child))
715 return;
716 // Dragged windows are stopped being observed by FinishDragging and do not
717 // change alignment during the drag. They also cannot be set to be the
718 // |last_active_window_|.
719 if (child == dragged_window_)
720 return;
721 // If this is the last window, set alignment and maximize the workspace.
722 if (!IsAnyWindowDocked()) {
723 alignment_ = DOCKED_ALIGNMENT_NONE;
724 UpdateDockedWidth(0);
726 if (last_active_window_ == child)
727 last_active_window_ = NULL;
728 child->RemoveObserver(this);
729 wm::GetWindowState(child)->RemoveObserver(this);
730 Relayout();
731 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
734 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(
735 aura::Window* child,
736 bool visible) {
737 if (IsPopupOrTransient(child))
738 return;
740 wm::WindowState* window_state = wm::GetWindowState(child);
741 if (visible && window_state->IsMinimized())
742 window_state->Restore();
743 Relayout();
744 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
747 void DockedWindowLayoutManager::SetChildBounds(
748 aura::Window* child,
749 const gfx::Rect& requested_bounds) {
750 // The minimum constraints have to be applied first by the layout manager.
751 gfx::Rect actual_new_bounds(requested_bounds);
752 if (child->delegate()) {
753 const gfx::Size& min_size = child->delegate()->GetMinimumSize();
754 actual_new_bounds.set_width(
755 std::max(min_size.width(), actual_new_bounds.width()));
756 actual_new_bounds.set_height(
757 std::max(min_size.height(), actual_new_bounds.height()));
759 SnapToPixelLayoutManager::SetChildBounds(child, actual_new_bounds);
760 if (IsPopupOrTransient(child))
761 return;
762 // Whenever one of our windows is moved or resized enforce layout.
763 ShelfLayoutManager* shelf_layout =
764 ShelfLayoutManager::ForShelf(dock_container_);
765 if (shelf_layout)
766 shelf_layout->UpdateVisibilityState();
769 ////////////////////////////////////////////////////////////////////////////////
770 // DockedWindowLayoutManager, ash::ShellObserver implementation:
772 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
773 Relayout();
774 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
775 MaybeMinimizeChildrenExcept(dragged_window_);
778 void DockedWindowLayoutManager::OnFullscreenStateChanged(
779 bool is_fullscreen, aura::Window* root_window) {
780 if (dock_container_->GetRootWindow() != root_window)
781 return;
782 // Entering fullscreen mode (including immersive) hides docked windows.
783 in_fullscreen_ = workspace_controller_->GetWindowState() ==
784 WORKSPACE_WINDOW_STATE_FULL_SCREEN;
786 // prevent Relayout from getting called multiple times during this
787 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
788 // Use a copy of children array because a call to MinimizeDockedWindow or
789 // RestoreDockedWindow can change order.
790 aura::Window::Windows children(dock_container_->children());
791 for (aura::Window::Windows::const_iterator iter = children.begin();
792 iter != children.end(); ++iter) {
793 aura::Window* window(*iter);
794 if (IsPopupOrTransient(window))
795 continue;
796 wm::WindowState* window_state = wm::GetWindowState(window);
797 if (in_fullscreen_) {
798 if (window->IsVisible())
799 MinimizeDockedWindow(window_state);
800 } else {
801 if (!window_state->IsMinimized())
802 RestoreDockedWindow(window_state);
806 Relayout();
807 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
810 void DockedWindowLayoutManager::OnShelfAlignmentChanged(
811 aura::Window* root_window) {
812 if (dock_container_->GetRootWindow() != root_window)
813 return;
815 if (!shelf_ || !shelf_->shelf_widget())
816 return;
818 if (alignment_ == DOCKED_ALIGNMENT_NONE)
819 return;
821 // Do not allow shelf and dock on the same side. Switch side that
822 // the dock is attached to and move all dock windows to that new side.
823 ShelfAlignment shelf_alignment = shelf_->shelf_widget()->GetAlignment();
824 if (alignment_ == DOCKED_ALIGNMENT_LEFT &&
825 shelf_alignment == SHELF_ALIGNMENT_LEFT) {
826 alignment_ = DOCKED_ALIGNMENT_RIGHT;
827 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT &&
828 shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
829 alignment_ = DOCKED_ALIGNMENT_LEFT;
831 Relayout();
832 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED);
835 /////////////////////////////////////////////////////////////////////////////
836 // DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
837 void DockedWindowLayoutManager::OnBackgroundUpdated(
838 ShelfBackgroundType background_type,
839 BackgroundAnimatorChangeType change_type) {
840 background_widget_->SetBackgroundType(background_type, change_type);
843 /////////////////////////////////////////////////////////////////////////////
844 // DockedWindowLayoutManager, WindowStateObserver implementation:
846 void DockedWindowLayoutManager::OnPreWindowStateTypeChange(
847 wm::WindowState* window_state,
848 wm::WindowStateType old_type) {
849 aura::Window* window = window_state->window();
850 if (IsPopupOrTransient(window))
851 return;
852 // The window property will still be set, but no actual change will occur
853 // until OnFullscreenStateChange is called when exiting fullscreen.
854 if (in_fullscreen_)
855 return;
856 if (!window_state->IsDocked()) {
857 if (window != dragged_window_) {
858 UndockWindow(window);
859 if (window_state->IsMaximizedOrFullscreen())
860 RecordUmaAction(DOCKED_ACTION_MAXIMIZE, event_source_);
861 else
862 RecordUmaAction(DOCKED_ACTION_UNDOCK, event_source_);
864 } else if (window_state->IsMinimized()) {
865 MinimizeDockedWindow(window_state);
866 } else if (old_type == wm::WINDOW_STATE_TYPE_DOCKED_MINIMIZED) {
867 RestoreDockedWindow(window_state);
868 } else if (old_type == wm::WINDOW_STATE_TYPE_MINIMIZED) {
869 NOTREACHED() << "Minimized window in docked layout manager";
873 /////////////////////////////////////////////////////////////////////////////
874 // DockedWindowLayoutManager, WindowObserver implementation:
876 void DockedWindowLayoutManager::OnWindowBoundsChanged(
877 aura::Window* window,
878 const gfx::Rect& old_bounds,
879 const gfx::Rect& new_bounds) {
880 // Only relayout if the dragged window would get docked.
881 if (window == dragged_window_ && is_dragged_window_docked_)
882 Relayout();
885 void DockedWindowLayoutManager::OnWindowVisibilityChanging(
886 aura::Window* window, bool visible) {
887 if (IsPopupOrTransient(window))
888 return;
889 int animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT;
890 if (visible) {
891 animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
892 ::wm::SetWindowVisibilityAnimationDuration(
893 window, base::TimeDelta::FromMilliseconds(kFadeDurationMs));
894 } else if (wm::GetWindowState(window)->IsMinimized()) {
895 animation_type = WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE;
897 ::wm::SetWindowVisibilityAnimationType(window, animation_type);
900 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
901 if (dragged_window_ == window) {
902 FinishDragging(DOCKED_ACTION_NONE, DOCKED_ACTION_SOURCE_UNKNOWN);
903 DCHECK(!dragged_window_);
904 DCHECK(!is_dragged_window_docked_);
906 if (window == last_active_window_)
907 last_active_window_ = NULL;
908 RecordUmaAction(DOCKED_ACTION_CLOSE, event_source_);
912 ////////////////////////////////////////////////////////////////////////////////
913 // DockedWindowLayoutManager, aura::client::ActivationChangeObserver
914 // implementation:
916 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
917 aura::Window* lost_active) {
918 if (gained_active && IsPopupOrTransient(gained_active))
919 return;
920 // Ignore if the window that is not managed by this was activated.
921 aura::Window* ancestor = NULL;
922 for (aura::Window* parent = gained_active;
923 parent; parent = parent->parent()) {
924 if (parent->parent() == dock_container_) {
925 ancestor = parent;
926 break;
929 if (ancestor)
930 UpdateStacking(ancestor);
933 ////////////////////////////////////////////////////////////////////////////////
934 // DockedWindowLayoutManager private implementation:
936 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
937 aura::Window* child) {
938 // Minimize any windows that don't fit without overlap.
939 const gfx::Rect work_area =
940 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
941 int available_room = work_area.height();
942 bool gap_needed = !!child;
943 if (child)
944 available_room -= GetWindowHeightCloseTo(child, 0);
945 // Use a copy of children array because a call to Minimize can change order.
946 aura::Window::Windows children(dock_container_->children());
947 aura::Window::Windows::const_reverse_iterator iter = children.rbegin();
948 while (iter != children.rend()) {
949 aura::Window* window(*iter++);
950 if (window == child || !IsUsedByLayout(window))
951 continue;
952 int room_needed = GetWindowHeightCloseTo(window, 0) +
953 (gap_needed ? kMinDockGap : 0);
954 gap_needed = true;
955 if (available_room > room_needed) {
956 available_room -= room_needed;
957 } else {
958 // Slow down minimizing animations. Lock duration so that it is not
959 // overridden by other ScopedLayerAnimationSettings down the stack.
960 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
961 settings.SetTransitionDuration(
962 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
963 settings.LockTransitionDuration();
964 wm::GetWindowState(window)->Minimize();
969 void DockedWindowLayoutManager::MinimizeDockedWindow(
970 wm::WindowState* window_state) {
971 DCHECK(!IsPopupOrTransient(window_state->window()));
972 window_state->window()->Hide();
973 if (window_state->IsActive())
974 window_state->Deactivate();
975 RecordUmaAction(DOCKED_ACTION_MINIMIZE, event_source_);
978 void DockedWindowLayoutManager::RestoreDockedWindow(
979 wm::WindowState* window_state) {
980 aura::Window* window = window_state->window();
981 DCHECK(!IsPopupOrTransient(window));
982 // Always place restored window at the bottom shuffling the other windows up.
983 // TODO(varkha): add a separate container for docked windows to keep track
984 // of ordering.
985 gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
986 dock_container_);
987 const gfx::Rect work_area = display.work_area();
989 // Evict the window if it can no longer be docked because of its height.
990 if (!CanDockWindow(window, DOCKED_ALIGNMENT_NONE)) {
991 window_state->Restore();
992 RecordUmaAction(DOCKED_ACTION_EVICT, event_source_);
993 return;
995 gfx::Rect bounds(window->bounds());
996 bounds.set_y(work_area.bottom());
997 window->SetBounds(bounds);
998 window->Show();
999 MaybeMinimizeChildrenExcept(window);
1000 RecordUmaAction(DOCKED_ACTION_RESTORE, event_source_);
1003 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action,
1004 DockedActionSource source) {
1005 if (action == DOCKED_ACTION_NONE)
1006 return;
1007 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action, DOCKED_ACTION_COUNT);
1008 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source,
1009 DOCKED_ACTION_SOURCE_COUNT);
1010 base::Time time_now = base::Time::Now();
1011 base::TimeDelta time_between_use = time_now - last_action_time_;
1012 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse",
1013 time_between_use.InSeconds(),
1015 base::TimeDelta::FromHours(10).InSeconds(),
1016 100);
1017 last_action_time_ = time_now;
1018 int docked_all_count = 0;
1019 int docked_visible_count = 0;
1020 int docked_panels_count = 0;
1021 int large_windows_count = 0;
1022 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
1023 const aura::Window* window(dock_container_->children()[i]);
1024 if (IsPopupOrTransient(window))
1025 continue;
1026 docked_all_count++;
1027 if (!IsUsedByLayout(window))
1028 continue;
1029 docked_visible_count++;
1030 if (window->type() == ui::wm::WINDOW_TYPE_PANEL)
1031 docked_panels_count++;
1032 const wm::WindowState* window_state = wm::GetWindowState(window);
1033 if (window_state->HasRestoreBounds()) {
1034 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen();
1035 if (restore_bounds.width() > kMaxDockWidth)
1036 large_windows_count++;
1039 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count);
1040 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count);
1041 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count);
1042 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count);
1045 void DockedWindowLayoutManager::UpdateDockedWidth(int width) {
1046 if (docked_width_ == width)
1047 return;
1048 docked_width_ = width;
1049 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_);
1052 void DockedWindowLayoutManager::OnDraggedWindowDocked(aura::Window* window) {
1053 DCHECK(!is_dragged_window_docked_);
1054 is_dragged_window_docked_ = true;
1057 void DockedWindowLayoutManager::OnDraggedWindowUndocked() {
1058 DCHECK (is_dragged_window_docked_);
1059 is_dragged_window_docked_ = false;
1062 bool DockedWindowLayoutManager::IsAnyWindowDocked() {
1063 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE;
1066 DockedAlignment DockedWindowLayoutManager::GetEdgeNearestWindow(
1067 const aura::Window* window) const {
1068 const gfx::Rect& bounds(window->GetBoundsInScreen());
1069 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
1070 return (abs(bounds.x() - container_bounds.x()) <
1071 abs(bounds.right() - container_bounds.right())) ?
1072 DOCKED_ALIGNMENT_LEFT : DOCKED_ALIGNMENT_RIGHT;
1075 void DockedWindowLayoutManager::Relayout() {
1076 if (in_layout_)
1077 return;
1078 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_)
1079 return;
1080 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
1082 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1083 aura::Window* active_window = NULL;
1084 std::vector<WindowWithHeight> visible_windows;
1085 for (size_t i = 0; i < dock_container_->children().size(); ++i) {
1086 aura::Window* window(dock_container_->children()[i]);
1088 if (!IsUsedByLayout(window) || window == dragged_window_)
1089 continue;
1091 // If the shelf is currently hidden (full-screen mode), hide window until
1092 // full-screen mode is exited.
1093 if (in_fullscreen_) {
1094 // The call to Hide does not set the minimize property, so the window will
1095 // be restored when the shelf becomes visible again.
1096 window->Hide();
1097 continue;
1099 if (window->HasFocus() ||
1100 window->Contains(
1101 aura::client::GetFocusClient(window)->GetFocusedWindow())) {
1102 DCHECK(!active_window);
1103 active_window = window;
1105 visible_windows.push_back(WindowWithHeight(window));
1107 // Consider docked dragged_window_ when fanning out other child windows.
1108 if (is_dragged_window_docked_) {
1109 visible_windows.push_back(WindowWithHeight(dragged_window_));
1110 DCHECK(!active_window);
1111 active_window = dragged_window_;
1114 // Position docked windows as well as the window being dragged.
1115 gfx::Rect work_area =
1116 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1117 if (shelf_observer_)
1118 work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
1119 int available_room = CalculateWindowHeightsAndRemainingRoom(work_area,
1120 &visible_windows);
1121 FanOutChildren(work_area,
1122 CalculateIdealWidth(visible_windows),
1123 available_room,
1124 &visible_windows);
1126 // After the first Relayout allow the windows to change their order easier
1127 // since we know they are docked.
1128 is_dragged_from_dock_ = true;
1129 UpdateStacking(active_window);
1132 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom(
1133 const gfx::Rect work_area,
1134 std::vector<WindowWithHeight>* visible_windows) {
1135 int available_room = work_area.height();
1136 int remaining_windows = visible_windows->size();
1137 int gap_height = remaining_windows > 1 ? kMinDockGap : 0;
1139 // Sort windows by their minimum heights and calculate target heights.
1140 std::sort(visible_windows->begin(), visible_windows->end(),
1141 CompareMinimumHeight());
1142 // Distribute the free space among the docked windows. Since the windows are
1143 // sorted (tall windows first) we can now assume that any window which
1144 // required more space than the current window will have already been
1145 // accounted for previously in this loop, so we can safely give that window
1146 // its proportional share of the remaining space.
1147 for (std::vector<WindowWithHeight>::reverse_iterator iter =
1148 visible_windows->rbegin();
1149 iter != visible_windows->rend(); ++iter) {
1150 iter->height_ = GetWindowHeightCloseTo(
1151 iter->window(),
1152 (available_room + gap_height) / remaining_windows - gap_height);
1153 available_room -= (iter->height_ + gap_height);
1154 remaining_windows--;
1156 return available_room + gap_height;
1159 int DockedWindowLayoutManager::CalculateIdealWidth(
1160 const std::vector<WindowWithHeight>& visible_windows) {
1161 int smallest_max_width = kMaxDockWidth;
1162 int largest_min_width = kMinDockWidth;
1163 // Ideal width of the docked area is as close to kIdealWidth as possible
1164 // while still respecting the minimum and maximum width restrictions on the
1165 // individual docked windows as well as the width that was possibly set by a
1166 // user (which needs to be preserved when dragging and rearranging windows).
1167 for (std::vector<WindowWithHeight>::const_iterator iter =
1168 visible_windows.begin();
1169 iter != visible_windows.end(); ++iter) {
1170 const aura::Window* window = iter->window();
1171 int min_window_width = window->bounds().width();
1172 int max_window_width = min_window_width;
1173 if (!wm::GetWindowState(window)->bounds_changed_by_user()) {
1174 min_window_width = GetWindowWidthCloseTo(window, kMinDockWidth);
1175 max_window_width = GetWindowWidthCloseTo(window, kMaxDockWidth);
1177 largest_min_width = std::max(largest_min_width, min_window_width);
1178 smallest_max_width = std::min(smallest_max_width, max_window_width);
1180 int ideal_width = std::max(largest_min_width,
1181 std::min(smallest_max_width, kIdealWidth));
1182 // Restrict docked area width regardless of window restrictions.
1183 ideal_width = std::max(std::min(ideal_width, kMaxDockWidth), kMinDockWidth);
1184 return ideal_width;
1187 void DockedWindowLayoutManager::FanOutChildren(
1188 const gfx::Rect& work_area,
1189 int ideal_docked_width,
1190 int available_room,
1191 std::vector<WindowWithHeight>* visible_windows) {
1192 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1194 // Calculate initial vertical offset and the gap or overlap between windows.
1195 const int num_windows = visible_windows->size();
1196 const float delta = static_cast<float>(available_room) /
1197 ((available_room > 0 || num_windows <= 1) ?
1198 num_windows + 1 : num_windows - 1);
1199 float y_pos = work_area.y() + ((delta > 0) ? delta : 0);
1201 // Docked area is shown only if there is at least one non-dragged visible
1202 // docked window.
1203 int new_width = ideal_docked_width;
1204 if (visible_windows->empty() ||
1205 (visible_windows->size() == 1 &&
1206 (*visible_windows)[0].window() == dragged_window_)) {
1207 new_width = 0;
1209 UpdateDockedWidth(new_width);
1210 // Sort windows by their center positions and fan out overlapping
1211 // windows.
1212 std::sort(visible_windows->begin(), visible_windows->end(),
1213 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : NULL,
1214 dock_container_,
1215 delta));
1216 for (std::vector<WindowWithHeight>::iterator iter = visible_windows->begin();
1217 iter != visible_windows->end(); ++iter) {
1218 aura::Window* window = iter->window();
1219 gfx::Rect bounds = ScreenUtil::ConvertRectToScreen(
1220 dock_container_, window->GetTargetBounds());
1221 // A window is extended or shrunk to be as close as possible to the ideal
1222 // docked area width. Windows that were resized by a user are kept at their
1223 // existing size.
1224 // This also enforces the min / max restrictions on the docked area width.
1225 bounds.set_width(GetWindowWidthCloseTo(
1226 window,
1227 wm::GetWindowState(window)->bounds_changed_by_user() ?
1228 bounds.width() : ideal_docked_width));
1229 DCHECK_LE(bounds.width(), ideal_docked_width);
1231 DockedAlignment alignment = alignment_;
1232 if (alignment == DOCKED_ALIGNMENT_NONE && window == dragged_window_)
1233 alignment = GetEdgeNearestWindow(window);
1235 // Fan out windows evenly distributing the overlap or remaining free space.
1236 bounds.set_height(iter->height_);
1237 bounds.set_y(std::max(work_area.y(),
1238 std::min(work_area.bottom() - bounds.height(),
1239 static_cast<int>(y_pos + 0.5))));
1240 y_pos += bounds.height() + delta + kMinDockGap;
1242 // All docked windows other than the one currently dragged remain stuck
1243 // to the screen edge (flush with the edge or centered in the dock area).
1244 switch (alignment) {
1245 case DOCKED_ALIGNMENT_LEFT:
1246 bounds.set_x(dock_bounds.x() +
1247 (ideal_docked_width - bounds.width()) / 2);
1248 break;
1249 case DOCKED_ALIGNMENT_RIGHT:
1250 bounds.set_x(dock_bounds.right() -
1251 (ideal_docked_width + bounds.width()) / 2);
1252 break;
1253 case DOCKED_ALIGNMENT_NONE:
1254 break;
1256 if (window == dragged_window_) {
1257 dragged_bounds_ = bounds;
1258 continue;
1260 // If the following asserts it is probably because not all the children
1261 // have been removed when dock was closed.
1262 DCHECK_NE(alignment_, DOCKED_ALIGNMENT_NONE);
1263 bounds = ScreenUtil::ConvertRectFromScreen(dock_container_, bounds);
1264 if (bounds != window->GetTargetBounds()) {
1265 ui::Layer* layer = window->layer();
1266 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
1267 slide_settings.SetPreemptionStrategy(
1268 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
1269 slide_settings.SetTransitionDuration(
1270 base::TimeDelta::FromMilliseconds(kSlideDurationMs));
1271 SetChildBoundsDirect(window, bounds);
1276 void DockedWindowLayoutManager::UpdateDockBounds(
1277 DockedWindowLayoutManagerObserver::Reason reason) {
1278 int dock_inset = docked_width_ + (docked_width_ > 0 ? kMinDockGap : 0);
1279 const gfx::Rect work_area =
1280 Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
1281 gfx::Rect bounds = gfx::Rect(
1282 alignment_ == DOCKED_ALIGNMENT_RIGHT && dock_inset > 0 ?
1283 dock_container_->bounds().right() - dock_inset:
1284 dock_container_->bounds().x(),
1285 dock_container_->bounds().y(),
1286 dock_inset,
1287 work_area.height());
1288 docked_bounds_ = bounds +
1289 dock_container_->GetBoundsInScreen().OffsetFromOrigin();
1290 FOR_EACH_OBSERVER(
1291 DockedWindowLayoutManagerObserver,
1292 observer_list_,
1293 OnDockBoundsChanging(bounds, reason));
1294 // Show or hide background for docked area.
1295 gfx::Rect background_bounds(docked_bounds_);
1296 if (shelf_observer_)
1297 background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
1298 background_widget_->SetBackgroundBounds(background_bounds, alignment_);
1299 if (docked_width_ > 0)
1300 background_widget_->Show();
1301 else
1302 background_widget_->Hide();
1305 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
1306 if (!active_window) {
1307 if (!last_active_window_)
1308 return;
1309 active_window = last_active_window_;
1312 // Windows are stacked like a deck of cards:
1313 // ,------.
1314 // |,------.|
1315 // |,------.|
1316 // | active |
1317 // | window |
1318 // |`------'|
1319 // |`------'|
1320 // `------'
1321 // Use the middle of each window to figure out how to stack the window.
1322 // This allows us to update the stacking when a window is being dragged around
1323 // by the titlebar.
1324 std::map<int, aura::Window*> window_ordering;
1325 for (aura::Window::Windows::const_iterator it =
1326 dock_container_->children().begin();
1327 it != dock_container_->children().end(); ++it) {
1328 if (!IsUsedByLayout(*it) ||
1329 ((*it) == dragged_window_ && !is_dragged_window_docked_)) {
1330 continue;
1332 gfx::Rect bounds = (*it)->bounds();
1333 window_ordering.insert(std::make_pair(bounds.y() + bounds.height() / 2,
1334 *it));
1336 int active_center_y = active_window->bounds().CenterPoint().y();
1338 aura::Window* previous_window = NULL;
1339 for (std::map<int, aura::Window*>::const_iterator it =
1340 window_ordering.begin();
1341 it != window_ordering.end() && it->first < active_center_y; ++it) {
1342 if (previous_window)
1343 dock_container_->StackChildAbove(it->second, previous_window);
1344 previous_window = it->second;
1346 for (std::map<int, aura::Window*>::const_reverse_iterator it =
1347 window_ordering.rbegin();
1348 it != window_ordering.rend() && it->first > active_center_y; ++it) {
1349 if (previous_window)
1350 dock_container_->StackChildAbove(it->second, previous_window);
1351 previous_window = it->second;
1354 if (previous_window && active_window->parent() == dock_container_)
1355 dock_container_->StackChildAbove(active_window, previous_window);
1356 if (active_window != dragged_window_)
1357 last_active_window_ = active_window;
1360 ////////////////////////////////////////////////////////////////////////////////
1361 // keyboard::KeyboardControllerObserver implementation:
1363 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
1364 const gfx::Rect& keyboard_bounds) {
1365 // This bounds change will have caused a change to the Shelf which does not
1366 // propagate automatically to this class, so manually recalculate bounds.
1367 Relayout();
1368 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING);
1371 } // namespace ash