Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: ash/common/wm/dock/docked_window_layout_manager.cc

Issue 2700523004: Remove docked windows entirely in M59. (Closed)
Patch Set: Rebase Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "ash/common/wm/dock/docked_window_layout_manager.h"
6
7 #include "ash/animation/animation_change_type.h"
8 #include "ash/common/shelf/shelf_background_animator.h"
9 #include "ash/common/shelf/shelf_background_animator_observer.h"
10 #include "ash/common/shelf/shelf_constants.h"
11 #include "ash/common/shelf/wm_shelf.h"
12 #include "ash/common/shelf/wm_shelf_observer.h"
13 #include "ash/common/wm/overview/window_selector_controller.h"
14 #include "ash/common/wm/window_animation_types.h"
15 #include "ash/common/wm/window_parenting_utils.h"
16 #include "ash/common/wm/window_resizer.h"
17 #include "ash/common/wm/window_state.h"
18 #include "ash/common/wm_shell.h"
19 #include "ash/common/wm_window.h"
20 #include "ash/public/cpp/shell_window_ids.h"
21 #include "ash/resources/grit/ash_resources.h"
22 #include "ash/root_window_controller.h"
23 #include "ash/shell.h"
24 #include "ash/wm/window_state_aura.h"
25 #include "base/auto_reset.h"
26 #include "base/metrics/histogram_macros.h"
27 #include "third_party/skia/include/core/SkColor.h"
28 #include "ui/aura/window.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/compositor/scoped_layer_animation_settings.h"
31 #include "ui/display/display.h"
32 #include "ui/display/screen.h"
33 #include "ui/views/background.h"
34 #include "ui/wm/core/coordinate_conversion.h"
35 #include "ui/wm/core/window_animations.h"
36 #include "ui/wm/public/activation_client.h"
37
38 namespace ash {
39
40 // Minimum, maximum width of the dock area and a width of the gap
41 // static
42 const int DockedWindowLayoutManager::kMaxDockWidth = 360;
43 // static
44 const int DockedWindowLayoutManager::kMinDockWidth = 200;
45 // static
46 const int DockedWindowLayoutManager::kMinDockGap = 2;
47 // static
48 const int DockedWindowLayoutManager::kIdealWidth = 250;
49 const int kMinimumHeight = 250;
50 const int kSlideDurationMs = 120;
51 const int kFadeDurationMs = 60;
52 const int kMinimizeDurationMs = 720;
53
54 class DockedBackgroundWidget : public views::Widget,
55 public WmShelfObserver,
56 public ShelfBackgroundAnimatorObserver {
57 public:
58 explicit DockedBackgroundWidget(DockedWindowLayoutManager* manager)
59 : manager_(manager),
60 alignment_(DOCKED_ALIGNMENT_NONE),
61 background_animator_(SHELF_BACKGROUND_DEFAULT,
62 nullptr,
63 Shell::GetInstance()->wallpaper_controller()),
64 opaque_background_(ui::LAYER_SOLID_COLOR),
65 visible_background_type_(manager_->shelf()->GetBackgroundType()),
66 visible_background_change_type_(AnimationChangeType::IMMEDIATE) {
67 manager_->shelf()->AddObserver(this);
68 InitWidget(manager_->dock_container());
69
70 background_animator_.AddObserver(this);
71 }
72
73 ~DockedBackgroundWidget() override {
74 background_animator_.RemoveObserver(this);
75 manager_->shelf()->RemoveObserver(this);
76 }
77
78 // Sets widget bounds and sizes opaque background layer to fill the widget.
79 void SetBackgroundBounds(const gfx::Rect& bounds, DockedAlignment alignment) {
80 SetBounds(bounds);
81 opaque_background_.SetBounds(gfx::Rect(bounds.size()));
82 alignment_ = alignment;
83 }
84
85 private:
86 // views::Widget:
87 void OnNativeWidgetVisibilityChanged(bool visible) override {
88 views::Widget::OnNativeWidgetVisibilityChanged(visible);
89 UpdateBackground();
90 }
91
92 // ShelfBackgroundAnimatorObserver:
93 void UpdateShelfBackground(SkColor color) override {
94 opaque_background_.SetColor(color);
95 }
96
97 // WmShelfObserver:
98 void OnBackgroundTypeChanged(ShelfBackgroundType background_type,
99 AnimationChangeType change_type) override {
100 // Sets the background type. Starts an animation to transition to
101 // |background_type| if the widget is visible. If the widget is not visible,
102 // the animation is postponed till the widget becomes visible.
103 visible_background_type_ = background_type;
104 visible_background_change_type_ = change_type;
105 if (IsVisible())
106 UpdateBackground();
107 }
108
109 void InitWidget(WmWindow* parent) {
110 views::Widget::InitParams params;
111 params.type = views::Widget::InitParams::TYPE_POPUP;
112 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
113 params.keep_on_top = false;
114 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
115 params.accept_events = false;
116 set_focus_on_creation(false);
117 parent->GetRootWindowController()->ConfigureWidgetInitParamsForContainer(
118 this, parent->GetShellWindowId(), &params);
119 Init(params);
120 SetVisibilityChangedAnimationsEnabled(false);
121 WmWindow* wm_window = WmWindow::Get(this->GetNativeWindow());
122 wm_window->SetLockedToRoot(true);
123 opaque_background_.SetColor(SK_ColorBLACK);
124 opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
125 opaque_background_.SetOpacity(0.0f);
126 wm_window->GetLayer()->Add(&opaque_background_);
127
128 // This background should be explicitly stacked below any windows already in
129 // the dock, otherwise the z-order is set by the order in which windows were
130 // added to the container, and UpdateStacking only manages user windows, not
131 // the background widget.
132 parent->StackChildAtBottom(wm_window);
133 }
134
135 // Transitions to |visible_background_type_| if the widget is visible and to
136 // SHELF_BACKGROUND_DEFAULT if it is not.
137 void UpdateBackground() {
138 ShelfBackgroundType background_type =
139 IsVisible() ? visible_background_type_ : SHELF_BACKGROUND_DEFAULT;
140 AnimationChangeType change_type = IsVisible()
141 ? visible_background_change_type_
142 : AnimationChangeType::IMMEDIATE;
143 background_animator_.PaintBackground(background_type, change_type);
144 SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
145 }
146
147 DockedWindowLayoutManager* manager_;
148
149 DockedAlignment alignment_;
150
151 // The animator for the background transitions.
152 ShelfBackgroundAnimator background_animator_;
153
154 // TODO(bruthig): Remove opaque_background_ (see https://crbug.com/621551).
155 // Solid black background that can be made fully opaque.
156 ui::Layer opaque_background_;
157
158 // The background type to use when the widget is visible. When not visible,
159 // the widget uses SHELF_BACKGROUND_DEFAULT.
160 ShelfBackgroundType visible_background_type_;
161
162 // Whether the widget should animate to |visible_background_type_|.
163 AnimationChangeType visible_background_change_type_;
164
165 DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
166 };
167
168 namespace {
169
170 // Returns true if a window is a popup or a transient child.
171 bool IsPopupOrTransient(const WmWindow* window) {
172 return (window->GetType() == ui::wm::WINDOW_TYPE_POPUP ||
173 window->GetTransientParent());
174 }
175
176 // Certain windows (minimized, hidden or popups) are not docked and are ignored
177 // by layout logic even when they are children of a docked container.
178 bool IsWindowDocked(const WmWindow* window) {
179 return (window->IsVisible() && !window->GetWindowState()->IsMinimized() &&
180 !IsPopupOrTransient(window));
181 }
182
183 void UndockWindow(WmWindow* window) {
184 gfx::Rect previous_bounds = window->GetBounds();
185 aura::Window* old_parent = window->aura_window()->parent();
186 window->SetParentUsingContext(window, gfx::Rect());
187 if (window->aura_window()->parent() != old_parent) {
188 wm::ReparentTransientChildrenOfChild(window->aura_window(), old_parent,
189 window->aura_window()->parent());
190 }
191 // Start maximize or fullscreen (affecting packaged apps) animation from
192 // previous window bounds.
193 window->GetLayer()->SetBounds(previous_bounds);
194 }
195
196 // Returns width that is as close as possible to |target_width| while being
197 // consistent with docked min and max restrictions and respects the |window|'s
198 // minimum and maximum size.
199 int GetWindowWidthCloseTo(const WmWindow* window, int target_width) {
200 if (!window->GetWindowState()->CanResize()) {
201 DCHECK_LE(window->GetBounds().width(),
202 DockedWindowLayoutManager::kMaxDockWidth);
203 return window->GetBounds().width();
204 }
205 int width = std::max(
206 DockedWindowLayoutManager::kMinDockWidth,
207 std::min(target_width, DockedWindowLayoutManager::kMaxDockWidth));
208 width = std::max(width, window->GetMinimumSize().width());
209 if (window->GetMaximumSize().width() != 0)
210 width = std::min(width, window->GetMaximumSize().width());
211 DCHECK_LE(width, DockedWindowLayoutManager::kMaxDockWidth);
212 return width;
213 }
214
215 // Returns height that is as close as possible to |target_height| while
216 // respecting the |window|'s minimum and maximum size.
217 int GetWindowHeightCloseTo(const WmWindow* window, int target_height) {
218 if (!window->GetWindowState()->CanResize())
219 return window->GetBounds().height();
220 int minimum_height =
221 std::max(kMinimumHeight, window->GetMinimumSize().height());
222 int maximum_height = window->GetMaximumSize().height();
223 if (minimum_height)
224 target_height = std::max(target_height, minimum_height);
225 if (maximum_height)
226 target_height = std::min(target_height, maximum_height);
227 return target_height;
228 }
229
230 } // namespace
231
232 struct DockedWindowLayoutManager::WindowWithHeight {
233 explicit WindowWithHeight(WmWindow* window)
234 : window(window), height(window->GetBounds().height()) {}
235 WmWindow* window;
236 int height;
237 };
238
239 // A functor used to sort the windows in order of their minimum height.
240 struct DockedWindowLayoutManager::CompareMinimumHeight {
241 bool operator()(const WindowWithHeight& win1, const WindowWithHeight& win2) {
242 return GetWindowHeightCloseTo(win1.window, 0) <
243 GetWindowHeightCloseTo(win2.window, 0);
244 }
245 };
246
247 // A functor used to sort the windows in order of their center Y position.
248 // |delta| is a pre-calculated distance from the bottom of one window to the top
249 // of the next. Its value can be positive (gap) or negative (overlap).
250 // Half of |delta| is used as a transition point at which windows could ideally
251 // swap positions.
252 struct DockedWindowLayoutManager::CompareWindowPos {
253 CompareWindowPos(WmWindow* dragged_window,
254 WmWindow* docked_container,
255 float delta)
256 : dragged_window_(dragged_window),
257 docked_container_(docked_container),
258 delta_(delta / 2) {}
259
260 bool operator()(const WindowWithHeight& window_with_height1,
261 const WindowWithHeight& window_with_height2) {
262 // Use target coordinates since animations may be active when windows are
263 // reordered.
264 WmWindow* win1(window_with_height1.window);
265 WmWindow* win2(window_with_height2.window);
266 gfx::Rect win1_bounds =
267 docked_container_->ConvertRectToScreen(win1->GetTargetBounds());
268 gfx::Rect win2_bounds =
269 docked_container_->ConvertRectToScreen(win2->GetTargetBounds());
270 win1_bounds.set_height(window_with_height1.height);
271 win2_bounds.set_height(window_with_height2.height);
272 // If one of the windows is the |dragged_window_| attempt to make an
273 // earlier swap between the windows than just based on their centers.
274 // This is possible if the dragged window is at least as tall as the other
275 // window.
276 if (win1 == dragged_window_)
277 return compare_two_windows(win1_bounds, win2_bounds);
278 if (win2 == dragged_window_)
279 return !compare_two_windows(win2_bounds, win1_bounds);
280 // Otherwise just compare the centers.
281 return win1_bounds.CenterPoint().y() < win2_bounds.CenterPoint().y();
282 }
283
284 // Based on center point tries to deduce where the drag is coming from.
285 // When dragging from below up the transition point is lower.
286 // When dragging from above down the transition point is higher.
287 bool compare_bounds(const gfx::Rect& dragged, const gfx::Rect& other) {
288 if (dragged.CenterPoint().y() < other.CenterPoint().y())
289 return dragged.CenterPoint().y() < other.y() - delta_;
290 return dragged.CenterPoint().y() < other.bottom() + delta_;
291 }
292
293 // Performs comparison both ways and selects stable result.
294 bool compare_two_windows(const gfx::Rect& bounds1, const gfx::Rect& bounds2) {
295 // Try comparing windows in both possible orders and see if the comparison
296 // is stable.
297 bool result1 = compare_bounds(bounds1, bounds2);
298 bool result2 = compare_bounds(bounds2, bounds1);
299 if (result1 != result2)
300 return result1;
301
302 // Otherwise it is not possible to be sure that the windows will not bounce.
303 // In this case just compare the centers.
304 return bounds1.CenterPoint().y() < bounds2.CenterPoint().y();
305 }
306
307 private:
308 WmWindow* dragged_window_;
309 WmWindow* docked_container_;
310 float delta_;
311 };
312
313 ////////////////////////////////////////////////////////////////////////////////
314 // A class that observes shelf for bounds changes.
315 class DockedWindowLayoutManager::ShelfWindowObserver
316 : public aura::WindowObserver {
317 public:
318 explicit ShelfWindowObserver(DockedWindowLayoutManager* docked_layout_manager)
319 : docked_layout_manager_(docked_layout_manager) {
320 DCHECK(docked_layout_manager_->shelf()->GetWindow());
321 docked_layout_manager_->shelf()->GetWindow()->aura_window()->AddObserver(
322 this);
323 }
324
325 ~ShelfWindowObserver() override {
326 if (docked_layout_manager_->shelf() &&
327 docked_layout_manager_->shelf()->GetWindow()) {
328 docked_layout_manager_->shelf()
329 ->GetWindow()
330 ->aura_window()
331 ->RemoveObserver(this);
332 }
333 }
334
335 // aura::WindowObserver:
336 void OnWindowBoundsChanged(aura::Window* window,
337 const gfx::Rect& old_bounds,
338 const gfx::Rect& new_bounds) override {
339 shelf_bounds_in_screen_ = new_bounds;
340 ::wm::ConvertRectToScreen(window->parent(), &shelf_bounds_in_screen_);
341
342 // When the shelf is auto-hidden, it has an invisible height of 3px used
343 // as a hit region which is specific to Chrome OS MD (for non-MD, the 3
344 // pixels are visible). In computing the work area we should consider a
345 // hidden shelf as having a height of 0 (for non-MD, shelf height is 3).
346 if (docked_layout_manager_->shelf()->GetAutoHideState() ==
347 ShelfAutoHideState::SHELF_AUTO_HIDE_HIDDEN) {
348 shelf_bounds_in_screen_.set_height(
349 GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE));
350 }
351 docked_layout_manager_->OnShelfBoundsChanged();
352 }
353
354 const gfx::Rect& shelf_bounds_in_screen() const {
355 return shelf_bounds_in_screen_;
356 }
357
358 private:
359 DockedWindowLayoutManager* docked_layout_manager_;
360 gfx::Rect shelf_bounds_in_screen_;
361
362 DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
363 };
364
365 ////////////////////////////////////////////////////////////////////////////////
366 // DockedWindowLayoutManager public implementation:
367 DockedWindowLayoutManager::DockedWindowLayoutManager(WmWindow* dock_container)
368 : dock_container_(dock_container),
369 root_window_controller_(dock_container->GetRootWindowController()),
370 in_layout_(false),
371 dragged_window_(nullptr),
372 is_dragged_window_docked_(false),
373 is_dragged_from_dock_(false),
374 shelf_(nullptr),
375 in_fullscreen_(root_window_controller_->GetWorkspaceWindowState() ==
376 wm::WORKSPACE_WINDOW_STATE_FULL_SCREEN),
377 docked_width_(0),
378 in_overview_(false),
379 alignment_(DOCKED_ALIGNMENT_NONE),
380 preferred_alignment_(DOCKED_ALIGNMENT_NONE),
381 event_source_(DOCKED_ACTION_SOURCE_UNKNOWN),
382 last_active_window_(nullptr),
383 last_action_time_(base::Time::Now()),
384 background_widget_(nullptr) {
385 DCHECK(dock_container);
386 Shell::GetInstance()->AddShellObserver(this);
387 Shell::GetInstance()->activation_client()->AddObserver(this);
388 display::Screen::GetScreen()->AddObserver(this);
389 }
390
391 DockedWindowLayoutManager::~DockedWindowLayoutManager() {
392 Shutdown();
393 }
394
395 // static
396 DockedWindowLayoutManager* DockedWindowLayoutManager::Get(WmWindow* window) {
397 if (!window)
398 return nullptr;
399
400 WmWindow* root = window->GetRootWindow();
401 return static_cast<DockedWindowLayoutManager*>(
402 root->GetChildByShellWindowId(kShellWindowId_DockedContainer)
403 ->GetLayoutManager());
404 }
405
406 void DockedWindowLayoutManager::Shutdown() {
407 background_widget_.reset();
408 shelf_observer_.reset();
409 shelf_ = nullptr;
410 for (WmWindow* child : dock_container_->GetChildren()) {
411 child->aura_window()->RemoveObserver(this);
412 child->GetWindowState()->RemoveObserver(this);
413 }
414 Shell::GetInstance()->activation_client()->RemoveObserver(this);
415 Shell::GetInstance()->RemoveShellObserver(this);
416 display::Screen::GetScreen()->RemoveObserver(this);
417 }
418
419 void DockedWindowLayoutManager::AddObserver(
420 DockedWindowLayoutManagerObserver* observer) {
421 observer_list_.AddObserver(observer);
422 }
423
424 void DockedWindowLayoutManager::RemoveObserver(
425 DockedWindowLayoutManagerObserver* observer) {
426 observer_list_.RemoveObserver(observer);
427 }
428
429 void DockedWindowLayoutManager::StartDragging(WmWindow* window) {
430 DCHECK(!dragged_window_);
431 dragged_window_ = window;
432 DCHECK(!IsPopupOrTransient(window));
433 // Start observing a window unless it is docked container's child in which
434 // case it is already observed.
435 wm::WindowState* dragged_state = dragged_window_->GetWindowState();
436 if (dragged_window_->GetParent() != dock_container_) {
437 dragged_window_->aura_window()->AddObserver(this);
438 dragged_state->AddObserver(this);
439 } else if (!IsAnyWindowDocked() && dragged_state->drag_details() &&
440 !(dragged_state->drag_details()->bounds_change &
441 WindowResizer::kBoundsChange_Resizes)) {
442 // If there are no other docked windows clear alignment when a docked window
443 // is moved (but not when it is resized or the window could get undocked
444 // when resized away from the edge while docked).
445 alignment_ = DOCKED_ALIGNMENT_NONE;
446 }
447 is_dragged_from_dock_ = window->GetParent() == dock_container_;
448 DCHECK(!is_dragged_window_docked_);
449
450 // Resize all windows that are flush with the dock edge together if one of
451 // them gets resized.
452 if (dragged_window_->GetBounds().width() == docked_width_ &&
453 (dragged_state->drag_details()->bounds_change &
454 WindowResizer::kBoundsChange_Resizes) &&
455 (dragged_state->drag_details()->size_change_direction &
456 WindowResizer::kBoundsChangeDirection_Horizontal)) {
457 for (WmWindow* window1 : dock_container_->GetChildren()) {
458 if (IsWindowDocked(window1) && window1 != dragged_window_ &&
459 window1->GetBounds().width() == docked_width_) {
460 window1->GetWindowState()->set_bounds_changed_by_user(false);
461 }
462 }
463 }
464 }
465
466 void DockedWindowLayoutManager::DockDraggedWindow(WmWindow* window) {
467 DCHECK(!IsPopupOrTransient(window));
468 OnDraggedWindowDocked(window);
469 Relayout();
470 }
471
472 void DockedWindowLayoutManager::UndockDraggedWindow() {
473 DCHECK(!IsPopupOrTransient(dragged_window_));
474 OnDraggedWindowUndocked();
475 Relayout();
476 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
477 is_dragged_from_dock_ = false;
478 }
479
480 void DockedWindowLayoutManager::FinishDragging(DockedAction action,
481 DockedActionSource source) {
482 DCHECK(dragged_window_);
483 DCHECK(!IsPopupOrTransient(dragged_window_));
484 if (is_dragged_window_docked_)
485 OnDraggedWindowUndocked();
486 DCHECK(!is_dragged_window_docked_);
487 // Stop observing a window unless it is docked container's child in which
488 // case it needs to keep being observed after the drag completes.
489 if (dragged_window_->GetParent() != dock_container_) {
490 dragged_window_->aura_window()->RemoveObserver(this);
491 dragged_window_->GetWindowState()->RemoveObserver(this);
492 if (last_active_window_ == dragged_window_)
493 last_active_window_ = nullptr;
494 } else {
495 // If this is the first window that got docked by a move update alignment.
496 if (alignment_ == DOCKED_ALIGNMENT_NONE)
497 alignment_ = GetEdgeNearestWindow(dragged_window_);
498 // A window is no longer dragged and is a child.
499 // When a window becomes a child at drag start this is
500 // the only opportunity we will have to enforce a window
501 // count limit so do it here.
502 MaybeMinimizeChildrenExcept(dragged_window_);
503 }
504 dragged_window_ = nullptr;
505 dragged_bounds_ = gfx::Rect();
506 Relayout();
507 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
508 RecordUmaAction(action, source);
509 }
510
511 void DockedWindowLayoutManager::SetShelf(WmShelf* shelf) {
512 DCHECK(!shelf_);
513 shelf_ = shelf;
514 shelf_observer_.reset(new ShelfWindowObserver(this));
515 }
516
517 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
518 const WmWindow* window) const {
519 const gfx::Rect& bounds(window->GetBoundsInScreen());
520
521 // Test overlap with an existing docked area first.
522 if (docked_bounds_.Intersects(bounds) &&
523 alignment_ != DOCKED_ALIGNMENT_NONE) {
524 // A window is being added to other docked windows (on the same side).
525 return alignment_;
526 }
527
528 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
529 if (bounds.x() <= container_bounds.x() &&
530 bounds.right() > container_bounds.x()) {
531 return DOCKED_ALIGNMENT_LEFT;
532 } else if (bounds.x() < container_bounds.right() &&
533 bounds.right() >= container_bounds.right()) {
534 return DOCKED_ALIGNMENT_RIGHT;
535 }
536 return DOCKED_ALIGNMENT_NONE;
537 }
538
539 DockedAlignment DockedWindowLayoutManager::CalculateAlignment() const {
540 return CalculateAlignmentExcept(dragged_window_);
541 }
542
543 DockedAlignment DockedWindowLayoutManager::CalculateAlignmentExcept(
544 const WmWindow* window) const {
545 // Find a child that is not the window being queried and is not a popup.
546 // If such exists the current alignment is returned - even if some of the
547 // children are hidden or minimized (so they can be restored without losing
548 // the docked state).
549 for (WmWindow* child : dock_container_->GetChildren()) {
550 if (window != child && !IsPopupOrTransient(child))
551 return alignment_;
552 }
553 // No docked windows remain other than possibly the window being queried.
554 // Return |NONE| to indicate that windows may get docked on either side.
555 return DOCKED_ALIGNMENT_NONE;
556 }
557
558 bool DockedWindowLayoutManager::CanDockWindow(
559 WmWindow* window,
560 DockedAlignment desired_alignment) {
561 // Don't allow interactive docking of windows with transient parents such as
562 // modal browser dialogs. Prevent docking of panels attached to shelf during
563 // the drag.
564 wm::WindowState* window_state = window->GetWindowState();
565 bool should_attach_to_shelf =
566 window_state->drag_details() &&
567 window_state->drag_details()->should_attach_to_shelf;
568 if (IsPopupOrTransient(window) || should_attach_to_shelf)
569 return false;
570 // If a window is wide and cannot be resized down to maximum width allowed
571 // then it cannot be docked.
572 // TODO(varkha). Prevent windows from changing size programmatically while
573 // they are docked. The size will take effect only once a window is undocked.
574 // See http://crbug.com/307792.
575 if (window->GetBounds().width() > kMaxDockWidth &&
576 (!window_state->CanResize() ||
577 (window->GetMinimumSize().width() != 0 &&
578 window->GetMinimumSize().width() > kMaxDockWidth))) {
579 return false;
580 }
581 // If a window is tall and cannot be resized down to maximum height allowed
582 // then it cannot be docked.
583 const gfx::Rect work_area =
584 dock_container_->GetDisplayNearestWindow().work_area();
585 if (GetWindowHeightCloseTo(window, work_area.height()) > work_area.height())
586 return false;
587 // Cannot dock on the other size from an existing dock.
588 const DockedAlignment alignment = CalculateAlignmentExcept(window);
589 if (desired_alignment != DOCKED_ALIGNMENT_NONE &&
590 alignment != DOCKED_ALIGNMENT_NONE && alignment != desired_alignment) {
591 return false;
592 }
593 // Do not allow docking on the same side as shelf.
594 return IsDockedAlignmentValid(desired_alignment);
595 }
596
597 bool DockedWindowLayoutManager::IsDockedAlignmentValid(
598 DockedAlignment alignment) const {
599 ShelfAlignment shelf_alignment =
600 shelf_ ? shelf_->GetAlignment() : SHELF_ALIGNMENT_BOTTOM;
601 if ((alignment == DOCKED_ALIGNMENT_LEFT &&
602 shelf_alignment == SHELF_ALIGNMENT_LEFT) ||
603 (alignment == DOCKED_ALIGNMENT_RIGHT &&
604 shelf_alignment == SHELF_ALIGNMENT_RIGHT)) {
605 return false;
606 }
607 return true;
608 }
609
610 void DockedWindowLayoutManager::MaybeSetDesiredDockedAlignment(
611 DockedAlignment alignment) {
612 // If the requested alignment is |NONE| or there are no
613 // docked windows return early as we can't change whether there is a
614 // dock or not. If the requested alignment is the same as the current
615 // alignment return early as an optimization.
616 if (alignment == DOCKED_ALIGNMENT_NONE ||
617 alignment_ == DOCKED_ALIGNMENT_NONE || alignment_ == alignment ||
618 !IsDockedAlignmentValid(alignment)) {
619 return;
620 }
621 alignment_ = alignment;
622
623 Relayout();
624 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
625 }
626
627 void DockedWindowLayoutManager::OnShelfBoundsChanged() {
628 Relayout();
629 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
630 }
631
632 ////////////////////////////////////////////////////////////////////////////////
633 // DockedWindowLayoutManager, aura::LayoutManager implementation:
634 void DockedWindowLayoutManager::OnWindowResized() {
635 MaybeMinimizeChildrenExcept(dragged_window_);
636 Relayout();
637 // When screen resizes update the insets even when dock width or alignment
638 // does not change.
639 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_RESIZED);
640 }
641
642 void DockedWindowLayoutManager::OnWindowAddedToLayout(WmWindow* child) {
643 if (IsPopupOrTransient(child))
644 return;
645 // Dragged windows are already observed by StartDragging and do not change
646 // docked alignment during the drag.
647 if (child == dragged_window_)
648 return;
649 // If this is the first window getting docked - update alignment.
650 // A window can be added without proper bounds when window is moved to another
651 // display via API or due to display configuration change, so the alignment
652 // is set based on which edge is closer in the new display.
653 if (alignment_ == DOCKED_ALIGNMENT_NONE) {
654 alignment_ = preferred_alignment_ != DOCKED_ALIGNMENT_NONE
655 ? preferred_alignment_
656 : GetEdgeNearestWindow(child);
657 }
658 MaybeMinimizeChildrenExcept(child);
659 child->aura_window()->AddObserver(this);
660 child->GetWindowState()->AddObserver(this);
661 Relayout();
662 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
663
664 // Only keyboard-initiated actions are recorded here. Dragging cases
665 // are handled in FinishDragging.
666 if (event_source_ != DOCKED_ACTION_SOURCE_UNKNOWN)
667 RecordUmaAction(DOCKED_ACTION_DOCK, event_source_);
668 }
669
670 void DockedWindowLayoutManager::OnWindowRemovedFromLayout(WmWindow* child) {
671 if (IsPopupOrTransient(child))
672 return;
673 // Dragged windows are stopped being observed by FinishDragging and do not
674 // change alignment during the drag. They also cannot be set to be the
675 // |last_active_window_|.
676 if (child == dragged_window_)
677 return;
678 // If this is the last window, set alignment and maximize the workspace.
679 if (!IsAnyWindowDocked()) {
680 alignment_ = DOCKED_ALIGNMENT_NONE;
681 UpdateDockedWidth(0);
682 }
683 if (last_active_window_ == child)
684 last_active_window_ = nullptr;
685 child->aura_window()->RemoveObserver(this);
686 child->GetWindowState()->RemoveObserver(this);
687 Relayout();
688 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
689 }
690
691 void DockedWindowLayoutManager::OnChildWindowVisibilityChanged(WmWindow* child,
692 bool visible) {
693 if (IsPopupOrTransient(child))
694 return;
695
696 wm::WindowState* window_state = child->GetWindowState();
697 if (visible && window_state->IsMinimized())
698 window_state->Restore();
699 Relayout();
700 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
701 }
702
703 void DockedWindowLayoutManager::SetChildBounds(
704 WmWindow* child,
705 const gfx::Rect& requested_bounds) {
706 // The minimum constraints have to be applied first by the layout manager.
707 gfx::Rect actual_new_bounds(requested_bounds);
708 if (child->HasNonClientArea()) {
709 const gfx::Size min_size = child->GetMinimumSize();
710 actual_new_bounds.set_width(
711 std::max(min_size.width(), actual_new_bounds.width()));
712 actual_new_bounds.set_height(
713 std::max(min_size.height(), actual_new_bounds.height()));
714 }
715 if (IsWindowDocked(child) && child != dragged_window_)
716 return;
717 wm::WmSnapToPixelLayoutManager::SetChildBounds(child, actual_new_bounds);
718 if (IsPopupOrTransient(child))
719 return;
720 // Whenever one of our windows is moved or resized enforce layout.
721 if (shelf_)
722 shelf_->UpdateVisibilityState();
723 }
724
725 ////////////////////////////////////////////////////////////////////////////////
726 // DockedWindowLayoutManager, display::DisplayObserver implementation:
727
728 void DockedWindowLayoutManager::OnDisplayMetricsChanged(
729 const display::Display& display,
730 uint32_t changed_metrics) {
731 if (dock_container_->GetDisplayNearestWindow().id() != display.id())
732 return;
733
734 Relayout();
735 UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
736 MaybeMinimizeChildrenExcept(dragged_window_);
737 }
738
739 /////////////////////////////////////////////////////////////////////////////
740 // DockedWindowLayoutManager, WindowStateObserver implementation:
741
742 void DockedWindowLayoutManager::OnPreWindowStateTypeChange(
743 wm::WindowState* window_state,
744 wm::WindowStateType old_type) {
745 WmWindow* window = window_state->window();
746 if (IsPopupOrTransient(window))
747 return;
748 // The window property will still be set, but no actual change will occur
749 // until OnFullscreenStateChange is called when exiting fullscreen.
750 if (in_fullscreen_)
751 return;
752 if (!window_state->IsDocked()) {
753 if (window != dragged_window_) {
754 UndockWindow(window);
755 if (window_state->IsMaximizedOrFullscreenOrPinned())
756 RecordUmaAction(DOCKED_ACTION_MAXIMIZE, event_source_);
757 else
758 RecordUmaAction(DOCKED_ACTION_UNDOCK, event_source_);
759 }
760 } else if (window_state->IsMinimized()) {
761 MinimizeDockedWindow(window_state);
762 } else if (old_type == wm::WINDOW_STATE_TYPE_DOCKED_MINIMIZED) {
763 RestoreDockedWindow(window_state);
764 } else if (old_type == wm::WINDOW_STATE_TYPE_MINIMIZED) {
765 NOTREACHED() << "Minimized window in docked layout manager";
766 }
767 }
768
769 /////////////////////////////////////////////////////////////////////////////
770 // DockedWindowLayoutManager, WindowObserver implementation:
771
772 void DockedWindowLayoutManager::OnWindowBoundsChanged(
773 aura::Window* window,
774 const gfx::Rect& old_bounds,
775 const gfx::Rect& new_bounds) {
776 // Only relayout if the dragged window would get docked.
777 if (WmWindow::Get(window) == dragged_window_ && is_dragged_window_docked_)
778 Relayout();
779 }
780
781 void DockedWindowLayoutManager::OnWindowVisibilityChanging(aura::Window* window,
782 bool visible) {
783 if (IsPopupOrTransient(WmWindow::Get(window)))
784 return;
785 int animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT;
786 if (visible) {
787 animation_type = ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DROP;
788 ::wm::SetWindowVisibilityAnimationDuration(
789 window, base::TimeDelta::FromMilliseconds(kFadeDurationMs));
790 } else if (wm::GetWindowState(window)->IsMinimized()) {
791 animation_type = wm::WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE;
792 }
793 ::wm::SetWindowVisibilityAnimationType(window, animation_type);
794 }
795
796 void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
797 if (dragged_window_ == WmWindow::Get(window)) {
798 FinishDragging(DOCKED_ACTION_NONE, DOCKED_ACTION_SOURCE_UNKNOWN);
799 DCHECK(!dragged_window_);
800 DCHECK(!is_dragged_window_docked_);
801 }
802 if (WmWindow::Get(window) == last_active_window_)
803 last_active_window_ = nullptr;
804 RecordUmaAction(DOCKED_ACTION_CLOSE, event_source_);
805 }
806
807 ////////////////////////////////////////////////////////////////////////////////
808 // DockedWindowLayoutManager, ActivationChangeObserver implementation:
809
810 void DockedWindowLayoutManager::OnWindowActivated(ActivationReason reason,
811 aura::Window* gained_active,
812 aura::Window* lost_active) {
813 WmWindow* wm_gained_active = WmWindow::Get(gained_active);
814 if (wm_gained_active && IsPopupOrTransient(wm_gained_active))
815 return;
816 // Ignore if the window that is not managed by this was activated.
817 WmWindow* ancestor = nullptr;
818 for (WmWindow* parent = wm_gained_active; parent;
819 parent = parent->GetParent()) {
820 if (parent->GetParent() == dock_container_) {
821 ancestor = parent;
822 break;
823 }
824 }
825 if (ancestor) {
826 // Window activation from overview mode may unminimize a window and require
827 // layout update.
828 MaybeMinimizeChildrenExcept(wm_gained_active);
829 Relayout();
830 UpdateStacking(ancestor);
831 }
832 }
833
834 ////////////////////////////////////////////////////////////////////////////////
835 // DockedWindowLayoutManager, ShellObserver implementation:
836
837 void DockedWindowLayoutManager::OnShelfAlignmentChanged(WmWindow* root_window) {
838 if (!shelf_ || alignment_ == DOCKED_ALIGNMENT_NONE ||
839 root_window != shelf_->GetWindow()->GetRootWindow()) {
840 return;
841 }
842
843 // Do not allow shelf and dock on the same side. Switch side that
844 // the dock is attached to and move all dock windows to that new side.
845 ShelfAlignment shelf_alignment = shelf_->GetAlignment();
846 if (alignment_ == DOCKED_ALIGNMENT_LEFT &&
847 shelf_alignment == SHELF_ALIGNMENT_LEFT) {
848 alignment_ = DOCKED_ALIGNMENT_RIGHT;
849 } else if (alignment_ == DOCKED_ALIGNMENT_RIGHT &&
850 shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
851 alignment_ = DOCKED_ALIGNMENT_LEFT;
852 }
853 Relayout();
854 UpdateDockBounds(DockedWindowLayoutManagerObserver::SHELF_ALIGNMENT_CHANGED);
855 }
856
857 void DockedWindowLayoutManager::OnFullscreenStateChanged(
858 bool is_fullscreen,
859 WmWindow* root_window) {
860 if (root_window != dock_container_->GetRootWindow())
861 return;
862
863 // Entering fullscreen mode (including immersive) hides docked windows.
864 in_fullscreen_ = root_window_controller_->GetWorkspaceWindowState() ==
865 wm::WORKSPACE_WINDOW_STATE_FULL_SCREEN;
866 {
867 // prevent Relayout from getting called multiple times during this
868 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
869 // Use a copy of children array because a call to MinimizeDockedWindow or
870 // RestoreDockedWindow can change order.
871 for (WmWindow* window : dock_container_->GetChildren()) {
872 if (IsPopupOrTransient(window))
873 continue;
874 wm::WindowState* window_state = window->GetWindowState();
875 if (in_fullscreen_) {
876 if (window->IsVisible())
877 MinimizeDockedWindow(window_state);
878 } else {
879 if (!window_state->IsMinimized())
880 RestoreDockedWindow(window_state);
881 }
882 }
883 }
884 Relayout();
885 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
886 }
887
888 void DockedWindowLayoutManager::OnOverviewModeStarting() {
889 in_overview_ = true;
890 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
891 }
892
893 void DockedWindowLayoutManager::OnOverviewModeEnded() {
894 in_overview_ = false;
895 UpdateDockBounds(DockedWindowLayoutManagerObserver::CHILD_CHANGED);
896 }
897
898 ////////////////////////////////////////////////////////////////////////////////
899 // DockedWindowLayoutManager private implementation:
900
901 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(WmWindow* child) {
902 WindowSelectorController* window_selector_controller =
903 WmShell::Get()->window_selector_controller();
904 if (window_selector_controller->IsRestoringMinimizedWindows())
905 return;
906 // Minimize any windows that don't fit without overlap.
907 const gfx::Rect work_area =
908 dock_container_->GetDisplayNearestWindow().work_area();
909 int available_room = work_area.height();
910 bool gap_needed = !!child;
911 if (child)
912 available_room -= GetWindowHeightCloseTo(child, 0);
913 // Use a copy of children array because a call to Minimize can change order.
914 std::vector<WmWindow*> children(dock_container_->GetChildren());
915 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
916 WmWindow* window(*iter);
917 if (window == child || !IsWindowDocked(window))
918 continue;
919 int room_needed =
920 GetWindowHeightCloseTo(window, 0) + (gap_needed ? kMinDockGap : 0);
921 gap_needed = true;
922 if (available_room > room_needed) {
923 available_room -= room_needed;
924 } else {
925 // Slow down minimizing animations. Lock duration so that it is not
926 // overridden by other ScopedLayerAnimationSettings down the stack.
927 ui::ScopedLayerAnimationSettings settings(
928 window->GetLayer()->GetAnimator());
929 settings.SetTransitionDuration(
930 base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
931 settings.LockTransitionDuration();
932 window->GetWindowState()->Minimize();
933 }
934 }
935 }
936
937 void DockedWindowLayoutManager::MinimizeDockedWindow(
938 wm::WindowState* window_state) {
939 DCHECK(!IsPopupOrTransient(window_state->window()));
940 window_state->window()->Hide();
941 if (window_state->IsActive())
942 window_state->Deactivate();
943 RecordUmaAction(DOCKED_ACTION_MINIMIZE, event_source_);
944 }
945
946 void DockedWindowLayoutManager::RestoreDockedWindow(
947 wm::WindowState* window_state) {
948 WmWindow* window = window_state->window();
949 DCHECK(!IsPopupOrTransient(window));
950
951 // Evict the window if it can no longer be docked because of its height.
952 if (!CanDockWindow(window, DOCKED_ALIGNMENT_NONE)) {
953 window_state->Restore();
954 RecordUmaAction(DOCKED_ACTION_EVICT, event_source_);
955 return;
956 }
957
958 // Always place restored window at the bottom shuffling the other windows up.
959 // TODO(varkha): add a separate container for docked windows to keep track
960 // of ordering.
961 const gfx::Rect work_area =
962 dock_container_->GetDisplayNearestWindow().work_area();
963 gfx::Rect bounds(window->GetBounds());
964 bounds.set_y(work_area.bottom());
965 window->SetBounds(bounds);
966 window->Show();
967 MaybeMinimizeChildrenExcept(window);
968 RecordUmaAction(DOCKED_ACTION_RESTORE, event_source_);
969 }
970
971 void DockedWindowLayoutManager::RecordUmaAction(DockedAction action,
972 DockedActionSource source) {
973 if (action == DOCKED_ACTION_NONE)
974 return;
975 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.Action", action, DOCKED_ACTION_COUNT);
976 UMA_HISTOGRAM_ENUMERATION("Ash.Dock.ActionSource", source,
977 DOCKED_ACTION_SOURCE_COUNT);
978 base::Time time_now = base::Time::Now();
979 base::TimeDelta time_between_use = time_now - last_action_time_;
980 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.Dock.TimeBetweenUse",
981 time_between_use.InSeconds(), 1,
982 base::TimeDelta::FromHours(10).InSeconds(), 100);
983 last_action_time_ = time_now;
984 int docked_all_count = 0;
985 int docked_visible_count = 0;
986 int docked_panels_count = 0;
987 int large_windows_count = 0;
988 for (WmWindow* window : dock_container_->GetChildren()) {
989 if (IsPopupOrTransient(window))
990 continue;
991 docked_all_count++;
992 if (!IsWindowDocked(window))
993 continue;
994 docked_visible_count++;
995 if (window->GetType() == ui::wm::WINDOW_TYPE_PANEL)
996 docked_panels_count++;
997 const wm::WindowState* window_state = window->GetWindowState();
998 if (window_state->HasRestoreBounds()) {
999 const gfx::Rect restore_bounds = window_state->GetRestoreBoundsInScreen();
1000 if (restore_bounds.width() > kMaxDockWidth)
1001 large_windows_count++;
1002 }
1003 }
1004 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsAll", docked_all_count);
1005 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsLarge", large_windows_count);
1006 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsPanels", docked_panels_count);
1007 UMA_HISTOGRAM_COUNTS_100("Ash.Dock.ItemsVisible", docked_visible_count);
1008 }
1009
1010 void DockedWindowLayoutManager::UpdateDockedWidth(int width) {
1011 if (docked_width_ == width)
1012 return;
1013 docked_width_ = width;
1014 UMA_HISTOGRAM_COUNTS_10000("Ash.Dock.Width", docked_width_);
1015 }
1016
1017 void DockedWindowLayoutManager::OnDraggedWindowDocked(WmWindow* window) {
1018 DCHECK(!is_dragged_window_docked_);
1019 is_dragged_window_docked_ = true;
1020 }
1021
1022 void DockedWindowLayoutManager::OnDraggedWindowUndocked() {
1023 DCHECK(is_dragged_window_docked_);
1024 is_dragged_window_docked_ = false;
1025 }
1026
1027 bool DockedWindowLayoutManager::IsAnyWindowDocked() {
1028 return CalculateAlignment() != DOCKED_ALIGNMENT_NONE;
1029 }
1030
1031 DockedAlignment DockedWindowLayoutManager::GetEdgeNearestWindow(
1032 const WmWindow* window) const {
1033 const gfx::Rect bounds(window->GetBoundsInScreen());
1034 const gfx::Rect container_bounds = dock_container_->GetBoundsInScreen();
1035 // Give one pixel preference for docking on the right side to a window that
1036 // has odd width and is centered in a screen that has even width (or vice
1037 // versa). This only matters to the tests but could be a source of flakiness.
1038 return (abs(bounds.x() - container_bounds.x()) + 1 <
1039 abs(bounds.right() - container_bounds.right()))
1040 ? DOCKED_ALIGNMENT_LEFT
1041 : DOCKED_ALIGNMENT_RIGHT;
1042 }
1043
1044 void DockedWindowLayoutManager::Relayout() {
1045 // Suppress layouts during overview mode while restoring minimized windows so
1046 // that docked animations are not interfering with the overview mode.
1047 WindowSelectorController* window_selector_controller =
1048 WmShell::Get()->window_selector_controller();
1049 if (in_layout_ || (window_selector_controller->IsRestoringMinimizedWindows()))
1050 return;
1051 if (alignment_ == DOCKED_ALIGNMENT_NONE && !is_dragged_window_docked_)
1052 return;
1053 base::AutoReset<bool> auto_reset_in_layout(&in_layout_, true);
1054
1055 WmWindow* active_window = nullptr;
1056 std::vector<WindowWithHeight> visible_windows;
1057 for (WmWindow* window : dock_container_->GetChildren()) {
1058 if (!IsWindowDocked(window) || window == dragged_window_)
1059 continue;
1060
1061 // If the shelf is currently hidden (full-screen mode), hide window until
1062 // full-screen mode is exited.
1063 if (in_fullscreen_) {
1064 // The call to Hide does not set the minimize property, so the window will
1065 // be restored when the shelf becomes visible again.
1066 window->Hide();
1067 continue;
1068 }
1069 if (window->IsFocused() ||
1070 window->Contains(window->GetShell()->GetFocusedWindow())) {
1071 DCHECK(!active_window);
1072 active_window = window;
1073 }
1074 visible_windows.push_back(WindowWithHeight(window));
1075 }
1076 // Consider docked dragged_window_ when fanning out other child windows.
1077 if (is_dragged_window_docked_) {
1078 visible_windows.push_back(WindowWithHeight(dragged_window_));
1079 DCHECK(!active_window);
1080 active_window = dragged_window_;
1081 }
1082
1083 // Position docked windows as well as the window being dragged.
1084 gfx::Rect work_area = dock_container_->GetDisplayNearestWindow().work_area();
1085 if (shelf_observer_)
1086 work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
1087 int available_room =
1088 CalculateWindowHeightsAndRemainingRoom(work_area, &visible_windows);
1089 FanOutChildren(work_area, CalculateIdealWidth(visible_windows),
1090 available_room, &visible_windows);
1091
1092 // After the first Relayout allow the windows to change their order easier
1093 // since we know they are docked.
1094 is_dragged_from_dock_ = true;
1095 UpdateStacking(active_window);
1096 }
1097
1098 int DockedWindowLayoutManager::CalculateWindowHeightsAndRemainingRoom(
1099 const gfx::Rect& work_area,
1100 std::vector<WindowWithHeight>* visible_windows) {
1101 int available_room = work_area.height();
1102 int remaining_windows = visible_windows->size();
1103 int gap_height = remaining_windows > 1 ? kMinDockGap : 0;
1104
1105 // Sort windows by their minimum heights and calculate target heights.
1106 std::sort(visible_windows->begin(), visible_windows->end(),
1107 CompareMinimumHeight());
1108 // Distribute the free space among the docked windows. Since the windows are
1109 // sorted (tall windows first) we can now assume that any window which
1110 // required more space than the current window will have already been
1111 // accounted for previously in this loop, so we can safely give that window
1112 // its proportional share of the remaining space.
1113 for (std::vector<WindowWithHeight>::reverse_iterator iter =
1114 visible_windows->rbegin();
1115 iter != visible_windows->rend(); ++iter) {
1116 iter->height = GetWindowHeightCloseTo(
1117 iter->window,
1118 (available_room + gap_height) / remaining_windows - gap_height);
1119 available_room -= (iter->height + gap_height);
1120 remaining_windows--;
1121 }
1122 return available_room + gap_height;
1123 }
1124
1125 int DockedWindowLayoutManager::CalculateIdealWidth(
1126 const std::vector<WindowWithHeight>& visible_windows) {
1127 int smallest_max_width = kMaxDockWidth;
1128 int largest_min_width = kMinDockWidth;
1129 // Ideal width of the docked area is as close to kIdealWidth as possible
1130 // while still respecting the minimum and maximum width restrictions on the
1131 // individual docked windows as well as the width that was possibly set by a
1132 // user (which needs to be preserved when dragging and rearranging windows).
1133 for (std::vector<WindowWithHeight>::const_iterator iter =
1134 visible_windows.begin();
1135 iter != visible_windows.end(); ++iter) {
1136 const WmWindow* window = iter->window;
1137 int min_window_width = window->GetBounds().width();
1138 int max_window_width = min_window_width;
1139 if (!window->GetWindowState()->bounds_changed_by_user()) {
1140 min_window_width = GetWindowWidthCloseTo(window, kMinDockWidth);
1141 max_window_width = GetWindowWidthCloseTo(window, kMaxDockWidth);
1142 }
1143 largest_min_width = std::max(largest_min_width, min_window_width);
1144 smallest_max_width = std::min(smallest_max_width, max_window_width);
1145 }
1146 int ideal_width =
1147 std::max(largest_min_width, std::min(smallest_max_width, kIdealWidth));
1148 // Restrict docked area width regardless of window restrictions.
1149 ideal_width = std::max(std::min(ideal_width, kMaxDockWidth), kMinDockWidth);
1150 return ideal_width;
1151 }
1152
1153 void DockedWindowLayoutManager::FanOutChildren(
1154 const gfx::Rect& work_area,
1155 int ideal_docked_width,
1156 int available_room,
1157 std::vector<WindowWithHeight>* visible_windows) {
1158 gfx::Rect dock_bounds = dock_container_->GetBoundsInScreen();
1159
1160 // Calculate initial vertical offset and the gap or overlap between windows.
1161 const int num_windows = visible_windows->size();
1162 const float delta =
1163 static_cast<float>(available_room) /
1164 ((available_room > 0 || num_windows <= 1) ? num_windows + 1
1165 : num_windows - 1);
1166 float y_pos = work_area.y() + ((delta > 0) ? delta : 0);
1167
1168 // Docked area is shown only if there is at least one non-dragged visible
1169 // docked window.
1170 int new_width = ideal_docked_width;
1171 if (visible_windows->empty() ||
1172 (visible_windows->size() == 1 &&
1173 (*visible_windows)[0].window == dragged_window_)) {
1174 new_width = 0;
1175 }
1176 UpdateDockedWidth(new_width);
1177 // Sort windows by their center positions and fan out overlapping
1178 // windows.
1179 std::sort(visible_windows->begin(), visible_windows->end(),
1180 CompareWindowPos(is_dragged_from_dock_ ? dragged_window_ : nullptr,
1181 dock_container_, delta));
1182 for (std::vector<WindowWithHeight>::iterator iter = visible_windows->begin();
1183 iter != visible_windows->end(); ++iter) {
1184 WmWindow* window = iter->window;
1185 gfx::Rect bounds =
1186 dock_container_->ConvertRectToScreen(window->GetTargetBounds());
1187 // A window is extended or shrunk to be as close as possible to the ideal
1188 // docked area width. Windows that were resized by a user are kept at their
1189 // existing size.
1190 // This also enforces the min / max restrictions on the docked area width.
1191 bounds.set_width(GetWindowWidthCloseTo(
1192 window, window->GetWindowState()->bounds_changed_by_user()
1193 ? bounds.width()
1194 : ideal_docked_width));
1195 DCHECK_LE(bounds.width(), ideal_docked_width);
1196
1197 DockedAlignment alignment = alignment_;
1198 if (alignment == DOCKED_ALIGNMENT_NONE && window == dragged_window_)
1199 alignment = GetEdgeNearestWindow(window);
1200
1201 // Fan out windows evenly distributing the overlap or remaining free space.
1202 bounds.set_height(iter->height);
1203 bounds.set_y(
1204 std::max(work_area.y(), std::min(work_area.bottom() - bounds.height(),
1205 static_cast<int>(y_pos + 0.5))));
1206 y_pos += bounds.height() + delta + kMinDockGap;
1207
1208 // All docked windows other than the one currently dragged remain stuck
1209 // to the screen edge (flush with the edge or centered in the dock area).
1210 switch (alignment) {
1211 case DOCKED_ALIGNMENT_LEFT:
1212 bounds.set_x(dock_bounds.x() +
1213 (ideal_docked_width - bounds.width()) / 2);
1214 break;
1215 case DOCKED_ALIGNMENT_RIGHT:
1216 bounds.set_x(dock_bounds.right() -
1217 (ideal_docked_width + bounds.width()) / 2);
1218 break;
1219 case DOCKED_ALIGNMENT_NONE:
1220 break;
1221 }
1222 if (window == dragged_window_) {
1223 dragged_bounds_ = bounds;
1224 continue;
1225 }
1226 // If the following asserts it is probably because not all the children
1227 // have been removed when dock was closed.
1228 DCHECK_NE(alignment_, DOCKED_ALIGNMENT_NONE);
1229 bounds = dock_container_->ConvertRectFromScreen(bounds);
1230 if (bounds != window->GetTargetBounds()) {
1231 ui::Layer* layer = window->GetLayer();
1232 ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
1233 slide_settings.SetPreemptionStrategy(
1234 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
1235 slide_settings.SetTransitionDuration(
1236 base::TimeDelta::FromMilliseconds(kSlideDurationMs));
1237 window->SetBoundsDirect(bounds);
1238 }
1239 }
1240 }
1241
1242 void DockedWindowLayoutManager::UpdateDockBounds(
1243 DockedWindowLayoutManagerObserver::Reason reason) {
1244 int docked_width = in_overview_ ? 0 : docked_width_;
1245 int dock_inset = docked_width + (docked_width > 0 ? kMinDockGap : 0);
1246 const gfx::Rect work_area =
1247 dock_container_->GetDisplayNearestWindow().work_area();
1248 gfx::Rect bounds = gfx::Rect(
1249 alignment_ == DOCKED_ALIGNMENT_RIGHT && dock_inset > 0
1250 ? dock_container_->GetBounds().right() - dock_inset
1251 : dock_container_->GetBounds().x(),
1252 dock_container_->GetBounds().y(), dock_inset, work_area.height());
1253 docked_bounds_ =
1254 bounds + dock_container_->GetBoundsInScreen().OffsetFromOrigin();
1255 for (auto& observer : observer_list_)
1256 observer.OnDockBoundsChanging(bounds, reason);
1257 // Show or hide background for docked area.
1258 gfx::Rect background_bounds(docked_bounds_);
1259 if (shelf_observer_)
1260 background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
1261 if (docked_width > 0) {
1262 // TODO: |shelf_| should not be null by the time we get here, but it may
1263 // be in mash as startup sequence doesn't yet match that of ash. Once
1264 // |shelf_| is created at same time as ash we can remove conditional.
1265 // http://crbug.com/632099
1266 if (shelf_) {
1267 if (!background_widget_)
1268 background_widget_.reset(new DockedBackgroundWidget(this));
1269 background_widget_->SetBackgroundBounds(background_bounds, alignment_);
1270 background_widget_->Show();
1271 }
1272 } else if (background_widget_) {
1273 background_widget_->Hide();
1274 }
1275 }
1276
1277 void DockedWindowLayoutManager::UpdateStacking(WmWindow* active_window) {
1278 if (!active_window) {
1279 if (!last_active_window_)
1280 return;
1281 active_window = last_active_window_;
1282 }
1283
1284 // Windows are stacked like a deck of cards:
1285 // ,------.
1286 // |,------.|
1287 // |,------.|
1288 // | active |
1289 // | window |
1290 // |`------'|
1291 // |`------'|
1292 // `------'
1293 // Use the middle of each window to figure out how to stack the window.
1294 // This allows us to update the stacking when a window is being dragged around
1295 // by the titlebar.
1296 std::map<int, WmWindow*> window_ordering;
1297 for (WmWindow* child : dock_container_->GetChildren()) {
1298 if (!IsWindowDocked(child) ||
1299 (child == dragged_window_ && !is_dragged_window_docked_)) {
1300 continue;
1301 }
1302 gfx::Rect bounds = child->GetBounds();
1303 window_ordering.insert(
1304 std::make_pair(bounds.y() + bounds.height() / 2, child));
1305 }
1306 int active_center_y = active_window->GetBounds().CenterPoint().y();
1307
1308 WmWindow* previous_window = nullptr;
1309 for (std::map<int, WmWindow*>::const_iterator it = window_ordering.begin();
1310 it != window_ordering.end() && it->first < active_center_y; ++it) {
1311 if (previous_window)
1312 dock_container_->StackChildAbove(it->second, previous_window);
1313 previous_window = it->second;
1314 }
1315 for (std::map<int, WmWindow*>::const_reverse_iterator it =
1316 window_ordering.rbegin();
1317 it != window_ordering.rend() && it->first > active_center_y; ++it) {
1318 if (previous_window)
1319 dock_container_->StackChildAbove(it->second, previous_window);
1320 previous_window = it->second;
1321 }
1322
1323 if (previous_window && active_window->GetParent() == dock_container_)
1324 dock_container_->StackChildAbove(active_window, previous_window);
1325 if (active_window != dragged_window_)
1326 last_active_window_ = active_window;
1327 }
1328
1329 ////////////////////////////////////////////////////////////////////////////////
1330 // keyboard::KeyboardControllerObserver implementation:
1331
1332 void DockedWindowLayoutManager::OnKeyboardBoundsChanging(
1333 const gfx::Rect& keyboard_bounds) {
1334 // This bounds change will have caused a change to the Shelf which does not
1335 // propagate automatically to this class, so manually recalculate bounds.
1336 Relayout();
1337 UpdateDockBounds(DockedWindowLayoutManagerObserver::KEYBOARD_BOUNDS_CHANGING);
1338 }
1339
1340 void DockedWindowLayoutManager::OnKeyboardClosed() {}
1341
1342 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698