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