1 // Copyright 2019 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/home_screen/drag_window_from_shelf_controller.h"
6
7 #include "ash/display/screen_orientation_controller.h"
8 #include "ash/home_screen/home_screen_controller.h"
9 #include "ash/home_screen/home_screen_delegate.h"
10 #include "ash/home_screen/window_scale_animation.h"
11 #include "ash/public/cpp/presentation_time_recorder.h"
12 #include "ash/public/cpp/shelf_config.h"
13 #include "ash/public/cpp/window_backdrop.h"
14 #include "ash/public/cpp/window_properties.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/scoped_animation_disabler.h"
17 #include "ash/screen_util.h"
18 #include "ash/shelf/hotseat_widget.h"
19 #include "ash/shelf/shelf.h"
20 #include "ash/shell.h"
21 #include "ash/wallpaper/wallpaper_constants.h"
22 #include "ash/wallpaper/wallpaper_view.h"
23 #include "ash/wallpaper/wallpaper_widget_controller.h"
24 #include "ash/wm/mru_window_tracker.h"
25 #include "ash/wm/overview/overview_constants.h"
26 #include "ash/wm/overview/overview_controller.h"
27 #include "ash/wm/overview/overview_session.h"
28 #include "ash/wm/overview/overview_utils.h"
29 #include "ash/wm/splitview/split_view_controller.h"
30 #include "ash/wm/splitview/split_view_drag_indicators.h"
31 #include "ash/wm/splitview/split_view_utils.h"
32 #include "ash/wm/window_properties.h"
33 #include "ash/wm/window_state.h"
34 #include "ash/wm/window_transient_descendant_iterator.h"
35 #include "ash/wm/window_util.h"
36 #include "base/callback_helpers.h"
37 #include "base/metrics/histogram_macros.h"
38 #include "base/numerics/ranges.h"
39 #include "ui/aura/window_tree_host.h"
40 #include "ui/base/hit_test.h"
41 #include "ui/compositor/layer_animation_observer.h"
42 #include "ui/compositor/scoped_layer_animation_settings.h"
43 #include "ui/display/screen.h"
44 #include "ui/gfx/geometry/point_f.h"
45 #include "ui/gfx/transform_util.h"
46 #include "ui/wm/core/coordinate_conversion.h"
47 #include "ui/wm/core/window_util.h"
48
49 namespace ash {
50
51 namespace {
52
53 // The minimum window scale factor when dragging a window from shelf.
54 constexpr float kMinimumWindowScaleDuringDragging = 0.3f;
55
56 // The ratio in display height at which point the dragged window shrinks to its
57 // minimum scale kMinimumWindowScaleDuringDragging.
58 constexpr float kMinYDisplayHeightRatio = 0.125f;
59
60 // Amount of time to wait to show overview after the user slows down or stops
61 // window dragging.
62 constexpr base::TimeDelta kShowOverviewTimeWhenDragSuspend =
63 base::TimeDelta::FromMilliseconds(40);
64
65 // The scroll update threshold to restart the show overview timer.
66 constexpr float kScrollUpdateOverviewThreshold = 2.f;
67
68 // Presentation time histogram names.
69 constexpr char kDragWindowFromShelfHistogram[] =
70 "Ash.DragWindowFromShelf.PresentationTime";
71 constexpr char kDragWindowFromShelfMaxLatencyHistogram[] =
72 "Ash.DragWindowFromShelf.PresentationTime.MaxLatency";
73
74 } // namespace
75
76 // Hide all visible windows expect the dragged windows or the window showing in
77 // splitview during dragging.
78 class DragWindowFromShelfController::WindowsHider
79 : public aura::WindowObserver {
80 public:
WindowsHider(aura::Window * dragged_window)81 explicit WindowsHider(aura::Window* dragged_window)
82 : dragged_window_(dragged_window) {
83 std::vector<aura::Window*> windows =
84 Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
85 for (auto* window : windows) {
86 if (window == dragged_window_)
87 continue;
88 if (::wm::HasTransientAncestor(window, dragged_window_))
89 continue;
90 if (!window->IsVisible())
91 continue;
92 if (SplitViewController::Get(window)->IsWindowInSplitView(window))
93 continue;
94
95 hidden_windows_.push_back(window);
96 window->AddObserver(this);
97 window->SetProperty(kHideDuringWindowDragging, true);
98 }
99 window_util::MinimizeAndHideWithoutAnimation(hidden_windows_);
100 }
101
~WindowsHider()102 ~WindowsHider() override {
103 for (auto* window : hidden_windows_) {
104 window->RemoveObserver(this);
105 window->ClearProperty(kHideDuringWindowDragging);
106 }
107 hidden_windows_.clear();
108 }
109
RestoreWindowsVisibility()110 void RestoreWindowsVisibility() {
111 for (auto* window : hidden_windows_) {
112 window->RemoveObserver(this);
113 ScopedAnimationDisabler disabler(window);
114 window->Show();
115 window->ClearProperty(kHideDuringWindowDragging);
116 }
117 hidden_windows_.clear();
118 }
119
120 // Even though we explicitly minimize the windows, some (i.e. ARC apps)
121 // minimize asynchronously so they may not be truly minimized after |this| is
122 // constructed.
WindowsMinimized()123 bool WindowsMinimized() {
124 return std::all_of(hidden_windows_.begin(), hidden_windows_.end(),
125 [](const aura::Window* w) {
126 return WindowState::Get(w)->IsMinimized();
127 });
128 }
129
130 // aura::WindowObserver:
OnWindowDestroying(aura::Window * window)131 void OnWindowDestroying(aura::Window* window) override {
132 window->RemoveObserver(this);
133 hidden_windows_.erase(
134 std::find(hidden_windows_.begin(), hidden_windows_.end(), window));
135 }
136
137 private:
138 aura::Window* dragged_window_;
139 std::vector<aura::Window*> hidden_windows_;
140
141 DISALLOW_COPY_AND_ASSIGN(WindowsHider);
142 };
143
144 // static
GetReturnToMaximizedThreshold()145 float DragWindowFromShelfController::GetReturnToMaximizedThreshold() {
146 return Shell::GetPrimaryRootWindowController()
147 ->shelf()
148 ->hotseat_widget()
149 ->GetHotseatFullDragAmount();
150 }
151
DragWindowFromShelfController(aura::Window * window,const gfx::PointF & location_in_screen)152 DragWindowFromShelfController::DragWindowFromShelfController(
153 aura::Window* window,
154 const gfx::PointF& location_in_screen)
155 : window_(window) {
156 window_->AddObserver(this);
157 OnDragStarted(location_in_screen);
158
159 presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
160 window_->GetHost()->compositor(), kDragWindowFromShelfHistogram,
161 kDragWindowFromShelfMaxLatencyHistogram);
162 }
163
~DragWindowFromShelfController()164 DragWindowFromShelfController::~DragWindowFromShelfController() {
165 CancelDrag();
166 if (window_)
167 window_->RemoveObserver(this);
168 }
169
Drag(const gfx::PointF & location_in_screen,float scroll_x,float scroll_y)170 void DragWindowFromShelfController::Drag(const gfx::PointF& location_in_screen,
171 float scroll_x,
172 float scroll_y) {
173 // |window_| might have been destroyed during dragging.
174 if (!window_)
175 return;
176
177 if (!drag_started_)
178 return;
179
180 presentation_time_recorder_->RequestNext();
181 UpdateDraggedWindow(location_in_screen);
182
183 // Open overview if the window has been dragged far enough and the scroll
184 // delta has decreased to kOpenOverviewThreshold. Wait until all windows are
185 // minimized or they will not show up in overview.
186 DCHECK(windows_hider_);
187 OverviewController* overview_controller = Shell::Get()->overview_controller();
188 if (std::abs(scroll_y) <= kOpenOverviewThreshold &&
189 !overview_controller->InOverviewSession() &&
190 windows_hider_->WindowsMinimized()) {
191 overview_controller->StartOverview(OverviewEnterExitType::kImmediateEnter);
192 OnWindowDragStartedInOverview();
193 }
194
195 // If overview is active, update its splitview indicator during dragging if
196 // splitview is allowed in current configuration.
197 if (overview_controller->InOverviewSession()) {
198 const SplitViewController::SnapPosition snap_position =
199 GetSnapPosition(location_in_screen);
200 const SplitViewDragIndicators::WindowDraggingState window_dragging_state =
201 SplitViewDragIndicators::ComputeWindowDraggingState(
202 /*is_dragging=*/true,
203 SplitViewDragIndicators::WindowDraggingState::kFromShelf,
204 snap_position);
205 OverviewSession* overview_session = overview_controller->overview_session();
206 overview_session->UpdateSplitViewDragIndicatorsWindowDraggingStates(
207 Shell::GetPrimaryRootWindow(), window_dragging_state);
208 overview_session->OnWindowDragContinued(window_, location_in_screen,
209 window_dragging_state);
210
211 if (snap_position != SplitViewController::NONE) {
212 // If the dragged window is in snap preview area, make sure overview is
213 // visible.
214 ShowOverviewDuringOrAfterDrag();
215 } else if (std::abs(scroll_x) > kShowOverviewThreshold ||
216 std::abs(scroll_y) > kShowOverviewThreshold) {
217 // If the dragging velocity is large enough, hide overview windows.
218 show_overview_timer_.Stop();
219 HideOverviewDuringDrag();
220 } else if (!show_overview_timer_.IsRunning() ||
221 std::abs(scroll_x) > kScrollUpdateOverviewThreshold ||
222 std::abs(scroll_y) > kScrollUpdateOverviewThreshold) {
223 // Otherwise start the |show_overview_timer_| to show and update
224 // overview when the dragging slows down or stops. Note if the window is
225 // still being dragged with scroll rate more than
226 // kScrollUpdateOverviewThreshold, we restart the show overview timer.
227 show_overview_timer_.Start(
228 FROM_HERE, kShowOverviewTimeWhenDragSuspend, this,
229 &DragWindowFromShelfController::ShowOverviewDuringOrAfterDrag);
230 }
231 }
232
233 previous_location_in_screen_ = location_in_screen;
234 }
235
EndDrag(const gfx::PointF & location_in_screen,base::Optional<float> velocity_y)236 base::Optional<ShelfWindowDragResult> DragWindowFromShelfController::EndDrag(
237 const gfx::PointF& location_in_screen,
238 base::Optional<float> velocity_y) {
239 if (!drag_started_)
240 return base::nullopt;
241
242 UpdateDraggedWindow(location_in_screen);
243
244 drag_started_ = false;
245 previous_location_in_screen_ = location_in_screen;
246 presentation_time_recorder_.reset();
247 OverviewController* overview_controller = Shell::Get()->overview_controller();
248 SplitViewController* split_view_controller =
249 SplitViewController::Get(Shell::GetPrimaryRootWindow());
250 const bool in_overview = overview_controller->InOverviewSession();
251 const bool in_splitview = split_view_controller->InSplitViewMode();
252 const bool drop_window_in_overview =
253 ShouldDropWindowInOverview(location_in_screen, velocity_y);
254 end_snap_position_ = GetSnapPositionOnDragEnd(location_in_screen, velocity_y);
255
256 window_drag_result_ = base::nullopt;
257 if (ShouldGoToHomeScreen(location_in_screen, velocity_y)) {
258 DCHECK(!in_splitview);
259 if (in_overview)
260 overview_controller->EndOverview(OverviewEnterExitType::kFadeOutExit);
261 window_drag_result_ = ShelfWindowDragResult::kGoToHomeScreen;
262 } else if (ShouldRestoreToOriginalBounds(location_in_screen)) {
263 window_drag_result_ = ShelfWindowDragResult::kRestoreToOriginalBounds;
264 } else if (!in_overview) {
265 // if overview is not active during the entire drag process, scale down the
266 // dragged window to go to home screen.
267 window_drag_result_ = ShelfWindowDragResult::kGoToHomeScreen;
268 } else {
269 if (drop_window_in_overview)
270 window_drag_result_ = ShelfWindowDragResult::kGoToOverviewMode;
271 else if (end_snap_position_ != SplitViewController::NONE)
272 window_drag_result_ = ShelfWindowDragResult::kGoToSplitviewMode;
273 // For window that may drop in overview or snap in split screen, restore its
274 // original backdrop mode.
275 WindowBackdrop::Get(window_)->RestoreBackdrop();
276 }
277 WindowState::Get(window_)->DeleteDragDetails();
278
279 if (window_drag_result_.has_value()) {
280 UMA_HISTOGRAM_ENUMERATION(kHandleDragWindowFromShelfHistogramName,
281 *window_drag_result_);
282 }
283 return window_drag_result_;
284 }
285
CancelDrag()286 void DragWindowFromShelfController::CancelDrag() {
287 if (!drag_started_)
288 return;
289
290 UMA_HISTOGRAM_ENUMERATION(kHandleDragWindowFromShelfHistogramName,
291 ShelfWindowDragResult::kDragCanceled);
292
293 drag_started_ = false;
294 presentation_time_recorder_.reset();
295 // Reset the window's transform to identity transform.
296 window_->SetTransform(gfx::Transform());
297 WindowBackdrop::Get(window_)->RestoreBackdrop();
298
299 // End overview if it was opened during dragging.
300 OverviewController* overview_controller = Shell::Get()->overview_controller();
301 if (overview_controller->InOverviewSession())
302 overview_controller->EndOverview(OverviewEnterExitType::kImmediateExit);
303 ReshowHiddenWindowsOnDragEnd();
304
305 window_drag_result_ = ShelfWindowDragResult::kDragCanceled;
306 // When the drag is cancelled, the window should restore to its original snap
307 // position.
308 OnDragEnded(previous_location_in_screen_,
309 /*should_drop_window_in_overview=*/false,
310 /*snap_position=*/initial_snap_position_);
311 WindowState::Get(window_)->DeleteDragDetails();
312 }
313
IsDraggedWindowAnimating() const314 bool DragWindowFromShelfController::IsDraggedWindowAnimating() const {
315 return window_ && window_->layer()->GetAnimator()->is_animating();
316 }
317
FinalizeDraggedWindow()318 void DragWindowFromShelfController::FinalizeDraggedWindow() {
319 if (!window_drag_result_.has_value()) {
320 started_in_overview_ = false;
321 return;
322 }
323
324 DCHECK(!drag_started_);
325 DCHECK(window_);
326
327 OnDragEnded(previous_location_in_screen_,
328 *window_drag_result_ == ShelfWindowDragResult::kGoToOverviewMode,
329 end_snap_position_);
330 }
331
OnWindowDestroying(aura::Window * window)332 void DragWindowFromShelfController::OnWindowDestroying(aura::Window* window) {
333 DCHECK_EQ(window_, window);
334
335 CancelDrag();
336 window_->RemoveObserver(this);
337 window_ = nullptr;
338 }
339
AddObserver(DragWindowFromShelfController::Observer * observer)340 void DragWindowFromShelfController::AddObserver(
341 DragWindowFromShelfController::Observer* observer) {
342 observers_.AddObserver(observer);
343 }
344
RemoveObserver(DragWindowFromShelfController::Observer * observer)345 void DragWindowFromShelfController::RemoveObserver(
346 DragWindowFromShelfController::Observer* observer) {
347 observers_.RemoveObserver(observer);
348 }
349
OnDragStarted(const gfx::PointF & location_in_screen)350 void DragWindowFromShelfController::OnDragStarted(
351 const gfx::PointF& location_in_screen) {
352 drag_started_ = true;
353 started_in_overview_ =
354 Shell::Get()->overview_controller()->InOverviewSession();
355 initial_location_in_screen_ = location_in_screen;
356 previous_location_in_screen_ = location_in_screen;
357 WindowState::Get(window_)->CreateDragDetails(
358 initial_location_in_screen_, HTCLIENT, ::wm::WINDOW_MOVE_SOURCE_TOUCH);
359
360 // Disable the backdrop on the dragged window during dragging.
361 WindowBackdrop::Get(window_)->DisableBackdrop();
362
363 // Hide all visible windows behind the dragged window during dragging.
364 windows_hider_ = std::make_unique<WindowsHider>(window_);
365
366 // Hide the home launcher until it's eligible to show it.
367 Shell::Get()->home_screen_controller()->OnWindowDragStarted();
368
369 // Use the same dim and blur as in overview during dragging.
370 RootWindowController::ForWindow(window_->GetRootWindow())
371 ->wallpaper_widget_controller()
372 ->SetWallpaperBlur(wallpaper_constants::kOverviewBlur);
373
374 // If the dragged window is one of the snapped window in splitview, it needs
375 // to be detached from splitview before start dragging.
376 SplitViewController* split_view_controller =
377 SplitViewController::Get(Shell::GetPrimaryRootWindow());
378 // Preserve initial snap position
379 if (split_view_controller->IsWindowInSplitView(window_)) {
380 initial_snap_position_ =
381 split_view_controller->GetPositionOfSnappedWindow(window_);
382 }
383 split_view_controller->OnWindowDragStarted(window_);
384 // Note SplitViewController::OnWindowDragStarted() may open overview.
385 if (Shell::Get()->overview_controller()->InOverviewSession())
386 OnWindowDragStartedInOverview();
387 }
388
OnDragEnded(const gfx::PointF & location_in_screen,bool should_drop_window_in_overview,SplitViewController::SnapPosition snap_position)389 void DragWindowFromShelfController::OnDragEnded(
390 const gfx::PointF& location_in_screen,
391 bool should_drop_window_in_overview,
392 SplitViewController::SnapPosition snap_position) {
393 OverviewController* overview_controller = Shell::Get()->overview_controller();
394 if (overview_controller->InOverviewSession()) {
395 // Make sure overview is visible after drag ends.
396 ShowOverviewDuringOrAfterDrag();
397
398 OverviewSession* overview_session = overview_controller->overview_session();
399 overview_session->ResetSplitViewDragIndicatorsWindowDraggingStates();
400
401 // No need to reposition overview windows if we are not dropping the dragged
402 // window into overview. Overview will either be exited or unchanged, and
403 // the extra movement from existing window will just add unnecessary
404 // movement which will also slow down our dragged window animation.
405 if (!should_drop_window_in_overview)
406 overview_session->SuspendReposition();
407 overview_session->OnWindowDragEnded(
408 window_, location_in_screen, should_drop_window_in_overview,
409 /*snap=*/snap_position != SplitViewController::NONE);
410 overview_session->ResumeReposition();
411 }
412
413 SplitViewController* split_view_controller =
414 SplitViewController::Get(Shell::GetPrimaryRootWindow());
415 if (split_view_controller->InSplitViewMode() ||
416 snap_position != SplitViewController::NONE) {
417 split_view_controller->OnWindowDragEnded(
418 window_, snap_position, gfx::ToRoundedPoint(location_in_screen));
419 }
420
421 // Scale-in-to-show home screen if home screen should be shown after drag
422 // ends.
423 Shell::Get()->home_screen_controller()->OnWindowDragEnded(/*animate=*/true);
424
425 // Clear the wallpaper dim and blur if not in overview after drag ends.
426 // If in overview, the dim and blur will be cleared after overview ends.
427 if (!overview_controller->InOverviewSession()) {
428 RootWindowController::ForWindow(window_->GetRootWindow())
429 ->wallpaper_widget_controller()
430 ->SetWallpaperBlur(wallpaper_constants::kClear);
431 }
432
433 DCHECK(window_drag_result_.has_value());
434 switch (*window_drag_result_) {
435 case ShelfWindowDragResult::kGoToHomeScreen:
436 ScaleDownWindowAfterDrag();
437 break;
438 case ShelfWindowDragResult::kRestoreToOriginalBounds:
439 ScaleUpToRestoreWindowAfterDrag();
440 break;
441 case ShelfWindowDragResult::kGoToOverviewMode:
442 case ShelfWindowDragResult::kGoToSplitviewMode:
443 case ShelfWindowDragResult::kDragCanceled:
444 // No action is needed.
445 break;
446 }
447 window_drag_result_.reset();
448 started_in_overview_ = false;
449 }
450
UpdateDraggedWindow(const gfx::PointF & location_in_screen)451 void DragWindowFromShelfController::UpdateDraggedWindow(
452 const gfx::PointF& location_in_screen) {
453 gfx::Rect bounds = window_->bounds();
454 ::wm::ConvertRectToScreen(window_->parent(), &bounds);
455
456 // Calculate the window's transform based on the location.
457 // For scale, at |initial_location_in_screen_| or bounds.bottom(), the scale
458 // is 1.0, and at the |min_y| position of its bounds, it reaches to its
459 // minimum scale |kMinimumWindowScaleDuringDragging|. Calculate the desired
460 // scale based on the current y position.
461 const gfx::Rect display_bounds =
462 display::Screen::GetScreen()
463 ->GetDisplayNearestPoint(gfx::ToRoundedPoint(location_in_screen))
464 .bounds();
465 const float min_y = display_bounds.y() +
466 display_bounds.height() * kMinYDisplayHeightRatio +
467 kMinimumWindowScaleDuringDragging * bounds.height();
468 float y_full =
469 std::min(initial_location_in_screen_.y(), (float)bounds.bottom()) - min_y;
470 float y_diff = location_in_screen.y() - min_y;
471 float scale = (1.0f - kMinimumWindowScaleDuringDragging) * y_diff / y_full +
472 kMinimumWindowScaleDuringDragging;
473 scale = base::ClampToRange(scale, /*min=*/kMinimumWindowScaleDuringDragging,
474 /*max=*/1.f);
475
476 // Calculate the desired translation so that the dragged window stays under
477 // the finger during the dragging.
478 // Since vertical drag doesn't start until after passing the top of the shelf,
479 // the y calculations should be relative to the window bounds instead of
480 // |initial_location_in_screen| (which is on the shelf)
481 gfx::Transform transform;
482 transform.Translate(
483 (location_in_screen.x() - bounds.x()) -
484 (initial_location_in_screen_.x() - bounds.x()) * scale,
485 (location_in_screen.y() - bounds.y()) - bounds.height() * scale);
486 transform.Scale(scale, scale);
487
488 // The dragged window cannot exceed the top or bottom of the display. So
489 // calculate the expected transformed bounds and then adjust the transform if
490 // needed.
491 gfx::RectF transformed_bounds(window_->bounds());
492 gfx::Transform new_tranform = TransformAboutPivot(
493 gfx::ToRoundedPoint(transformed_bounds.origin()), transform);
494 new_tranform.TransformRect(&transformed_bounds);
495 ::wm::TranslateRectToScreen(window_->parent(), &transformed_bounds);
496 if (transformed_bounds.y() < display_bounds.y()) {
497 transform.Translate(0,
498 (display_bounds.y() - transformed_bounds.y()) / scale);
499 } else if (transformed_bounds.bottom() > bounds.bottom()) {
500 DCHECK_EQ(1.f, scale);
501 transform.Translate(
502 0, (bounds.bottom() - transformed_bounds.bottom()) / scale);
503 }
504
505 SetTransform(window_, transform);
506 }
507
508 SplitViewController::SnapPosition
GetSnapPosition(const gfx::PointF & location_in_screen) const509 DragWindowFromShelfController::GetSnapPosition(
510 const gfx::PointF& location_in_screen) const {
511 // if |location_in_screen| is close to the bottom of the screen and is
512 // inside of GetReturnToMaximizedThreshold() threshold, we should not try to
513 // snap the window.
514 if (ShouldRestoreToOriginalBounds(location_in_screen))
515 return SplitViewController::NONE;
516
517 aura::Window* root_window = Shell::GetPrimaryRootWindow();
518 SplitViewController::SnapPosition snap_position = ::ash::GetSnapPosition(
519 root_window, window_, gfx::ToRoundedPoint(location_in_screen),
520 gfx::ToRoundedPoint(initial_location_in_screen_),
521 /*snap_distance_from_edge=*/kDistanceFromEdge,
522 /*minimum_drag_distance=*/kMinDragDistance,
523 /*horizontal_edge_inset=*/kScreenEdgeInsetForSnap,
524 /*vertical_edge_inset=*/kScreenEdgeInsetForSnap);
525
526 // For portrait mode, since the drag starts from the bottom of the screen,
527 // we should only allow the window to snap to the top of the screen.
528 const bool is_landscape = IsCurrentScreenOrientationLandscape();
529 const bool is_primary = IsCurrentScreenOrientationPrimary();
530 if (!is_landscape &&
531 ((is_primary && snap_position == SplitViewController::RIGHT) ||
532 (!is_primary && snap_position == SplitViewController::LEFT))) {
533 snap_position = SplitViewController::NONE;
534 }
535
536 return snap_position;
537 }
538
ShouldRestoreToOriginalBounds(const gfx::PointF & location_in_screen) const539 bool DragWindowFromShelfController::ShouldRestoreToOriginalBounds(
540 const gfx::PointF& location_in_screen) const {
541 const gfx::Rect display_bounds =
542 display::Screen::GetScreen()
543 ->GetDisplayNearestPoint(gfx::ToRoundedPoint(location_in_screen))
544 .bounds();
545 gfx::RectF transformed_window_bounds =
546 window_util::GetTransformedBounds(window_, /*top_inset=*/0);
547
548 return transformed_window_bounds.bottom() >
549 display_bounds.bottom() - GetReturnToMaximizedThreshold();
550 }
551
ShouldGoToHomeScreen(const gfx::PointF & location_in_screen,base::Optional<float> velocity_y) const552 bool DragWindowFromShelfController::ShouldGoToHomeScreen(
553 const gfx::PointF& location_in_screen,
554 base::Optional<float> velocity_y) const {
555 // If the drag ends below the shelf, do not go to home screen (theoretically
556 // it may happen in kExtended hotseat case when drag can start and end below
557 // the shelf).
558 if (location_in_screen.y() >=
559 Shelf::ForWindow(window_)->GetIdealBoundsForWorkAreaCalculation().y()) {
560 return false;
561 }
562
563 // Do not go home if we're in split screen.
564 if (SplitViewController::Get(Shell::GetPrimaryRootWindow())
565 ->InSplitViewMode()) {
566 return false;
567 }
568
569 // If overview is invisible when the drag ends, no matter what the velocity
570 // is, we should go to home screen.
571 if (Shell::Get()->overview_controller()->InOverviewSession() &&
572 !show_overview_windows_) {
573 return true;
574 }
575
576 // Otherwise go home if the velocity is large enough.
577 return velocity_y.has_value() && *velocity_y < 0 &&
578 std::abs(*velocity_y) >= kVelocityToHomeScreenThreshold;
579 }
580
581 SplitViewController::SnapPosition
GetSnapPositionOnDragEnd(const gfx::PointF & location_in_screen,base::Optional<float> velocity_y) const582 DragWindowFromShelfController::GetSnapPositionOnDragEnd(
583 const gfx::PointF& location_in_screen,
584 base::Optional<float> velocity_y) const {
585 if (!Shell::Get()->overview_controller()->InOverviewSession() ||
586 ShouldGoToHomeScreen(location_in_screen, velocity_y)) {
587 return SplitViewController::NONE;
588 }
589
590 // When dragging ends but restore to original bounds, we should restore
591 // window's initial snap position
592 if (ShouldRestoreToOriginalBounds(location_in_screen))
593 return initial_snap_position_;
594
595 return GetSnapPosition(location_in_screen);
596 }
597
ShouldDropWindowInOverview(const gfx::PointF & location_in_screen,base::Optional<float> velocity_y) const598 bool DragWindowFromShelfController::ShouldDropWindowInOverview(
599 const gfx::PointF& location_in_screen,
600 base::Optional<float> velocity_y) const {
601 if (!Shell::Get()->overview_controller()->InOverviewSession())
602 return false;
603
604 if (ShouldGoToHomeScreen(location_in_screen, velocity_y))
605 return false;
606
607 const bool in_splitview =
608 SplitViewController::Get(Shell::GetPrimaryRootWindow())
609 ->InSplitViewMode();
610 if (!in_splitview && ShouldRestoreToOriginalBounds(location_in_screen)) {
611 return false;
612 }
613
614 if (in_splitview) {
615 if (velocity_y.has_value() && *velocity_y < 0 &&
616 std::abs(*velocity_y) >= kVelocityToOverviewThreshold) {
617 return true;
618 }
619 if (ShouldRestoreToOriginalBounds(location_in_screen))
620 return false;
621 }
622
623 return GetSnapPositionOnDragEnd(location_in_screen, velocity_y) ==
624 SplitViewController::NONE;
625 }
626
ReshowHiddenWindowsOnDragEnd()627 void DragWindowFromShelfController::ReshowHiddenWindowsOnDragEnd() {
628 windows_hider_->RestoreWindowsVisibility();
629 }
630
ShowOverviewDuringOrAfterDrag()631 void DragWindowFromShelfController::ShowOverviewDuringOrAfterDrag() {
632 show_overview_timer_.Stop();
633 OverviewController* overview_controller = Shell::Get()->overview_controller();
634 if (!overview_controller->InOverviewSession())
635 return;
636
637 show_overview_windows_ = true;
638 overview_controller->overview_session()->SetVisibleDuringWindowDragging(
639 /*visible=*/true, /*animate=*/true);
640 for (Observer& observer : observers_)
641 observer.OnOverviewVisibilityChanged(true);
642 }
643
HideOverviewDuringDrag()644 void DragWindowFromShelfController::HideOverviewDuringDrag() {
645 show_overview_windows_ = false;
646
647 OverviewController* overview_controller = Shell::Get()->overview_controller();
648 if (!overview_controller->InOverviewSession())
649 return;
650 overview_controller->overview_session()->SetVisibleDuringWindowDragging(
651 /*visible=*/false,
652 /*animate=*/false);
653 for (Observer& observer : observers_)
654 observer.OnOverviewVisibilityChanged(false);
655 }
656
ScaleDownWindowAfterDrag()657 void DragWindowFromShelfController::ScaleDownWindowAfterDrag() {
658 // Notify home screen controller that the home screen is about to be shown, so
659 // home screen and shelf start updating their state as the window is
660 // minimizing.
661 Shell::Get()
662 ->home_screen_controller()
663 ->delegate()
664 ->OnHomeLauncherPositionChanged(
665 /*percent_shown=*/100,
666 display::Screen::GetScreen()->GetPrimaryDisplay().id());
667
668 // Do the scale-down transform for the entire transient tree.
669 for (auto* window : GetTransientTreeIterator(window_)) {
670 // self-destructed when window transform animation is done.
671 new WindowScaleAnimation(
672 window, WindowScaleAnimation::WindowScaleType::kScaleDownToShelf,
673 window == window_
674 ? base::BindOnce(
675 &DragWindowFromShelfController::OnWindowScaledDownAfterDrag,
676 weak_ptr_factory_.GetWeakPtr())
677 : base::NullCallback());
678 }
679 }
680
OnWindowScaledDownAfterDrag()681 void DragWindowFromShelfController::OnWindowScaledDownAfterDrag() {
682 HomeScreenController* home_screen_controller =
683 Shell::Get()->home_screen_controller();
684 if (!home_screen_controller || !home_screen_controller->delegate())
685 return;
686
687 home_screen_controller->delegate()->OnHomeLauncherAnimationComplete(
688 /*shown=*/true, display::Screen::GetScreen()->GetPrimaryDisplay().id());
689 }
690
ScaleUpToRestoreWindowAfterDrag()691 void DragWindowFromShelfController::ScaleUpToRestoreWindowAfterDrag() {
692 // Do the scale up transform for the entire transient tee.
693 for (auto* window : GetTransientTreeIterator(window_)) {
694 new WindowScaleAnimation(
695 window, WindowScaleAnimation::WindowScaleType::kScaleUpToRestore,
696 base::BindOnce(
697 &DragWindowFromShelfController::OnWindowRestoredToOrignalBounds,
698 weak_ptr_factory_.GetWeakPtr(),
699 /*should_end_overview=*/!started_in_overview_));
700 }
701 }
702
OnWindowRestoredToOrignalBounds(bool end_overview)703 void DragWindowFromShelfController::OnWindowRestoredToOrignalBounds(
704 bool end_overview) {
705 base::AutoReset<bool> auto_reset(&during_window_restoration_callback_, true);
706 if (end_overview) {
707 Shell::Get()->overview_controller()->EndOverview(
708 OverviewEnterExitType::kImmediateExit);
709 }
710 ReshowHiddenWindowsOnDragEnd();
711 }
712
OnWindowDragStartedInOverview()713 void DragWindowFromShelfController::OnWindowDragStartedInOverview() {
714 OverviewSession* overview_session =
715 Shell::Get()->overview_controller()->overview_session();
716 DCHECK(overview_session);
717 overview_session->OnWindowDragStarted(window_, /*animate=*/false);
718 if (ShouldAllowSplitView())
719 overview_session->SetSplitViewDragIndicatorsDraggedWindow(window_);
720 // Hide overview windows first and fade in the windows after delaying
721 // kShowOverviewTimeWhenDragSuspend.
722 HideOverviewDuringDrag();
723 }
724
725 } // namespace ash
726