1 // Copyright 2014 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/wm/overview/overview_grid.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <memory>
10 #include <utility>
11 
12 #include "ash/frame_throttler/frame_throttling_controller.h"
13 #include "ash/metrics/histogram_macros.h"
14 #include "ash/public/cpp/ash_features.h"
15 #include "ash/public/cpp/metrics_util.h"
16 #include "ash/public/cpp/presentation_time_recorder.h"
17 #include "ash/public/cpp/shelf_config.h"
18 #include "ash/public/cpp/shelf_types.h"
19 #include "ash/public/cpp/window_properties.h"
20 #include "ash/root_window_controller.h"
21 #include "ash/root_window_settings.h"
22 #include "ash/rotator/screen_rotation_animator.h"
23 #include "ash/screen_util.h"
24 #include "ash/shell.h"
25 #include "ash/wallpaper/wallpaper_controller_impl.h"
26 #include "ash/wm/desks/desk_mini_view.h"
27 #include "ash/wm/desks/desk_name_view.h"
28 #include "ash/wm/desks/desks_bar_view.h"
29 #include "ash/wm/desks/desks_util.h"
30 #include "ash/wm/mru_window_tracker.h"
31 #include "ash/wm/overview/cleanup_animation_observer.h"
32 #include "ash/wm/overview/drop_target_view.h"
33 #include "ash/wm/overview/overview_constants.h"
34 #include "ash/wm/overview/overview_controller.h"
35 #include "ash/wm/overview/overview_delegate.h"
36 #include "ash/wm/overview/overview_grid_event_handler.h"
37 #include "ash/wm/overview/overview_highlight_controller.h"
38 #include "ash/wm/overview/overview_item.h"
39 #include "ash/wm/overview/overview_item_view.h"
40 #include "ash/wm/overview/overview_session.h"
41 #include "ash/wm/overview/overview_utils.h"
42 #include "ash/wm/overview/overview_window_drag_controller.h"
43 #include "ash/wm/overview/scoped_overview_animation_settings.h"
44 #include "ash/wm/splitview/split_view_constants.h"
45 #include "ash/wm/splitview/split_view_divider.h"
46 #include "ash/wm/splitview/split_view_utils.h"
47 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
48 #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
49 #include "ash/wm/window_util.h"
50 #include "ash/wm/workspace/backdrop_controller.h"
51 #include "ash/wm/workspace/workspace_layout_manager.h"
52 #include "ash/wm/workspace_controller.h"
53 #include "base/bind.h"
54 #include "base/containers/unique_ptr_adapters.h"
55 #include "base/numerics/ranges.h"
56 #include "base/numerics/safe_conversions.h"
57 #include "base/strings/utf_string_conversions.h"
58 #include "ui/aura/client/aura_constants.h"
59 #include "ui/compositor/layer_animation_observer.h"
60 #include "ui/compositor/throughput_tracker.h"
61 #include "ui/gfx/geometry/vector2d_f.h"
62 #include "ui/gfx/transform_util.h"
63 #include "ui/views/view.h"
64 #include "ui/views/widget/widget.h"
65 #include "ui/wm/core/coordinate_conversion.h"
66 #include "ui/wm/core/window_animations.h"
67 
68 namespace ash {
69 namespace {
70 
71 // Windows are not allowed to get taller than this.
72 constexpr int kMaxHeight = 512;
73 
74 // Margins reserved in the overview mode.
75 constexpr float kOverviewInsetRatio = 0.05f;
76 
77 // Additional vertical inset reserved for windows in overview mode.
78 constexpr float kOverviewVerticalInset = 0.1f;
79 
80 // Number of rows for windows in tablet overview mode.
81 constexpr int kTabletLayoutRow = 2;
82 
83 constexpr int kMinimumItemsForNewLayout = 6;
84 
85 // Wait a while before unpausing the occlusion tracker after a scroll has
86 // completed as the user may start another scroll.
87 constexpr base::TimeDelta kOcclusionUnpauseDurationForScroll =
88     base::TimeDelta::FromMilliseconds(500);
89 
90 constexpr base::TimeDelta kOcclusionUnpauseDurationForRotation =
91     base::TimeDelta::FromMilliseconds(300);
92 
93 // Histogram names for overview enter/exit smoothness in clamshell,
94 // tablet mode and splitview.
95 constexpr char kOverviewEnterClamshellHistogram[] =
96     "Ash.Overview.AnimationSmoothness.Enter.ClamshellMode";
97 constexpr char kOverviewEnterSingleClamshellHistogram[] =
98     "Ash.Overview.AnimationSmoothness.Enter.SingleClamshellMode";
99 constexpr char kOverviewEnterTabletHistogram[] =
100     "Ash.Overview.AnimationSmoothness.Enter.TabletMode";
101 constexpr char kOverviewEnterMinimizedTabletHistogram[] =
102     "Ash.Overview.AnimationSmoothness.Enter.MinimizedTabletMode";
103 constexpr char kOverviewEnterSplitViewHistogram[] =
104     "Ash.Overview.AnimationSmoothness.Enter.SplitView";
105 
106 constexpr char kOverviewExitClamshellHistogram[] =
107     "Ash.Overview.AnimationSmoothness.Exit.ClamshellMode";
108 constexpr char kOverviewExitSingleClamshellHistogram[] =
109     "Ash.Overview.AnimationSmoothness.Exit.SingleClamshellMode";
110 constexpr char kOverviewExitTabletHistogram[] =
111     "Ash.Overview.AnimationSmoothness.Exit.TabletMode";
112 constexpr char kOverviewExitMinimizedTabletHistogram[] =
113     "Ash.Overview.AnimationSmoothness.Exit.MinimizedTabletMode";
114 constexpr char kOverviewExitSplitViewHistogram[] =
115     "Ash.Overview.AnimationSmoothness.Exit.SplitView";
116 
117 // The UMA histogram that records presentation time for grid scrolling in the
118 // new overview layout.
119 constexpr char kOverviewScrollHistogram[] =
120     "Ash.Overview.Scroll.PresentationTime.TabletMode";
121 constexpr char kOverviewScrollMaxLatencyHistogram[] =
122     "Ash.Overview.Scroll.PresentationTime.MaxLatency.TabletMode";
123 
124 template <const char* clamshell_single_name,
125           const char* clamshell_multi_name,
126           const char* tablet_name,
127           const char* splitview_name,
128           const char* tablet_minimized_name>
129 class OverviewMetricsTracker : public OverviewGrid::MetricsTracker {
130  public:
OverviewMetricsTracker(ui::Compositor * compositor,bool in_split_view,bool single_animation_in_clamshell,bool minimized_in_tablet)131   OverviewMetricsTracker(ui::Compositor* compositor,
132                          bool in_split_view,
133                          bool single_animation_in_clamshell,
134                          bool minimized_in_tablet)
135       : tracker_(compositor->RequestNewThroughputTracker()) {
136     tracker_.Start(metrics_util::ForSmoothness(base::BindRepeating(
137         &OverviewMetricsTracker::ReportOverviewSmoothness, in_split_view,
138         single_animation_in_clamshell, minimized_in_tablet)));
139   }
140   OverviewMetricsTracker(const OverviewMetricsTracker&) = delete;
141   OverviewMetricsTracker& operator=(const OverviewMetricsTracker&) = delete;
~OverviewMetricsTracker()142   ~OverviewMetricsTracker() override { tracker_.Stop(); }
143 
ReportOverviewSmoothness(bool in_split_view,bool single_animation_in_clamshell,bool minimized_in_tablet,int smoothness)144   static void ReportOverviewSmoothness(bool in_split_view,
145                                        bool single_animation_in_clamshell,
146                                        bool minimized_in_tablet,
147                                        int smoothness) {
148     if (single_animation_in_clamshell)
149       UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_single_name, smoothness);
150     else
151       UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_multi_name, smoothness);
152 
153     if (minimized_in_tablet) {
154       UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(
155           in_split_view, tablet_minimized_name, smoothness);
156     } else {
157       UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(in_split_view,
158                                                        tablet_name, smoothness);
159     }
160     UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(in_split_view, splitview_name,
161                                           smoothness);
162   }
163 
164  private:
165   ui::ThroughputTracker tracker_;
166 };
167 
168 using OverviewEnterMetricsTracker =
169     OverviewMetricsTracker<kOverviewEnterSingleClamshellHistogram,
170                            kOverviewEnterClamshellHistogram,
171                            kOverviewEnterTabletHistogram,
172                            kOverviewEnterSplitViewHistogram,
173                            kOverviewEnterMinimizedTabletHistogram>;
174 using OverviewExitMetricsTracker =
175     OverviewMetricsTracker<kOverviewExitSingleClamshellHistogram,
176                            kOverviewExitClamshellHistogram,
177                            kOverviewExitTabletHistogram,
178                            kOverviewExitSplitViewHistogram,
179                            kOverviewExitMinimizedTabletHistogram>;
180 
181 class ShutdownAnimationMetricsTrackerObserver : public OverviewObserver {
182  public:
ShutdownAnimationMetricsTrackerObserver(ui::Compositor * compositor,bool in_split_view,bool single_animation,bool minimized_in_tablet)183   ShutdownAnimationMetricsTrackerObserver(ui::Compositor* compositor,
184                                           bool in_split_view,
185                                           bool single_animation,
186                                           bool minimized_in_tablet)
187       : metrics_tracker_(compositor,
188                          in_split_view,
189                          single_animation,
190                          minimized_in_tablet) {
191     Shell::Get()->overview_controller()->AddObserver(this);
192   }
193   ShutdownAnimationMetricsTrackerObserver(
194       const ShutdownAnimationMetricsTrackerObserver&) = delete;
195   ShutdownAnimationMetricsTrackerObserver& operator=(
196       const ShutdownAnimationMetricsTrackerObserver&) = delete;
~ShutdownAnimationMetricsTrackerObserver()197   ~ShutdownAnimationMetricsTrackerObserver() override {
198     Shell::Get()->overview_controller()->RemoveObserver(this);
199   }
200 
201   // OverviewObserver:
OnOverviewModeEndingAnimationComplete(bool canceled)202   void OnOverviewModeEndingAnimationComplete(bool canceled) override {
203     delete this;
204   }
205 
206  private:
207   OverviewExitMetricsTracker metrics_tracker_;
208 };
209 
210 // Creates |drop_target_widget_|. It's created when a window or overview item is
211 // dragged around, and destroyed when the drag ends.
CreateDropTargetWidget(aura::Window * root_window,aura::Window * dragged_window)212 std::unique_ptr<views::Widget> CreateDropTargetWidget(
213     aura::Window* root_window,
214     aura::Window* dragged_window) {
215   views::Widget::InitParams params;
216   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
217   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
218   params.activatable = views::Widget::InitParams::Activatable::ACTIVATABLE_NO;
219   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
220   params.name = "OverviewDropTarget";
221   params.accept_events = false;
222   params.parent = desks_util::GetActiveDeskContainerForRoot(root_window);
223   params.init_properties_container.SetProperty(kHideInDeskMiniViewKey, true);
224   auto widget = std::make_unique<views::Widget>();
225   widget->set_focus_on_creation(false);
226   widget->Init(std::move(params));
227   widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
228 
229   // Show plus icon if drag a tab from a multi-tab window.
230   widget->SetContentsView(std::make_unique<DropTargetView>(
231       dragged_window->GetProperty(ash::kTabDraggingSourceWindowKey)));
232   aura::Window* drop_target_window = widget->GetNativeWindow();
233   drop_target_window->parent()->StackChildAtBottom(drop_target_window);
234   widget->Show();
235   return widget;
236 }
237 
GetWantedDropTargetOpacity(SplitViewDragIndicators::WindowDraggingState window_dragging_state)238 float GetWantedDropTargetOpacity(
239     SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
240   switch (window_dragging_state) {
241     case SplitViewDragIndicators::WindowDraggingState::kNoDrag:
242     case SplitViewDragIndicators::WindowDraggingState::kOtherDisplay:
243     case SplitViewDragIndicators::WindowDraggingState::kToSnapLeft:
244     case SplitViewDragIndicators::WindowDraggingState::kToSnapRight:
245       return 0.f;
246     case SplitViewDragIndicators::WindowDraggingState::kFromOverview:
247     case SplitViewDragIndicators::WindowDraggingState::kFromTop:
248     case SplitViewDragIndicators::WindowDraggingState::kFromShelf:
249       return 1.f;
250   }
251 }
252 
GetGridInsets(const gfx::Rect & grid_bounds)253 gfx::Insets GetGridInsets(const gfx::Rect& grid_bounds) {
254   const int horizontal_inset =
255       base::ClampFloor(std::min(kOverviewInsetRatio * grid_bounds.width(),
256                                 kOverviewInsetRatio * grid_bounds.height()));
257   const int vertical_inset =
258       horizontal_inset +
259       kOverviewVerticalInset * (grid_bounds.height() - 2 * horizontal_inset);
260 
261   return gfx::Insets(std::max(0, vertical_inset - kWindowMargin),
262                      std::max(0, horizontal_inset - kWindowMargin));
263 }
264 
ShouldExcludeItemFromGridLayout(OverviewItem * item,const base::flat_set<OverviewItem * > & ignored_items)265 bool ShouldExcludeItemFromGridLayout(
266     OverviewItem* item,
267     const base::flat_set<OverviewItem*>& ignored_items) {
268   return item->animating_to_close() || ignored_items.contains(item);
269 }
270 
271 }  // namespace
272 
273 // The class to observe the overview window that the dragged tabs will merge
274 // into. After the dragged tabs merge into the overview window, and if the
275 // overview window represents a minimized window, we need to update the
276 // overview minimized widget's content view so that it reflects the merge.
277 class OverviewGrid::TargetWindowObserver : public aura::WindowObserver {
278  public:
279   TargetWindowObserver() = default;
~TargetWindowObserver()280   ~TargetWindowObserver() override { StopObserving(); }
281 
StartObserving(aura::Window * window)282   void StartObserving(aura::Window* window) {
283     if (target_window_)
284       StopObserving();
285 
286     target_window_ = window;
287     target_window_->AddObserver(this);
288   }
289 
290   // aura::WindowObserver:
OnWindowPropertyChanged(aura::Window * window,const void * key,intptr_t old)291   void OnWindowPropertyChanged(aura::Window* window,
292                                const void* key,
293                                intptr_t old) override {
294     DCHECK_EQ(window, target_window_);
295     // When the property is cleared, the dragged window should have been merged
296     // into |target_window_|, update the corresponding window item in overview.
297     if (key == kIsDeferredTabDraggingTargetWindowKey &&
298         !window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)) {
299       UpdateWindowItemInOverviewContaining(window);
300       StopObserving();
301     }
302   }
303 
OnWindowDestroying(aura::Window * window)304   void OnWindowDestroying(aura::Window* window) override {
305     DCHECK_EQ(window, target_window_);
306     StopObserving();
307   }
308 
309  private:
UpdateWindowItemInOverviewContaining(aura::Window * window)310   void UpdateWindowItemInOverviewContaining(aura::Window* window) {
311     OverviewController* overview_controller =
312         Shell::Get()->overview_controller();
313     if (!overview_controller->InOverviewSession())
314       return;
315 
316     OverviewGrid* grid =
317         overview_controller->overview_session()->GetGridWithRootWindow(
318             window->GetRootWindow());
319     if (!grid)
320       return;
321 
322     OverviewItem* item = grid->GetOverviewItemContaining(window);
323     if (!item)
324       return;
325 
326     item->UpdateItemContentViewForMinimizedWindow();
327   }
328 
StopObserving()329   void StopObserving() {
330     if (target_window_)
331       target_window_->RemoveObserver(this);
332     target_window_ = nullptr;
333   }
334 
335   aura::Window* target_window_ = nullptr;
336 
337   DISALLOW_COPY_AND_ASSIGN(TargetWindowObserver);
338 };
339 
OverviewGrid(aura::Window * root_window,const std::vector<aura::Window * > & windows,OverviewSession * overview_session)340 OverviewGrid::OverviewGrid(aura::Window* root_window,
341                            const std::vector<aura::Window*>& windows,
342                            OverviewSession* overview_session)
343     : root_window_(root_window),
344       overview_session_(overview_session),
345       split_view_drag_indicators_(
346           ShouldAllowSplitView()
347               ? std::make_unique<SplitViewDragIndicators>(root_window)
348               : nullptr),
349       bounds_(GetGridBoundsInScreen(root_window)) {
350   for (auto* window : windows) {
351     if (window->GetRootWindow() != root_window)
352       continue;
353 
354     // Stop ongoing animations before entering overview mode. Because we are
355     // deferring SetTransform of the windows beneath the window covering the
356     // available workspace, we need to set the correct transforms of these
357     // windows before entering overview mode again in the
358     // OnImplicitAnimationsCompleted() of the observer of the
359     // available-workspace-covering window's animation.
360     auto* animator = window->layer()->GetAnimator();
361     if (animator->is_animating())
362       window->layer()->GetAnimator()->StopAnimating();
363     window_list_.push_back(
364         std::make_unique<OverviewItem>(window, overview_session_, this));
365   }
366 }
367 
368 OverviewGrid::~OverviewGrid() = default;
369 
Shutdown()370 void OverviewGrid::Shutdown() {
371   EndNudge();
372 
373   SplitViewController::Get(root_window_)->RemoveObserver(this);
374   ScreenRotationAnimator::GetForRootWindow(root_window_)->RemoveObserver(this);
375   Shell::Get()->wallpaper_controller()->RemoveObserver(this);
376   grid_event_handler_.reset();
377 
378   bool has_non_cover_animating = false;
379   int animate_count = 0;
380 
381   for (const auto& window : window_list_) {
382     if (window->should_animate_when_exiting() && !has_non_cover_animating) {
383       has_non_cover_animating |=
384           !CanCoverAvailableWorkspace(window->GetWindow());
385       animate_count++;
386     }
387     window->Shutdown();
388   }
389   bool single_animation_in_clamshell =
390       (animate_count == 1 && !has_non_cover_animating) &&
391       !Shell::Get()->tablet_mode_controller()->InTabletMode();
392 
393   const bool in_split_view =
394       SplitViewController::Get(root_window_)->InSplitViewMode();
395   // OverviewGrid in splitscreen does not include the window to be activated.
396   if (!window_list_.empty() || in_split_view) {
397     bool minimized_in_tablet = overview_session_->enter_exit_overview_type() ==
398                                OverviewEnterExitType::kFadeOutExit;
399     // The following instance self-destructs when shutdown animation ends.
400     new ShutdownAnimationMetricsTrackerObserver(
401         root_window_->layer()->GetCompositor(), in_split_view,
402         single_animation_in_clamshell, minimized_in_tablet);
403   }
404 
405   window_list_.clear();
406   overview_session_ = nullptr;
407 }
408 
PrepareForOverview()409 void OverviewGrid::PrepareForOverview() {
410   if (!ShouldAnimateWallpaper(root_window_))
411     MaybeInitDesksWidget();
412 
413   for (const auto& window : window_list_)
414     window->PrepareForOverview();
415   SplitViewController::Get(root_window_)->AddObserver(this);
416   if (Shell::Get()->tablet_mode_controller()->InTabletMode())
417     ScreenRotationAnimator::GetForRootWindow(root_window_)->AddObserver(this);
418 
419   grid_event_handler_ = std::make_unique<OverviewGridEventHandler>(this);
420   Shell::Get()->wallpaper_controller()->AddObserver(this);
421 }
422 
PositionWindows(bool animate,const base::flat_set<OverviewItem * > & ignored_items,OverviewTransition transition)423 void OverviewGrid::PositionWindows(
424     bool animate,
425     const base::flat_set<OverviewItem*>& ignored_items,
426     OverviewTransition transition) {
427   if (!overview_session_ || suspend_reposition_ || window_list_.empty())
428     return;
429 
430   DCHECK_NE(transition, OverviewTransition::kExit);
431 
432   std::vector<gfx::RectF> rects =
433       ShouldUseTabletModeGridLayout() &&
434               (window_list_.size() - ignored_items.size() >=
435                kMinimumItemsForNewLayout)
436           ? GetWindowRectsForTabletModeLayout(ignored_items)
437           : GetWindowRects(ignored_items);
438 
439   if (transition == OverviewTransition::kEnter) {
440     CalculateWindowListAnimationStates(/*selected_item=*/nullptr, transition,
441                                        rects);
442   }
443 
444   // Position the windows centering the left-aligned rows vertically. Do not
445   // position items in |ignored_items|.
446   OverviewAnimationType animation_type = OVERVIEW_ANIMATION_NONE;
447   switch (transition) {
448     case OverviewTransition::kEnter: {
449       const bool entering_from_home =
450           overview_session_->enter_exit_overview_type() ==
451           OverviewEnterExitType::kFadeInEnter;
452       animation_type = entering_from_home
453                            ? OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER
454                            : OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_ON_ENTER;
455       break;
456     }
457     case OverviewTransition::kInOverview:
458       animation_type = OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW;
459       break;
460     case OverviewTransition::kExit:
461       NOTREACHED();
462   }
463 
464   int animate_count = 0;
465   bool has_non_cover_animating = false;
466   std::vector<OverviewAnimationType> animation_types(rects.size());
467 
468   const bool can_do_spawn_animation =
469       animate && transition == OverviewTransition::kInOverview;
470 
471   for (size_t i = 0; i < window_list_.size(); ++i) {
472     OverviewItem* window_item = window_list_[i].get();
473     if (ShouldExcludeItemFromGridLayout(window_item, ignored_items)) {
474       rects[i].SetRect(0, 0, 0, 0);
475       continue;
476     }
477 
478     // Calculate if each window item needs animation.
479     bool should_animate_item = animate;
480     // If we're in entering overview process, not all window items in the grid
481     // might need animation even if the grid needs animation.
482     if (animate && transition == OverviewTransition::kEnter)
483       should_animate_item = window_item->should_animate_when_entering();
484 
485     if (animate && transition == OverviewTransition::kEnter) {
486       if (window_item->should_animate_when_entering() &&
487           !has_non_cover_animating) {
488         has_non_cover_animating |=
489             !CanCoverAvailableWorkspace(window_item->GetWindow());
490         ++animate_count;
491       }
492     }
493 
494     if (can_do_spawn_animation && window_item->should_use_spawn_animation())
495       animation_type = OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW;
496 
497     animation_types[i] =
498         should_animate_item ? animation_type : OVERVIEW_ANIMATION_NONE;
499   }
500 
501   if (animate && transition == OverviewTransition::kEnter &&
502       !window_list_.empty()) {
503     bool single_animation_in_clamshell =
504         animate_count == 1 && !has_non_cover_animating &&
505         !Shell::Get()->tablet_mode_controller()->InTabletMode();
506     bool minimized_in_tablet = overview_session_->enter_exit_overview_type() ==
507                                OverviewEnterExitType::kFadeInEnter;
508     metrics_tracker_ = std::make_unique<OverviewEnterMetricsTracker>(
509         window_list_[0]->GetWindow()->layer()->GetCompositor(),
510         SplitViewController::Get(root_window_)->InSplitViewMode(),
511         single_animation_in_clamshell, minimized_in_tablet);
512   }
513 
514   // Apply the animation after creating metrics_tracker_ so that unit test
515   // can correctly count the measure requests.
516   for (size_t i = 0; i < window_list_.size(); ++i) {
517     if (rects[i].IsEmpty())
518       continue;
519     OverviewItem* window_item = window_list_[i].get();
520     window_item->SetBounds(rects[i], animation_types[i]);
521   }
522 }
523 
GetOverviewItemContaining(const aura::Window * window) const524 OverviewItem* OverviewGrid::GetOverviewItemContaining(
525     const aura::Window* window) const {
526   for (const auto& window_item : window_list_) {
527     if (window_item && window_item->Contains(window))
528       return window_item.get();
529   }
530   return nullptr;
531 }
532 
AddItem(aura::Window * window,bool reposition,bool animate,const base::flat_set<OverviewItem * > & ignored_items,size_t index,bool use_spawn_animation,bool restack)533 void OverviewGrid::AddItem(aura::Window* window,
534                            bool reposition,
535                            bool animate,
536                            const base::flat_set<OverviewItem*>& ignored_items,
537                            size_t index,
538                            bool use_spawn_animation,
539                            bool restack) {
540   DCHECK(!GetOverviewItemContaining(window));
541   DCHECK_LE(index, window_list_.size());
542 
543   window_list_.insert(
544       window_list_.begin() + index,
545       std::make_unique<OverviewItem>(window, overview_session_, this));
546 
547   UpdateFrameThrottling();
548   auto* item = window_list_[index].get();
549   item->PrepareForOverview();
550 
551   if (animate && use_spawn_animation && reposition) {
552     item->set_should_use_spawn_animation(true);
553   } else {
554     // The item is added after overview enter animation is complete, so
555     // just call OnStartingAnimationComplete() only if we won't animate it with
556     // with the spawn animation. Otherwise, OnStartingAnimationComplete() will
557     // be called when the spawn-item-animation completes (See
558     // OverviewItem::OnItemSpawnedAnimationCompleted()).
559     item->OnStartingAnimationComplete();
560   }
561 
562   if (restack) {
563     if (reposition && animate)
564       item->set_should_restack_on_animation_end(true);
565     else
566       item->Restack();
567   }
568   if (reposition)
569     PositionWindows(animate, ignored_items);
570 }
571 
AppendItem(aura::Window * window,bool reposition,bool animate,bool use_spawn_animation)572 void OverviewGrid::AppendItem(aura::Window* window,
573                               bool reposition,
574                               bool animate,
575                               bool use_spawn_animation) {
576   AddItem(window, reposition, animate, /*ignored_items=*/{},
577           window_list_.size(), use_spawn_animation, /*restack=*/false);
578 }
579 
AddItemInMruOrder(aura::Window * window,bool reposition,bool animate,bool restack)580 void OverviewGrid::AddItemInMruOrder(aura::Window* window,
581                                      bool reposition,
582                                      bool animate,
583                                      bool restack) {
584   AddItem(window, reposition, animate, /*ignored_items=*/{},
585           FindInsertionIndex(window), /*use_spawn_animation=*/false, restack);
586 }
587 
RemoveItem(OverviewItem * overview_item,bool item_destroying,bool reposition)588 void OverviewGrid::RemoveItem(OverviewItem* overview_item,
589                               bool item_destroying,
590                               bool reposition) {
591   EndNudge();
592 
593   // Use reverse iterator to be efficient when removing all.
594   auto iter = std::find_if(window_list_.rbegin(), window_list_.rend(),
595                            base::MatchesUniquePtr(overview_item));
596   DCHECK(iter != window_list_.rend());
597 
598   // This can also be called when shutting down |this|, at which the item will
599   // be cleaning up and its associated view may be nullptr. |overview_item|
600   // needs to still be in |window_list_| so we can compute what the deleted
601   // index is.
602   if (overview_session_ && (*iter)->overview_item_view()) {
603     overview_session_->highlight_controller()->OnViewDestroyingOrDisabling(
604         (*iter)->overview_item_view());
605   }
606 
607   // Erase from the list first because deleting OverviewItem can lead to
608   // iterating through the |window_list_|.
609   std::unique_ptr<OverviewItem> tmp = std::move(*iter);
610   window_list_.erase(std::next(iter).base());
611   tmp.reset();
612 
613   UpdateFrameThrottling();
614 
615   if (!item_destroying)
616     return;
617 
618   if (!overview_session_)
619     return;
620 
621   if (empty()) {
622     overview_session_->OnGridEmpty();
623     return;
624   }
625 
626   if (reposition) {
627     // Update the grid bounds if needed and reposition the windows minus the
628     // currently overview dragged window, if there is one. Note: this does not
629     // update the grid bounds if the window being dragged from the top or shelf,
630     // the former being handled in TabletModeWindowDragDelegate's destructor.
631     base::flat_set<OverviewItem*> ignored_items;
632     OverviewItem* dragged_item =
633         overview_session_->GetCurrentDraggedOverviewItem();
634     if (dragged_item)
635       ignored_items.insert(dragged_item);
636     const gfx::Rect grid_bounds = GetGridBoundsInScreen(
637         root_window_,
638         split_view_drag_indicators_
639             ? base::make_optional(
640                   split_view_drag_indicators_->current_window_dragging_state())
641             : base::nullopt,
642         /*divider_changed=*/false,
643         /*account_for_hotseat=*/true);
644     SetBoundsAndUpdatePositions(grid_bounds, ignored_items, /*animate=*/true);
645   }
646 }
647 
AddDropTargetForDraggingFromThisGrid(OverviewItem * dragged_item)648 void OverviewGrid::AddDropTargetForDraggingFromThisGrid(
649     OverviewItem* dragged_item) {
650   DCHECK(!drop_target_widget_);
651   drop_target_widget_ =
652       CreateDropTargetWidget(root_window_, dragged_item->GetWindow());
653   const size_t position = GetOverviewItemIndex(dragged_item) + 1u;
654   overview_session_->AddItem(drop_target_widget_->GetNativeWindow(),
655                              /*reposition=*/true, /*animate=*/false,
656                              /*ignored_items=*/{dragged_item}, position);
657 }
658 
AddDropTargetNotForDraggingFromThisGrid(aura::Window * dragged_window,bool animate)659 void OverviewGrid::AddDropTargetNotForDraggingFromThisGrid(
660     aura::Window* dragged_window,
661     bool animate) {
662   DCHECK(!drop_target_widget_);
663   drop_target_widget_ = CreateDropTargetWidget(root_window_, dragged_window);
664   aura::Window* drop_target_window = drop_target_widget_->GetNativeWindow();
665   if (animate) {
666     drop_target_widget_->SetOpacity(0.f);
667     ScopedOverviewAnimationSettings settings(
668         OVERVIEW_ANIMATION_DROP_TARGET_FADE, drop_target_window);
669     drop_target_widget_->SetOpacity(1.f);
670   }
671   const size_t position = FindInsertionIndex(dragged_window);
672   overview_session_->AddItem(drop_target_window, /*reposition=*/true, animate,
673                              /*ignored_items=*/{}, position);
674 }
675 
RemoveDropTarget()676 void OverviewGrid::RemoveDropTarget() {
677   DCHECK(drop_target_widget_);
678   OverviewItem* drop_target = GetDropTarget();
679   overview_session_->RemoveItem(drop_target);
680   drop_target_widget_.reset();
681 }
682 
SetBoundsAndUpdatePositions(const gfx::Rect & bounds_in_screen,const base::flat_set<OverviewItem * > & ignored_items,bool animate)683 void OverviewGrid::SetBoundsAndUpdatePositions(
684     const gfx::Rect& bounds_in_screen,
685     const base::flat_set<OverviewItem*>& ignored_items,
686     bool animate) {
687   bounds_ = bounds_in_screen;
688   MaybeUpdateDesksWidgetBounds();
689   PositionWindows(animate, ignored_items);
690 }
691 
RearrangeDuringDrag(OverviewItem * dragged_item,SplitViewDragIndicators::WindowDraggingState window_dragging_state)692 void OverviewGrid::RearrangeDuringDrag(
693     OverviewItem* dragged_item,
694     SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
695   OverviewItem* drop_target = GetDropTarget();
696 
697   // Update the drop target visibility according to |window_dragging_state|.
698   if (drop_target) {
699     ScopedOverviewAnimationSettings settings(
700         OVERVIEW_ANIMATION_DROP_TARGET_FADE,
701         drop_target_widget_->GetNativeWindow());
702     drop_target->SetOpacity(GetWantedDropTargetOpacity(window_dragging_state));
703   }
704 
705   // Update the grid's bounds.
706   const gfx::Rect wanted_grid_bounds = GetGridBoundsInScreen(
707       root_window_, base::make_optional(window_dragging_state),
708       /*divider_changed=*/false, /*account_for_hotseat=*/true);
709   if (bounds_ != wanted_grid_bounds) {
710     base::flat_set<OverviewItem*> ignored_items;
711     if (dragged_item)
712       ignored_items.insert(dragged_item);
713     SetBoundsAndUpdatePositions(wanted_grid_bounds, ignored_items,
714                                 /*animate=*/true);
715   }
716 }
717 
SetSplitViewDragIndicatorsDraggedWindow(aura::Window * dragged_window)718 void OverviewGrid::SetSplitViewDragIndicatorsDraggedWindow(
719     aura::Window* dragged_window) {
720   DCHECK(split_view_drag_indicators_);
721   split_view_drag_indicators_->SetDraggedWindow(dragged_window);
722 }
723 
SetSplitViewDragIndicatorsWindowDraggingState(SplitViewDragIndicators::WindowDraggingState window_dragging_state)724 void OverviewGrid::SetSplitViewDragIndicatorsWindowDraggingState(
725     SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
726   DCHECK(split_view_drag_indicators_);
727   split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state);
728 }
729 
MaybeUpdateDesksWidgetBounds()730 bool OverviewGrid::MaybeUpdateDesksWidgetBounds() {
731   if (!desks_widget_)
732     return false;
733 
734   const gfx::Rect desks_widget_bounds = GetDesksWidgetBounds();
735   if (desks_widget_bounds != desks_widget_->GetWindowBoundsInScreen()) {
736     // Note that the desks widget window is placed on the active desk container,
737     // which has the kUsesScreenCoordinatesKey property set to true, and hence
738     // we use the screen coordinates when positioning the desks widget.
739     //
740     // On certain display zooms, the requested |desks_widget_bounds| may differ
741     // than the current screen bounds of the desks widget by 1dp, but internally
742     // it will end up being the same and therefore a layout may not be
743     // triggered. This can cause mini views not to show up at all. We must
744     // guarantee that a layout will always occur by invalidating the layout.
745     // See https://crbug.com/1056371 for more details.
746     desks_bar_view_->InvalidateLayout();
747     desks_widget_->SetBounds(desks_widget_bounds);
748     return true;
749   }
750   return false;
751 }
752 
UpdateDropTargetBackgroundVisibility(OverviewItem * dragged_item,const gfx::PointF & location_in_screen)753 void OverviewGrid::UpdateDropTargetBackgroundVisibility(
754     OverviewItem* dragged_item,
755     const gfx::PointF& location_in_screen) {
756   DCHECK(drop_target_widget_);
757   aura::Window* target_window =
758       GetTargetWindowOnLocation(location_in_screen, dragged_item);
759   DropTargetView* drop_target_view =
760       static_cast<DropTargetView*>(drop_target_widget_->GetContentsView());
761   DCHECK(drop_target_view);
762   drop_target_view->UpdateBackgroundVisibility(
763       target_window && IsDropTargetWindow(target_window));
764 }
765 
OnSelectorItemDragStarted(OverviewItem * item)766 void OverviewGrid::OnSelectorItemDragStarted(OverviewItem* item) {
767   CommitDeskNameChanges();
768   for (auto& overview_mode_item : window_list_)
769     overview_mode_item->OnSelectorItemDragStarted(item);
770 }
771 
OnSelectorItemDragEnded(bool snap)772 void OverviewGrid::OnSelectorItemDragEnded(bool snap) {
773   for (auto& overview_mode_item : window_list_)
774     overview_mode_item->OnSelectorItemDragEnded(snap);
775 }
776 
OnWindowDragStarted(aura::Window * dragged_window,bool animate)777 void OverviewGrid::OnWindowDragStarted(aura::Window* dragged_window,
778                                        bool animate) {
779   dragged_window_ = dragged_window;
780   AddDropTargetNotForDraggingFromThisGrid(dragged_window, animate);
781   // Stack the |dragged_window| at top during drag.
782   dragged_window->parent()->StackChildAtTop(dragged_window);
783   // Called to set caption and title visibility during dragging.
784   OnSelectorItemDragStarted(/*item=*/nullptr);
785 }
786 
OnWindowDragContinued(aura::Window * dragged_window,const gfx::PointF & location_in_screen,SplitViewDragIndicators::WindowDraggingState window_dragging_state)787 void OverviewGrid::OnWindowDragContinued(
788     aura::Window* dragged_window,
789     const gfx::PointF& location_in_screen,
790     SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
791   DCHECK_EQ(dragged_window_, dragged_window);
792   DCHECK_EQ(dragged_window->GetRootWindow(), root_window_);
793 
794   RearrangeDuringDrag(nullptr, window_dragging_state);
795   UpdateDropTargetBackgroundVisibility(nullptr, location_in_screen);
796 
797   aura::Window* target_window =
798       GetTargetWindowOnLocation(location_in_screen, /*ignored_item=*/nullptr);
799 
800   if (SplitViewDragIndicators::GetSnapPosition(window_dragging_state) !=
801       SplitViewController::NONE) {
802     // If the dragged window is currently dragged into preview window area,
803     // hide the highlight.
804     overview_session_->highlight_controller()->HideTabDragHighlight();
805 
806     // Also clear ash::kIsDeferredTabDraggingTargetWindowKey key on the target
807     // overview item so that it can't merge into this overview item if the
808     // dragged window is currently in preview window area.
809     if (target_window && !IsDropTargetWindow(target_window))
810       target_window->ClearProperty(kIsDeferredTabDraggingTargetWindowKey);
811 
812     return;
813   }
814 
815   // Show the tab drag highlight if |location_in_screen| is contained by the
816   // browser windows' overview item in overview.
817   if (target_window &&
818       target_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)) {
819     auto* item = GetOverviewItemContaining(target_window);
820     if (!item)
821       return;
822 
823     overview_session_->highlight_controller()->ShowTabDragHighlight(
824         item->overview_item_view());
825     return;
826   }
827 
828   overview_session_->highlight_controller()->HideTabDragHighlight();
829 }
830 
OnWindowDragEnded(aura::Window * dragged_window,const gfx::PointF & location_in_screen,bool should_drop_window_into_overview,bool snap)831 void OverviewGrid::OnWindowDragEnded(aura::Window* dragged_window,
832                                      const gfx::PointF& location_in_screen,
833                                      bool should_drop_window_into_overview,
834                                      bool snap) {
835   DCHECK_EQ(dragged_window_, dragged_window);
836   DCHECK_EQ(dragged_window->GetRootWindow(), root_window_);
837   DCHECK(drop_target_widget_.get());
838   dragged_window_ = nullptr;
839 
840   // Add the dragged window into drop target in overview if
841   // |should_drop_window_into_overview| is true. Only consider add the dragged
842   // window into drop target if SelectedWindow is false since drop target will
843   // not be selected and tab dragging might drag a tab window to merge it into a
844   // browser window in overview.
845   if (overview_session_->highlight_controller()->IsTabDragHighlightVisible())
846     overview_session_->highlight_controller()->HideTabDragHighlight();
847   else if (should_drop_window_into_overview)
848     AddDraggedWindowIntoOverviewOnDragEnd(dragged_window);
849 
850   RemoveDropTarget();
851 
852   // Called to reset caption and title visibility after dragging.
853   OnSelectorItemDragEnded(snap);
854 
855   // After drag ends, if the dragged window needs to merge into another window
856   // |target_window|, and we may need to update |minimized_widget_| that holds
857   // the contents of |target_window| if |target_window| is a minimized window
858   // in overview.
859   aura::Window* target_window =
860       GetTargetWindowOnLocation(location_in_screen, /*ignored_item=*/nullptr);
861   if (target_window &&
862       target_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)) {
863     // Create an window observer and update the minimized window widget after
864     // the dragged window merges into |target_window|.
865     if (!target_window_observer_)
866       target_window_observer_ = std::make_unique<TargetWindowObserver>();
867     target_window_observer_->StartObserving(target_window);
868   }
869 
870   // Update the grid bounds and reposition windows. Since the grid bounds might
871   // be updated based on the preview area during drag, but the window finally
872   // didn't be snapped to the preview area.
873   SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
874                               /*ignored_items=*/{},
875                               /*animate=*/true);
876 }
877 
SetVisibleDuringWindowDragging(bool visible,bool animate)878 void OverviewGrid::SetVisibleDuringWindowDragging(bool visible, bool animate) {
879   for (const auto& window_item : window_list_)
880     window_item->SetVisibleDuringWindowDragging(visible, animate);
881 
882   // Update |desks_widget_|.
883   if (desks_widget_) {
884     ui::Layer* layer = desks_widget_->GetNativeWindow()->layer();
885     float new_opacity = visible ? 1.f : 0.f;
886     if (layer->GetTargetOpacity() == new_opacity)
887       return;
888 
889     if (animate) {
890       ScopedOverviewAnimationSettings settings(
891           OVERVIEW_ANIMATION_OPACITY_ON_WINDOW_DRAG,
892           desks_widget_->GetNativeWindow());
893       layer->SetOpacity(new_opacity);
894     } else {
895       layer->SetOpacity(new_opacity);
896     }
897   }
898 }
899 
IsDropTargetWindow(aura::Window * window) const900 bool OverviewGrid::IsDropTargetWindow(aura::Window* window) const {
901   return drop_target_widget_ &&
902          drop_target_widget_->GetNativeWindow() == window;
903 }
904 
GetDropTarget()905 OverviewItem* OverviewGrid::GetDropTarget() {
906   return drop_target_widget_
907              ? GetOverviewItemContaining(drop_target_widget_->GetNativeWindow())
908              : nullptr;
909 }
910 
OnDisplayMetricsChanged()911 void OverviewGrid::OnDisplayMetricsChanged() {
912   if (split_view_drag_indicators_)
913     split_view_drag_indicators_->OnDisplayBoundsChanged();
914 
915   UpdateCannotSnapWarningVisibility();
916   // In case of split view mode, the grid bounds and item positions will be
917   // updated in |OnSplitViewDividerPositionChanged|.
918   if (SplitViewController::Get(root_window_)->InSplitViewMode())
919     return;
920   SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
921                               /*ignored_items=*/{}, /*animate=*/false);
922 }
923 
OnSplitViewStateChanged(SplitViewController::State previous_state,SplitViewController::State state)924 void OverviewGrid::OnSplitViewStateChanged(
925     SplitViewController::State previous_state,
926     SplitViewController::State state) {
927   // Do nothing if overview is being shutdown.
928   OverviewController* overview_controller = Shell::Get()->overview_controller();
929   if (!overview_controller->InOverviewSession())
930     return;
931 
932   SplitViewController* split_view_controller =
933       SplitViewController::Get(root_window_);
934   const bool unsnappable_window_activated =
935       state == SplitViewController::State::kNoSnap &&
936       split_view_controller->end_reason() ==
937           SplitViewController::EndReason::kUnsnappableWindowActivated;
938 
939   // Restore focus unless either a window was just snapped (and activated) or
940   // split view mode was ended by activating an unsnappable window.
941   if (state != SplitViewController::State::kNoSnap ||
942       unsnappable_window_activated) {
943     overview_session_->ResetFocusRestoreWindow(false);
944   }
945 
946   // If two windows were snapped to both sides of the screen or an unsnappable
947   // window was just activated, or we're in single split mode in clamshell mode
948   // and there is no window in overview, end overview mode and bail out.
949   if (state == SplitViewController::State::kBothSnapped ||
950       unsnappable_window_activated ||
951       (split_view_controller->InClamshellSplitViewMode() &&
952        overview_session_->IsEmpty())) {
953     overview_controller->EndOverview();
954     return;
955   }
956 
957   // Update the cannot snap warnings and adjust the grid bounds.
958   UpdateCannotSnapWarningVisibility();
959   SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
960                               /*ignored_items=*/{}, /*animate=*/false);
961 
962   // If split view mode was ended, then activate the overview focus window, to
963   // match the behavior of entering overview mode in the beginning.
964   if (state == SplitViewController::State::kNoSnap)
965     wm::ActivateWindow(overview_session_->GetOverviewFocusWindow());
966 }
967 
OnSplitViewDividerPositionChanged()968 void OverviewGrid::OnSplitViewDividerPositionChanged() {
969   SetBoundsAndUpdatePositions(
970       GetGridBoundsInScreen(root_window_,
971                             /*window_dragging_state=*/base::nullopt,
972                             /*divider_changed=*/true,
973                             /*account_for_hotseat=*/true),
974       /*ignored_items=*/{}, /*animate=*/false);
975 }
976 
OnScreenCopiedBeforeRotation()977 void OverviewGrid::OnScreenCopiedBeforeRotation() {
978   Shell::Get()->overview_controller()->PauseOcclusionTracker();
979 
980   for (auto& window : window_list()) {
981     window->set_disable_mask(true);
982     window->UpdateRoundedCornersAndShadow();
983     window->StopWidgetAnimation();
984   }
985 }
986 
OnScreenRotationAnimationFinished(ScreenRotationAnimator * animator,bool canceled)987 void OverviewGrid::OnScreenRotationAnimationFinished(
988     ScreenRotationAnimator* animator,
989     bool canceled) {
990   for (auto& window : window_list())
991     window->set_disable_mask(false);
992   Shell::Get()->overview_controller()->DelayedUpdateRoundedCornersAndShadow();
993   Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
994       kOcclusionUnpauseDurationForRotation);
995 }
996 
OnWallpaperChanging()997 void OverviewGrid::OnWallpaperChanging() {
998   grid_event_handler_.reset();
999 }
1000 
OnWallpaperChanged()1001 void OverviewGrid::OnWallpaperChanged() {
1002   grid_event_handler_ = std::make_unique<OverviewGridEventHandler>(this);
1003 }
1004 
OnStartingAnimationComplete(bool canceled)1005 void OverviewGrid::OnStartingAnimationComplete(bool canceled) {
1006   metrics_tracker_.reset();
1007   if (canceled)
1008     return;
1009 
1010   MaybeInitDesksWidget();
1011 
1012   for (auto& window : window_list())
1013     window->OnStartingAnimationComplete();
1014 
1015 }
1016 
CalculateWindowListAnimationStates(OverviewItem * selected_item,OverviewTransition transition,const std::vector<gfx::RectF> & target_bounds)1017 void OverviewGrid::CalculateWindowListAnimationStates(
1018     OverviewItem* selected_item,
1019     OverviewTransition transition,
1020     const std::vector<gfx::RectF>& target_bounds) {
1021   using OverviewTransition = OverviewTransition;
1022 
1023   // Sanity checks to enforce assumptions used in later codes.
1024   switch (transition) {
1025     case OverviewTransition::kEnter:
1026       DCHECK_EQ(target_bounds.size(), window_list_.size());
1027       break;
1028     case OverviewTransition::kExit:
1029       DCHECK(target_bounds.empty());
1030       break;
1031     default:
1032       NOTREACHED();
1033   }
1034 
1035   // Create a copy of |window_list_| which has always on top windows in the
1036   // front.
1037   std::vector<OverviewItem*> items;
1038   std::transform(
1039       window_list_.begin(), window_list_.end(), std::back_inserter(items),
1040       [](const std::unique_ptr<OverviewItem>& item) -> OverviewItem* {
1041         return item.get();
1042       });
1043   // Sort items by:
1044   // 1) Selected items that are always on top windows.
1045   // 2) Other always on top windows.
1046   // 3) Selected items that are not always on top windows.
1047   // 4) Other not always on top windows.
1048   // Preserves ordering if the category is the same.
1049   std::sort(items.begin(), items.end(),
1050             [&selected_item](OverviewItem* a, OverviewItem* b) {
1051               // NB: This treats all non-normal z-ordered windows the same. If
1052               // Aura ever adopts z-order levels, this will need to be changed.
1053               const bool a_on_top =
1054                   a->GetWindow()->GetProperty(aura::client::kZOrderingKey) !=
1055                   ui::ZOrderLevel::kNormal;
1056               const bool b_on_top =
1057                   b->GetWindow()->GetProperty(aura::client::kZOrderingKey) !=
1058                   ui::ZOrderLevel::kNormal;
1059               if (selected_item && a_on_top && b_on_top)
1060                 return a == selected_item;
1061               if (a_on_top)
1062                 return true;
1063               if (b_on_top)
1064                 return false;
1065               if (selected_item)
1066                 return a == selected_item;
1067               return false;
1068             });
1069 
1070   SkRegion occluded_region;
1071   auto* split_view_controller = SplitViewController::Get(root_window_);
1072   if (split_view_controller->InSplitViewMode()) {
1073     // Snapped windows and the split view divider are not included in
1074     // |target_bounds| or |window_list_|, but can occlude other windows, so add
1075     // them manually to |region| here.
1076     SkIRect snapped_window_bounds = gfx::RectToSkIRect(
1077         split_view_controller->GetDefaultSnappedWindow()->GetBoundsInScreen());
1078     occluded_region.op(snapped_window_bounds, SkRegion::kUnion_Op);
1079 
1080     auto* divider = split_view_controller->split_view_divider();
1081     if (divider) {
1082       aura::Window* divider_window =
1083           divider->divider_widget()->GetNativeWindow();
1084       SkIRect divider_bounds =
1085           gfx::RectToSkIRect(divider_window->GetBoundsInScreen());
1086       occluded_region.op(divider_bounds, SkRegion::kUnion_Op);
1087     }
1088   }
1089 
1090   gfx::Rect screen_bounds = GetGridEffectiveBounds();
1091   for (size_t i = 0; i < items.size(); ++i) {
1092     const bool minimized =
1093         WindowState::Get(items[i]->GetWindow())->IsMinimized();
1094     bool src_occluded = minimized;
1095     bool dst_occluded = false;
1096     gfx::Rect src_bounds_temp =
1097         minimized ? gfx::Rect()
1098                   : items[i]->GetWindow()->GetBoundsInRootWindow();
1099     if (!src_bounds_temp.IsEmpty()) {
1100       if (transition == OverviewTransition::kEnter &&
1101           Shell::Get()->tablet_mode_controller()->InTabletMode()) {
1102         BackdropController* backdrop_controller =
1103             GetActiveWorkspaceController(root_window_)
1104                 ->layout_manager()
1105                 ->backdrop_controller();
1106         if (backdrop_controller->GetTopmostWindowWithBackdrop() ==
1107             items[i]->GetWindow()) {
1108           src_bounds_temp = screen_util::GetDisplayWorkAreaBoundsInParent(
1109               items[i]->GetWindow());
1110         }
1111       } else if (transition == OverviewTransition::kExit) {
1112         // On exiting overview, |GetBoundsInRootWindow()| will have the overview
1113         // translation applied to it, so use |bounds()| and
1114         // |ConvertRectToScreen()| to get the true target bounds.
1115         src_bounds_temp = items[i]->GetWindow()->bounds();
1116         ::wm::ConvertRectToScreen(items[i]->root_window(), &src_bounds_temp);
1117       }
1118     }
1119 
1120     // The bounds of of the destination may be partially or fully offscreen.
1121     // Partially offscreen rects should be clipped so the onscreen portion is
1122     // treated normally. Fully offscreen rects (intersection with the screen
1123     // bounds is empty) should never be animated.
1124     gfx::Rect dst_bounds_temp = gfx::ToEnclosedRect(
1125         transition == OverviewTransition::kEnter ? target_bounds[i]
1126                                                  : items[i]->target_bounds());
1127     dst_bounds_temp.Intersect(screen_bounds);
1128     if (dst_bounds_temp.IsEmpty()) {
1129       items[i]->set_should_animate_when_entering(false);
1130       items[i]->set_should_animate_when_exiting(false);
1131       continue;
1132     }
1133 
1134     SkIRect src_bounds = gfx::RectToSkIRect(src_bounds_temp);
1135     SkIRect dst_bounds = gfx::RectToSkIRect(dst_bounds_temp);
1136     if (!occluded_region.isEmpty()) {
1137       src_occluded |=
1138           (!src_bounds.isEmpty() && occluded_region.contains(src_bounds));
1139       dst_occluded |= occluded_region.contains(dst_bounds);
1140     }
1141 
1142     // Add |src_bounds| to our region if it is not empty (minimized window).
1143     if (!src_bounds.isEmpty())
1144       occluded_region.op(src_bounds, SkRegion::kUnion_Op);
1145 
1146     const bool should_animate = !(src_occluded && dst_occluded);
1147     if (transition == OverviewTransition::kEnter)
1148       items[i]->set_should_animate_when_entering(should_animate);
1149     else if (transition == OverviewTransition::kExit)
1150       items[i]->set_should_animate_when_exiting(should_animate);
1151   }
1152 }
1153 
SetWindowListNotAnimatedWhenExiting()1154 void OverviewGrid::SetWindowListNotAnimatedWhenExiting() {
1155   should_animate_when_exiting_ = false;
1156   for (const auto& item : window_list_)
1157     item->set_should_animate_when_exiting(false);
1158 }
1159 
StartNudge(OverviewItem * item)1160 void OverviewGrid::StartNudge(OverviewItem* item) {
1161   // When there is one window left, there is no need to nudge.
1162   if (window_list_.size() <= 1) {
1163     nudge_data_.clear();
1164     return;
1165   }
1166 
1167   // If any of the items are being animated to close, do not nudge any windows
1168   // otherwise we have to deal with potential items getting removed from
1169   // |window_list_| midway through a nudge.
1170   for (const auto& window_item : window_list_) {
1171     if (window_item->animating_to_close()) {
1172       nudge_data_.clear();
1173       return;
1174     }
1175   }
1176 
1177   DCHECK(item);
1178 
1179   // Get the bounds of the windows currently, and the bounds if |item| were to
1180   // be removed.
1181   std::vector<gfx::RectF> src_rects;
1182   for (const auto& window_item : window_list_)
1183     src_rects.push_back(window_item->target_bounds());
1184 
1185   std::vector<gfx::RectF> dst_rects = GetWindowRects({item});
1186 
1187   const size_t index = GetOverviewItemIndex(item);
1188 
1189   // Returns a vector of integers indicating which row the item is in. |index|
1190   // is the index of the element which is going to be deleted and should not
1191   // factor into calculations. The call site should mark |index| as -1 if it
1192   // should not be used. The item at |index| is marked with a 0. The heights of
1193   // items are all set to the same value so a new row is determined if the y
1194   // value has changed from the previous item.
1195   auto get_rows = [](const std::vector<gfx::RectF>& bounds_list, size_t index) {
1196     std::vector<int> row_numbers;
1197     int current_row = 1;
1198     float last_y = 0;
1199     for (size_t i = 0; i < bounds_list.size(); ++i) {
1200       if (i == index) {
1201         row_numbers.push_back(0);
1202         continue;
1203       }
1204 
1205       // Update |current_row| if the y position has changed (heights are all
1206       // equal in overview, so a new y position indicates a new row).
1207       if (last_y != 0 && last_y != bounds_list[i].y())
1208         ++current_row;
1209 
1210       row_numbers.push_back(current_row);
1211       last_y = bounds_list[i].y();
1212     }
1213 
1214     return row_numbers;
1215   };
1216 
1217   std::vector<int> src_rows = get_rows(src_rects, -1);
1218   std::vector<int> dst_rows = get_rows(dst_rects, index);
1219 
1220   // Do nothing if the number of rows change.
1221   if (dst_rows.back() != 0 && src_rows.back() != dst_rows.back())
1222     return;
1223   size_t second_last_index = src_rows.size() - 2;
1224   if (dst_rows.back() == 0 &&
1225       src_rows[second_last_index] != dst_rows[second_last_index]) {
1226     return;
1227   }
1228 
1229   // Do nothing if the last item from the previous row will drop onto the
1230   // current row, this will cause the items in the current row to shift to the
1231   // right while the previous item stays in the previous row, which looks weird.
1232   if (src_rows[index] > 1) {
1233     // Find the last item from the previous row.
1234     size_t previous_row_last_index = index;
1235     while (src_rows[previous_row_last_index] == src_rows[index]) {
1236       --previous_row_last_index;
1237     }
1238 
1239     // Early return if the last item in the previous row changes rows.
1240     if (src_rows[previous_row_last_index] != dst_rows[previous_row_last_index])
1241       return;
1242   }
1243 
1244   // Helper to check whether the item at |item_index| will be nudged.
1245   auto should_nudge = [&src_rows, &dst_rows, &index](size_t item_index) {
1246     // Out of bounds.
1247     if (item_index >= src_rows.size())
1248       return false;
1249 
1250     // Nudging happens when the item stays on the same row and is also on the
1251     // same row as the item to be deleted was.
1252     if (dst_rows[item_index] == src_rows[index] &&
1253         dst_rows[item_index] == src_rows[item_index]) {
1254       return true;
1255     }
1256 
1257     return false;
1258   };
1259 
1260   // Starting from |index| go up and down while the nudge condition returns
1261   // true.
1262   std::vector<int> affected_indexes;
1263   size_t loop_index;
1264 
1265   if (index > 0) {
1266     loop_index = index - 1;
1267     while (should_nudge(loop_index)) {
1268       affected_indexes.push_back(loop_index);
1269       --loop_index;
1270     }
1271   }
1272 
1273   loop_index = index + 1;
1274   while (should_nudge(loop_index)) {
1275     affected_indexes.push_back(loop_index);
1276     ++loop_index;
1277   }
1278 
1279   // Populate |nudge_data_| with the indexes in |affected_indexes| and their
1280   // respective source and destination bounds.
1281   nudge_data_.resize(affected_indexes.size());
1282   for (size_t i = 0; i < affected_indexes.size(); ++i) {
1283     NudgeData data;
1284     data.index = affected_indexes[i];
1285     data.src = src_rects[data.index];
1286     data.dst = dst_rects[data.index];
1287     nudge_data_[i] = data;
1288   }
1289 }
1290 
UpdateNudge(OverviewItem * item,double value)1291 void OverviewGrid::UpdateNudge(OverviewItem* item, double value) {
1292   for (const auto& data : nudge_data_) {
1293     DCHECK_LT(data.index, window_list_.size());
1294 
1295     OverviewItem* nudged_item = window_list_[data.index].get();
1296     double nudge_param = value * value / 30.0;
1297     nudge_param = base::ClampToRange(nudge_param, 0.0, 1.0);
1298     gfx::RectF bounds =
1299         gfx::Tween::RectFValueBetween(nudge_param, data.src, data.dst);
1300     nudged_item->SetBounds(bounds, OVERVIEW_ANIMATION_NONE);
1301   }
1302 }
1303 
EndNudge()1304 void OverviewGrid::EndNudge() {
1305   nudge_data_.clear();
1306 }
1307 
GetTargetWindowOnLocation(const gfx::PointF & location_in_screen,OverviewItem * ignored_item)1308 aura::Window* OverviewGrid::GetTargetWindowOnLocation(
1309     const gfx::PointF& location_in_screen,
1310     OverviewItem* ignored_item) {
1311   for (std::unique_ptr<OverviewItem>& item : window_list_) {
1312     if (item.get() == ignored_item)
1313       continue;
1314     if (item->target_bounds().Contains(location_in_screen))
1315       return item->GetWindow();
1316   }
1317   return nullptr;
1318 }
1319 
IsDesksBarViewActive() const1320 bool OverviewGrid::IsDesksBarViewActive() const {
1321   DCHECK(desks_util::ShouldDesksBarBeCreated());
1322 
1323   // The desk bar view is not active if there is only a single desk when
1324   // overview is started. Once there are more than one desk, it should stay
1325   // active even if the 2nd to last desk is deleted.
1326   return DesksController::Get()->desks().size() > 1 ||
1327          (desks_bar_view_ && !desks_bar_view_->mini_views().empty());
1328 }
1329 
GetGridEffectiveBounds() const1330 gfx::Rect OverviewGrid::GetGridEffectiveBounds() const {
1331   if (!desks_util::ShouldDesksBarBeCreated() || !IsDesksBarViewActive())
1332     return bounds_;
1333 
1334   gfx::Rect effective_bounds = bounds_;
1335   effective_bounds.Inset(0,
1336                          DesksBarView::GetBarHeightForWidth(
1337                              root_window_, desks_bar_view_, bounds_.width()),
1338                          0, 0);
1339   return effective_bounds;
1340 }
1341 
IntersectsWithDesksBar(const gfx::Point & screen_location,bool update_desks_bar_drag_details,bool for_drop)1342 bool OverviewGrid::IntersectsWithDesksBar(const gfx::Point& screen_location,
1343                                           bool update_desks_bar_drag_details,
1344                                           bool for_drop) {
1345   DCHECK(desks_util::ShouldDesksBarBeCreated());
1346 
1347   const bool dragged_item_over_bar =
1348       desks_widget_->GetWindowBoundsInScreen().Contains(screen_location);
1349   if (update_desks_bar_drag_details) {
1350     desks_bar_view_->SetDragDetails(screen_location,
1351                                     !for_drop && dragged_item_over_bar);
1352   }
1353   return dragged_item_over_bar;
1354 }
1355 
MaybeDropItemOnDeskMiniView(const gfx::Point & screen_location,OverviewItem * drag_item)1356 bool OverviewGrid::MaybeDropItemOnDeskMiniView(
1357     const gfx::Point& screen_location,
1358     OverviewItem* drag_item) {
1359   DCHECK(desks_util::ShouldDesksBarBeCreated());
1360 
1361   // End the drag for the DesksBarView.
1362   if (!IntersectsWithDesksBar(screen_location,
1363                               /*update_desks_bar_drag_details=*/true,
1364                               /*for_drop=*/true)) {
1365     return false;
1366   }
1367 
1368   auto* desks_controller = DesksController::Get();
1369   for (auto* mini_view : desks_bar_view_->mini_views()) {
1370     if (!mini_view->IsPointOnMiniView(screen_location))
1371       continue;
1372 
1373     aura::Window* const dragged_window = drag_item->GetWindow();
1374     Desk* const target_desk = mini_view->desk();
1375     if (target_desk == desks_controller->active_desk())
1376       return false;
1377 
1378     return desks_controller->MoveWindowFromActiveDeskTo(
1379         dragged_window, target_desk, root_window_,
1380         DesksMoveWindowFromActiveDeskSource::kDragAndDrop);
1381   }
1382 
1383   return false;
1384 }
1385 
StartScroll()1386 void OverviewGrid::StartScroll() {
1387   Shell::Get()->overview_controller()->PauseOcclusionTracker();
1388 
1389   // Users are not allowed to scroll past the leftmost or rightmost bounds of
1390   // the items on screen in the grid. |scroll_offset_min_| is the amount needed
1391   // to fit the rightmost window into |total_bounds|. The max is zero which is
1392   // default because windows are aligned to the left from the beginning.
1393   gfx::Rect total_bounds = GetGridEffectiveBounds();
1394   total_bounds.Inset(GetGridInsets(total_bounds));
1395 
1396   float rightmost_window_right = 0;
1397   items_scrolling_bounds_.resize(window_list_.size());
1398   for (size_t i = 0; i < items_scrolling_bounds_.size(); ++i) {
1399     const gfx::RectF bounds = window_list_[i]->target_bounds();
1400     if (rightmost_window_right < bounds.right())
1401       rightmost_window_right = bounds.right();
1402 
1403     items_scrolling_bounds_[i] = bounds;
1404   }
1405 
1406   // |rightmost_window_right| may have been modified by an earlier scroll.
1407   // |scroll_offset_| is added to adjust for that.
1408   rightmost_window_right -= scroll_offset_;
1409   scroll_offset_min_ = total_bounds.right() - rightmost_window_right;
1410 
1411   presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
1412       const_cast<ui::Compositor*>(root_window()->layer()->GetCompositor()),
1413       kOverviewScrollHistogram, kOverviewScrollMaxLatencyHistogram);
1414 }
1415 
UpdateScrollOffset(float delta)1416 bool OverviewGrid::UpdateScrollOffset(float delta) {
1417   float new_scroll_offset = scroll_offset_;
1418   new_scroll_offset += delta;
1419   new_scroll_offset =
1420       base::ClampToRange(new_scroll_offset, scroll_offset_min_, 0.f);
1421 
1422   // For flings, we want to return false if we hit one of the edges, which is
1423   // when |new_scroll_offset| is exactly 0.f or |scroll_offset_min_|.
1424   const bool in_range =
1425       new_scroll_offset < 0.f && new_scroll_offset > scroll_offset_min_;
1426   if (new_scroll_offset == scroll_offset_)
1427     return in_range;
1428 
1429   // Update the bounds of the items which are currently visible on screen.
1430   DCHECK_EQ(items_scrolling_bounds_.size(), window_list_.size());
1431   for (size_t i = 0; i < items_scrolling_bounds_.size(); ++i) {
1432     const gfx::RectF previous_bounds = items_scrolling_bounds_[i];
1433     items_scrolling_bounds_[i].Offset(new_scroll_offset - scroll_offset_, 0.f);
1434     const gfx::RectF new_bounds = items_scrolling_bounds_[i];
1435     if (gfx::RectF(GetGridEffectiveBounds()).Intersects(new_bounds) ||
1436         gfx::RectF(GetGridEffectiveBounds()).Intersects(previous_bounds)) {
1437       window_list_[i]->SetBounds(new_bounds, OVERVIEW_ANIMATION_NONE);
1438     }
1439   }
1440 
1441   scroll_offset_ = new_scroll_offset;
1442 
1443   DCHECK(presentation_time_recorder_);
1444   presentation_time_recorder_->RequestNext();
1445   return in_range;
1446 }
1447 
EndScroll()1448 void OverviewGrid::EndScroll() {
1449   Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
1450       kOcclusionUnpauseDurationForScroll);
1451   items_scrolling_bounds_.clear();
1452   presentation_time_recorder_.reset();
1453 
1454   if (!overview_session_->is_shutting_down())
1455     PositionWindows(/*animate=*/false);
1456 }
1457 
CalculateWidthAndMaybeSetUnclippedBounds(OverviewItem * item,int height)1458 int OverviewGrid::CalculateWidthAndMaybeSetUnclippedBounds(OverviewItem* item,
1459                                                            int height) {
1460   const gfx::Size item_size(0, height);
1461   gfx::SizeF target_size = item->GetTargetBoundsInScreen().size();
1462   float scale = item->GetItemScale(item_size);
1463   OverviewGridWindowFillMode grid_fill_mode = item->GetWindowDimensionsType();
1464 
1465   // The drop target, unlike the other windows has its bounds set directly, so
1466   // |GetTargetBoundsInScreen()| won't return the value we want. Instead, get
1467   // the scale from the window it was meant to be a placeholder for.
1468   if (IsDropTargetWindow(item->GetWindow())) {
1469     aura::Window* dragged_window = nullptr;
1470     OverviewItem* grid_dragged_item =
1471         overview_session_->window_drag_controller()
1472             ? overview_session_->window_drag_controller()->item()
1473             : nullptr;
1474     if (grid_dragged_item)
1475       dragged_window = grid_dragged_item->GetWindow();
1476     else if (dragged_window_)
1477       dragged_window = dragged_window_;
1478     if (dragged_window && dragged_window->parent()) {
1479       const gfx::Size work_area_size =
1480           screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
1481               root_window_)
1482               .size();
1483       if (WindowState::Get(dragged_window)->IsMaximized()) {
1484         grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
1485             work_area_size);
1486         target_size = gfx::SizeF(work_area_size);
1487       } else {
1488         gfx::Size dragged_window_size = dragged_window->bounds().size();
1489         // If the drag started from a different root window, |dragged_window|
1490         // may not fit into the work area of |root_window_|. Then if
1491         // |dragged_window| is dropped into this grid, |dragged_window| will
1492         // shrink to fit into this work area. The drop target shall reflect
1493         // that.
1494         dragged_window_size.SetToMin(work_area_size);
1495         grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
1496             dragged_window_size);
1497         target_size = ::ash::GetTargetBoundsInScreen(dragged_window).size();
1498         target_size.SetToMin(gfx::SizeF(work_area_size));
1499       }
1500       const gfx::SizeF inset_size(0, height);
1501       scale = ScopedOverviewTransformWindow::GetItemScale(
1502           target_size, inset_size,
1503           dragged_window->GetProperty(aura::client::kTopViewInset),
1504           kHeaderHeightDp);
1505     }
1506   }
1507 
1508   int width = std::max(1, base::ClampFloor(target_size.width() * scale));
1509   switch (grid_fill_mode) {
1510     case OverviewGridWindowFillMode::kLetterBoxed:
1511       width = kExtremeWindowRatioThreshold * height;
1512       break;
1513     case OverviewGridWindowFillMode::kPillarBoxed:
1514       width = height / kExtremeWindowRatioThreshold;
1515       break;
1516     default:
1517       break;
1518   }
1519 
1520   // Get the bounds of the window if there is a snapped window or a window
1521   // about to be snapped.
1522   base::Optional<gfx::RectF> split_view_bounds =
1523       GetSplitviewBoundsMaintainingAspectRatio();
1524   if (!split_view_bounds) {
1525     item->set_unclipped_size(base::nullopt);
1526     return width;
1527   }
1528 
1529   // Perform horizontal clipping if the window's aspect ratio is wider than the
1530   // split view bounds aspect ratio, and vertical clipping otherwise.
1531   const float aspect_ratio =
1532       target_size.width() /
1533       (target_size.height() -
1534        item->GetWindow()->GetProperty(aura::client::kTopViewInset));
1535   const float target_aspect_ratio =
1536       split_view_bounds->width() / split_view_bounds->height();
1537   const bool clip_horizontally = aspect_ratio > target_aspect_ratio;
1538   const int window_height = height - kHeaderHeightDp;
1539   gfx::Size unclipped_size;
1540   if (clip_horizontally) {
1541     unclipped_size.set_width(width);
1542     unclipped_size.set_height(height);
1543     // For horizontal clipping, shrink |width| so that the aspect ratio matches
1544     // that of |split_view_bounds|.
1545     width = std::max(1, base::ClampFloor(target_aspect_ratio * window_height));
1546   } else {
1547     // For vertical clipping, we want |height| to stay the same, so calculate
1548     // what the unclipped height would be based on |split_view_bounds|.
1549 
1550     // Find the width so that it matches height and matches the aspect ratio of
1551     // |split_view_bounds|.
1552     width = split_view_bounds->width() * window_height /
1553             split_view_bounds->height();
1554     // The unclipped height is the height which matches |width| but keeps the
1555     // aspect ratio of |target_bounds|. Clipping takes the overview header into
1556     // account, so add that back in.
1557     const int unclipped_height =
1558         width * target_size.height() / target_size.width();
1559     unclipped_size.set_width(width);
1560     unclipped_size.set_height(unclipped_height + kHeaderHeightDp);
1561   }
1562 
1563   DCHECK(!unclipped_size.IsEmpty());
1564   item->set_unclipped_size(base::make_optional(unclipped_size));
1565   return width;
1566 }
1567 
OnDesksChanged()1568 void OverviewGrid::OnDesksChanged() {
1569   if (MaybeUpdateDesksWidgetBounds())
1570     PositionWindows(/*animate=*/false, /*ignored_items=*/{});
1571   else
1572     desks_bar_view_->Layout();
1573 }
1574 
IsDeskNameBeingModified() const1575 bool OverviewGrid::IsDeskNameBeingModified() const {
1576   return desks_bar_view_ && desks_bar_view_->IsDeskNameBeingModified();
1577 }
1578 
CommitDeskNameChanges()1579 void OverviewGrid::CommitDeskNameChanges() {
1580   // The desks bar widget may not be ready, since it is created asynchronously
1581   // later when the entering overview animations finish.
1582   if (desks_widget_)
1583     DeskNameView::CommitChanges(desks_widget_.get());
1584 }
1585 
MaybeInitDesksWidget()1586 void OverviewGrid::MaybeInitDesksWidget() {
1587   if (!desks_util::ShouldDesksBarBeCreated() || desks_widget_)
1588     return;
1589 
1590   desks_widget_ =
1591       DesksBarView::CreateDesksWidget(root_window_, GetDesksWidgetBounds());
1592 
1593   // The following order of function calls is significant: SetContentsView()
1594   // must be called before DesksBarView:: Init(). This is needed because the
1595   // desks mini views need to access the widget to get the root window in order
1596   // to know how to layout themselves.
1597   desks_bar_view_ =
1598       desks_widget_->SetContentsView(std::make_unique<DesksBarView>(this));
1599   desks_bar_view_->Init();
1600 
1601   desks_widget_->Show();
1602 
1603   // TODO(afakhry): Check if we need to keep this as the bottom-most window in
1604   // the container.
1605   auto* window = desks_widget_->GetNativeWindow();
1606   window->parent()->StackChildAtBottom(window);
1607 }
1608 
GetWindowRects(const base::flat_set<OverviewItem * > & ignored_items)1609 std::vector<gfx::RectF> OverviewGrid::GetWindowRects(
1610     const base::flat_set<OverviewItem*>& ignored_items) {
1611   gfx::Rect total_bounds = GetGridEffectiveBounds();
1612 
1613   // Windows occupy vertically centered area with additional vertical insets.
1614   total_bounds.Inset(GetGridInsets(total_bounds));
1615   std::vector<gfx::RectF> rects;
1616 
1617   // Keep track of the lowest coordinate.
1618   int max_bottom = total_bounds.y();
1619 
1620   // Right bound of the narrowest row.
1621   int min_right = total_bounds.right();
1622   // Right bound of the widest row.
1623   int max_right = total_bounds.x();
1624 
1625   // Keep track of the difference between the narrowest and the widest row.
1626   // Initially this is set to the worst it can ever be assuming the windows fit.
1627   int width_diff = total_bounds.width();
1628 
1629   // Initially allow the windows to occupy all available width. Shrink this
1630   // available space horizontally to find the breakdown into rows that achieves
1631   // the minimal |width_diff|.
1632   int right_bound = total_bounds.right();
1633 
1634   // Determine the optimal height bisecting between |low_height| and
1635   // |high_height|. Once this optimal height is known, |height_fixed| is set to
1636   // true and the rows are balanced by repeatedly squeezing the widest row to
1637   // cause windows to overflow to the subsequent rows.
1638   int low_height = 2 * kWindowMargin;
1639   int high_height = std::max(low_height, total_bounds.height() + 1);
1640   int height = 0.5 * (low_height + high_height);
1641   bool height_fixed = false;
1642 
1643   // Repeatedly try to fit the windows |rects| within |right_bound|.
1644   // If a maximum |height| is found such that all window |rects| fit, this
1645   // fitting continues while shrinking the |right_bound| in order to balance the
1646   // rows. If the windows fit the |right_bound| would have been decremented at
1647   // least once so it needs to be incremented once before getting out of this
1648   // loop and one additional pass made to actually fit the |rects|.
1649   // If the |rects| cannot fit (e.g. there are too many windows) the bisection
1650   // will still finish and we might increment the |right_bound| once pixel extra
1651   // which is acceptable since there is an unused margin on the right.
1652   bool make_last_adjustment = false;
1653   while (true) {
1654     gfx::Rect overview_mode_bounds(total_bounds);
1655     overview_mode_bounds.set_width(right_bound - total_bounds.x());
1656     bool windows_fit = FitWindowRectsInBounds(
1657         overview_mode_bounds, std::min(kMaxHeight, height), ignored_items,
1658         &rects, &max_bottom, &min_right, &max_right);
1659 
1660     if (height_fixed) {
1661       if (!windows_fit) {
1662         // Revert the previous change to |right_bound| and do one last pass.
1663         right_bound++;
1664         make_last_adjustment = true;
1665         break;
1666       }
1667       // Break if all the windows are zero-width at the current scale.
1668       if (max_right <= total_bounds.x())
1669         break;
1670     } else {
1671       // Find the optimal row height bisecting between |low_height| and
1672       // |high_height|.
1673       if (windows_fit)
1674         low_height = height;
1675       else
1676         high_height = height;
1677       height = 0.5 * (low_height + high_height);
1678       // When height can no longer be improved, start balancing the rows.
1679       if (height == low_height)
1680         height_fixed = true;
1681     }
1682 
1683     if (windows_fit && height_fixed) {
1684       if (max_right - min_right <= width_diff) {
1685         // Row alignment is getting better. Try to shrink the |right_bound| in
1686         // order to squeeze the widest row.
1687         right_bound = max_right - 1;
1688         width_diff = max_right - min_right;
1689       } else {
1690         // Row alignment is getting worse.
1691         // Revert the previous change to |right_bound| and do one last pass.
1692         right_bound++;
1693         make_last_adjustment = true;
1694         break;
1695       }
1696     }
1697   }
1698   // Once the windows in |window_list_| no longer fit, the change to
1699   // |right_bound| was reverted. Perform one last pass to position the |rects|.
1700   if (make_last_adjustment) {
1701     gfx::Rect overview_mode_bounds(total_bounds);
1702     overview_mode_bounds.set_width(right_bound - total_bounds.x());
1703     FitWindowRectsInBounds(overview_mode_bounds, std::min(kMaxHeight, height),
1704                            ignored_items, &rects, &max_bottom, &min_right,
1705                            &max_right);
1706   }
1707 
1708   gfx::Vector2dF offset(0, (total_bounds.bottom() - max_bottom) / 2.f);
1709   for (size_t i = 0; i < rects.size(); ++i)
1710     rects[i] += offset;
1711   return rects;
1712 }
1713 
GetWindowRectsForTabletModeLayout(const base::flat_set<OverviewItem * > & ignored_items)1714 std::vector<gfx::RectF> OverviewGrid::GetWindowRectsForTabletModeLayout(
1715     const base::flat_set<OverviewItem*>& ignored_items) {
1716   gfx::Rect total_bounds = GetGridEffectiveBounds();
1717   // Windows occupy vertically centered area with additional vertical insets.
1718   total_bounds.Inset(GetGridInsets(total_bounds));
1719 
1720   // |scroll_offset_min_| may be changed on positioning (either by closing
1721   // windows or display changes). Recalculate it and clamp |scroll_offset_|, so
1722   // that the items are always aligned left or right.
1723   float rightmost_window_right = 0;
1724   for (const auto& item : window_list_) {
1725     if (ShouldExcludeItemFromGridLayout(item.get(), ignored_items))
1726       continue;
1727     rightmost_window_right =
1728         std::max(rightmost_window_right, item->target_bounds().right());
1729   }
1730 
1731   // |rightmost_window_right| may have been modified by an earlier scroll.
1732   // |scroll_offset_| is added to adjust for that.
1733   rightmost_window_right -= scroll_offset_;
1734   scroll_offset_min_ = total_bounds.right() - rightmost_window_right;
1735   scroll_offset_ = base::ClampToRange(scroll_offset_, scroll_offset_min_, 0.f);
1736 
1737   // Map which contains up to |kTabletLayoutRow| entries with information on the
1738   // last items right bound per row. Used so we can place the next item directly
1739   // next to the last item. The key is the y-value of the row, and the value is
1740   // the rightmost x-value.
1741   base::flat_map<float, float> right_edge_map;
1742 
1743   // Since the number of rows is limited, windows are laid out column-wise so
1744   // that the most recently used windows are displayed first. When the dragged
1745   // item becomes an |ignored_item|, move the other windows accordingly.
1746   // |window_position| matches the positions of the windows' indexes from
1747   // |window_list_|. However, if a window turns out to be an ignored item,
1748   // |window_position| remains where the item was as to then reposition the
1749   // other window's bounds in place of that item.
1750   const int height = total_bounds.height() / kTabletLayoutRow;
1751   int window_position = 0;
1752   std::vector<gfx::RectF> rects;
1753   for (size_t i = 0; i < window_list_.size(); ++i) {
1754     OverviewItem* item = window_list_[i].get();
1755     if (ShouldExcludeItemFromGridLayout(item, ignored_items)) {
1756       rects.push_back(gfx::RectF());
1757       continue;
1758     }
1759 
1760     // Calculate the width and y position of the item.
1761     const int width =
1762         CalculateWidthAndMaybeSetUnclippedBounds(window_list_[i].get(), height);
1763     const int y =
1764         height * (window_position % kTabletLayoutRow) + total_bounds.y();
1765 
1766     // Use the right bounds of the item next to in the row as the x position, if
1767     // that item exists.
1768     const int x = right_edge_map.contains(y)
1769                       ? right_edge_map[y]
1770                       : total_bounds.x() + scroll_offset_;
1771     right_edge_map[y] = x + width;
1772     DCHECK_LE(int{right_edge_map.size()}, kTabletLayoutRow);
1773 
1774     const gfx::RectF bounds(x, y, width, height);
1775     rects.push_back(bounds);
1776     ++window_position;
1777   }
1778 
1779   return rects;
1780 }
1781 
FitWindowRectsInBounds(const gfx::Rect & bounds,int height,const base::flat_set<OverviewItem * > & ignored_items,std::vector<gfx::RectF> * out_rects,int * out_max_bottom,int * out_min_right,int * out_max_right)1782 bool OverviewGrid::FitWindowRectsInBounds(
1783     const gfx::Rect& bounds,
1784     int height,
1785     const base::flat_set<OverviewItem*>& ignored_items,
1786     std::vector<gfx::RectF>* out_rects,
1787     int* out_max_bottom,
1788     int* out_min_right,
1789     int* out_max_right) {
1790   const size_t window_count = window_list_.size();
1791   out_rects->resize(window_count);
1792 
1793   // Start in the top-left corner of |bounds|.
1794   int left = bounds.x();
1795   int top = bounds.y();
1796 
1797   // Keep track of the lowest coordinate.
1798   *out_max_bottom = bounds.y();
1799 
1800   // Right bound of the narrowest row.
1801   *out_min_right = bounds.right();
1802   // Right bound of the widest row.
1803   *out_max_right = bounds.x();
1804 
1805   // All elements are of same height and only the height is necessary to
1806   // determine each item's scale.
1807   for (size_t i = 0u; i < window_count; ++i) {
1808     if (ShouldExcludeItemFromGridLayout(window_list_[i].get(), ignored_items))
1809       continue;
1810 
1811     int width = CalculateWidthAndMaybeSetUnclippedBounds(window_list_[i].get(),
1812                                                          height) +
1813                 2 * kWindowMargin;
1814     int height_with_margin = height + 2 * kWindowMargin;
1815 
1816     if (left + width > bounds.right()) {
1817       // Move to the next row if possible.
1818       if (*out_min_right > left)
1819         *out_min_right = left;
1820       if (*out_max_right < left)
1821         *out_max_right = left;
1822       top += height_with_margin;
1823 
1824       // Check if the new row reaches the bottom or if the first item in the new
1825       // row does not fit within the available width.
1826       if (top + height_with_margin > bounds.bottom() ||
1827           bounds.x() + width > bounds.right()) {
1828         return false;
1829       }
1830       left = bounds.x();
1831     }
1832 
1833     // Position the current rect.
1834     (*out_rects)[i] = gfx::RectF(left, top, width, height_with_margin);
1835 
1836     // Increment horizontal position using sanitized positive |width|.
1837     left += width;
1838 
1839     *out_max_bottom = top + height_with_margin;
1840   }
1841 
1842   // Update the narrowest and widest row width for the last row.
1843   if (*out_min_right > left)
1844     *out_min_right = left;
1845   if (*out_max_right < left)
1846     *out_max_right = left;
1847 
1848   return true;
1849 }
1850 
GetOverviewItemIndex(OverviewItem * item) const1851 size_t OverviewGrid::GetOverviewItemIndex(OverviewItem* item) const {
1852   auto iter = std::find_if(window_list_.begin(), window_list_.end(),
1853                            base::MatchesUniquePtr(item));
1854   DCHECK(iter != window_list_.end());
1855   return iter - window_list_.begin();
1856 }
1857 
FindInsertionIndex(const aura::Window * window)1858 size_t OverviewGrid::FindInsertionIndex(const aura::Window* window) {
1859   size_t index = 0u;
1860   for (aura::Window* mru_window :
1861        Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk)) {
1862     if (index == size() ||
1863         IsDropTargetWindow(window_list_[index]->GetWindow()) ||
1864         mru_window == window) {
1865       return index;
1866     }
1867     // As we iterate over the whole MRU window list, the windows in this grid
1868     // will be encountered in the same order, but possibly with other windows in
1869     // between. Ignore those other windows, and only increment |index| when we
1870     // reach the next window in this grid.
1871     if (mru_window == window_list_[index]->GetWindow())
1872       ++index;
1873   }
1874   NOTREACHED();
1875   return 0u;
1876 }
1877 
AddDraggedWindowIntoOverviewOnDragEnd(aura::Window * dragged_window)1878 void OverviewGrid::AddDraggedWindowIntoOverviewOnDragEnd(
1879     aura::Window* dragged_window) {
1880   DCHECK(overview_session_);
1881   if (overview_session_->IsWindowInOverview(dragged_window))
1882     return;
1883 
1884   // Update the dragged window's bounds before adding it to overview. The
1885   // dragged window might have resized to a smaller size if the drag
1886   // happens on tab(s).
1887   if (window_util::IsDraggingTabs(dragged_window)) {
1888     const gfx::Rect old_bounds = dragged_window->bounds();
1889     // We need to temporarily disable the dragged window's ability to merge
1890     // into another window when changing the dragged window's bounds, so
1891     // that the dragged window doesn't merge into another window because of
1892     // its changed bounds.
1893     dragged_window->SetProperty(kCanAttachToAnotherWindowKey, false);
1894     TabletModeWindowState::UpdateWindowPosition(
1895         WindowState::Get(dragged_window), /*animate=*/false);
1896     const gfx::Rect new_bounds = dragged_window->bounds();
1897     if (old_bounds != new_bounds) {
1898       // It's for smoother animation.
1899       const gfx::Transform transform = gfx::TransformBetweenRects(
1900           gfx::RectF(new_bounds), gfx::RectF(old_bounds));
1901       dragged_window->SetTransform(transform);
1902     }
1903     dragged_window->ClearProperty(kCanAttachToAnotherWindowKey);
1904   }
1905 
1906   overview_session_->AddItemInMruOrder(dragged_window, /*reposition=*/false,
1907                                        /*animate=*/false, /*restack=*/true);
1908 }
1909 
GetDesksWidgetBounds() const1910 gfx::Rect OverviewGrid::GetDesksWidgetBounds() const {
1911   gfx::Rect desks_widget_screen_bounds = bounds_;
1912   desks_widget_screen_bounds.set_height(DesksBarView::GetBarHeightForWidth(
1913       root_window_, desks_bar_view_, desks_widget_screen_bounds.width()));
1914   // Shift the widget down to make room for the splitview indicator guidance
1915   // when it's shown at the top of the screen and no other windows are snapped.
1916   if (split_view_drag_indicators_ &&
1917       split_view_drag_indicators_->current_window_dragging_state() ==
1918           SplitViewDragIndicators::WindowDraggingState::kFromOverview &&
1919       !SplitViewController::IsLayoutHorizontal() &&
1920       !SplitViewController::Get(root_window_)->InSplitViewMode()) {
1921     desks_widget_screen_bounds.Offset(
1922         0, split_view_drag_indicators_->GetLeftHighlightViewBounds().height() +
1923                2 * kHighlightScreenEdgePaddingDp);
1924   }
1925 
1926   return screen_util::SnapBoundsToDisplayEdge(desks_widget_screen_bounds,
1927                                               root_window_);
1928 }
1929 
UpdateCannotSnapWarningVisibility()1930 void OverviewGrid::UpdateCannotSnapWarningVisibility() {
1931   for (auto& overview_mode_item : window_list_)
1932     overview_mode_item->UpdateCannotSnapWarningVisibility();
1933 }
1934 
UpdateFrameThrottling()1935 void OverviewGrid::UpdateFrameThrottling() {
1936   std::vector<aura::Window*> windows_to_throttle(window_list_.size(), nullptr);
1937   std::transform(
1938       window_list_.begin(), window_list_.end(), windows_to_throttle.begin(),
1939       [](std::unique_ptr<OverviewItem>& item) { return item->GetWindow(); });
1940   Shell::Get()->frame_throttling_controller()->StartThrottling(
1941       windows_to_throttle);
1942 }
1943 }  // namespace ash
1944