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

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

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

Powered by Google App Engine
This is Rietveld 408576698