1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/wm/overview/overview_session.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10 
11 #include "ash/accessibility/accessibility_controller_impl.h"
12 #include "ash/app_list/app_list_controller_impl.h"
13 #include "ash/metrics/user_metrics_recorder.h"
14 #include "ash/public/cpp/ash_features.h"
15 #include "ash/public/cpp/shell_window_ids.h"
16 #include "ash/public/cpp/window_properties.h"
17 #include "ash/root_window_settings.h"
18 #include "ash/scoped_animation_disabler.h"
19 #include "ash/screen_util.h"
20 #include "ash/shell.h"
21 #include "ash/strings/grit/ash_strings.h"
22 #include "ash/wm/desks/desk.h"
23 #include "ash/wm/desks/desks_controller.h"
24 #include "ash/wm/desks/desks_util.h"
25 #include "ash/wm/mru_window_tracker.h"
26 #include "ash/wm/overview/overview_controller.h"
27 #include "ash/wm/overview/overview_delegate.h"
28 #include "ash/wm/overview/overview_grid.h"
29 #include "ash/wm/overview/overview_highlight_controller.h"
30 #include "ash/wm/overview/overview_item.h"
31 #include "ash/wm/overview/overview_utils.h"
32 #include "ash/wm/overview/overview_window_drag_controller.h"
33 #include "ash/wm/overview/rounded_label_widget.h"
34 #include "ash/wm/overview/scoped_overview_animation_settings.h"
35 #include "ash/wm/splitview/split_view_controller.h"
36 #include "ash/wm/splitview/split_view_utils.h"
37 #include "ash/wm/window_state.h"
38 #include "ash/wm/window_util.h"
39 #include "base/auto_reset.h"
40 #include "base/metrics/histogram_macros.h"
41 #include "base/metrics/user_metrics.h"
42 #include "base/stl_util.h"
43 #include "base/threading/thread_task_runner_handle.h"
44 #include "ui/accessibility/ax_enums.mojom.h"
45 #include "ui/base/hit_test.h"
46 #include "ui/base/l10n/l10n_util.h"
47 #include "ui/compositor/layer.h"
48 #include "ui/compositor/scoped_layer_animation_settings.h"
49 #include "ui/display/screen.h"
50 #include "ui/events/event.h"
51 #include "ui/views/accessibility/view_accessibility.h"
52 #include "ui/views/widget/widget.h"
53 #include "ui/wm/core/coordinate_conversion.h"
54 
55 namespace ash {
56 
57 namespace {
58 
59 // Values for the no items indicator which appears when opening overview mode
60 // with no opened windows.
61 constexpr int kNoItemsIndicatorHeightDp = 32;
62 constexpr int kNoItemsIndicatorHorizontalPaddingDp = 16;
63 constexpr int kNoItemsIndicatorRoundingDp = 16;
64 constexpr int kNoItemsIndicatorVerticalPaddingDp = 8;
65 constexpr SkColor kNoItemsIndicatorBackgroundColor =
66     SkColorSetA(SK_ColorBLACK, 204);
67 constexpr SkColor kNoItemsIndicatorTextColor = SK_ColorWHITE;
68 
69 // Values for scrolling the grid by using the keyboard.
70 // TODO(sammiequon): See if we can use the same values used for web scrolling.
71 constexpr int kKeyboardPressScrollingDp = 75;
72 constexpr int kKeyboardHoldScrollingDp = 15;
73 
74 // Tries to end overview. Returns true if overview is successfully ended, or
75 // just was not active in the first place.
EndOverview()76 bool EndOverview() {
77   return Shell::Get()->overview_controller()->EndOverview();
78 }
79 
80 // A self-deleting window state observer that runs the given callback when its
81 // associated window state has been changed.
82 class AsyncWindowStateChangeObserver : public WindowStateObserver,
83                                        public aura::WindowObserver {
84  public:
AsyncWindowStateChangeObserver(aura::Window * window,base::OnceCallback<void (WindowState *)> on_post_window_state_changed)85   AsyncWindowStateChangeObserver(
86       aura::Window* window,
87       base::OnceCallback<void(WindowState*)> on_post_window_state_changed)
88       : window_(window),
89         on_post_window_state_changed_(std::move(on_post_window_state_changed)) {
90     DCHECK(!on_post_window_state_changed_.is_null());
91     WindowState::Get(window_)->AddObserver(this);
92     window_->AddObserver(this);
93   }
94 
~AsyncWindowStateChangeObserver()95   ~AsyncWindowStateChangeObserver() override { RemoveAllObservers(); }
96 
97   AsyncWindowStateChangeObserver(const AsyncWindowStateChangeObserver&) =
98       delete;
99   AsyncWindowStateChangeObserver& operator=(
100       const AsyncWindowStateChangeObserver&) = delete;
101 
102   // aura::WindowObserver:
OnWindowDestroying(aura::Window * window)103   void OnWindowDestroying(aura::Window* window) override { delete this; }
104 
105   // WindowStateObserver:
OnPostWindowStateTypeChange(WindowState * window_state,chromeos::WindowStateType)106   void OnPostWindowStateTypeChange(WindowState* window_state,
107                                    chromeos::WindowStateType) override {
108     RemoveAllObservers();
109     std::move(on_post_window_state_changed_).Run(window_state);
110     delete this;
111   }
112 
113  private:
RemoveAllObservers()114   void RemoveAllObservers() {
115     WindowState::Get(window_)->RemoveObserver(this);
116     window_->RemoveObserver(this);
117   }
118 
119   aura::Window* window_;
120 
121   base::OnceCallback<void(WindowState*)> on_post_window_state_changed_;
122 };
123 
124 // Simple override of views::Button. Allows to use a element of accessibility
125 // role button as the overview focus widget's contents.
126 class OverviewFocusButton : public views::Button {
127  public:
OverviewFocusButton()128   OverviewFocusButton() : views::Button(views::Button::PressedCallback()) {
129     // Make this not focusable to avoid accessibility error since this view has
130     // no accessible name.
131     SetFocusBehavior(FocusBehavior::NEVER);
132   }
133   OverviewFocusButton(const OverviewFocusButton&) = delete;
134   OverviewFocusButton& operator=(const OverviewFocusButton&) = delete;
135   ~OverviewFocusButton() override = default;
136 };
137 
138 }  // namespace
139 
140 // Class that updates the focusable overview widgets so that the point to the
141 // correct next and previous widgets for a11y purposes. Needs to be updated when
142 // an overview item is added or removed. It is expected that the desk widget
143 // does not get altered for the duration of overview.
144 class OverviewSession::AccessibilityFocusAnnotator {
145  public:
AccessibilityFocusAnnotator(OverviewSession * session)146   explicit AccessibilityFocusAnnotator(OverviewSession* session)
147       : session_(session) {}
148   AccessibilityFocusAnnotator(const AccessibilityFocusAnnotator&) = delete;
149   AccessibilityFocusAnnotator& operator=(const AccessibilityFocusAnnotator&) =
150       delete;
151   ~AccessibilityFocusAnnotator() = default;
152 
UpdateAccessibilityFocus()153   void UpdateAccessibilityFocus() {
154     if (session_->is_shutting_down())
155       return;
156 
157     // Construct the list of accessible widgets, these are the overview focus
158     // widget, desk bar widget, all the item widgets and the no window indicator
159     // widget, if available.
160     std::vector<views::Widget*> a11y_widgets;
161     if (session_->overview_focus_widget_)
162       a11y_widgets.push_back(session_->overview_focus_widget_.get());
163 
164     for (aura::Window* root : Shell::GetAllRootWindows()) {
165       OverviewGrid* grid = session_->GetGridWithRootWindow(root);
166       DCHECK(grid);
167       if (grid->desks_widget())
168         a11y_widgets.push_back(
169             const_cast<views::Widget*>(grid->desks_widget()));
170       for (const auto& item : grid->window_list())
171         a11y_widgets.push_back(item->item_widget());
172     }
173     if (session_->no_windows_widget_.get())
174       a11y_widgets.push_back(session_->no_windows_widget_.get());
175 
176     if (a11y_widgets.empty())
177       return;
178 
179     auto get_view_a11y =
180         [&a11y_widgets](int index) -> views::ViewAccessibility& {
181       return a11y_widgets[index]->GetContentsView()->GetViewAccessibility();
182     };
183 
184     // If there is only one widget left, clear the focus overrides so that they
185     // do not point to deleted objects.
186     if (a11y_widgets.size() == 1) {
187       get_view_a11y(/*index=*/0).OverridePreviousFocus(nullptr);
188       get_view_a11y(/*index=*/0).OverrideNextFocus(nullptr);
189       a11y_widgets[0]->GetContentsView()->NotifyAccessibilityEvent(
190           ax::mojom::Event::kTreeChanged, true);
191       return;
192     }
193 
194     int size = a11y_widgets.size();
195     for (int i = 0; i < size; ++i) {
196       int previous_index = (i + size - 1) % size;
197       int next_index = (i + 1) % size;
198       get_view_a11y(i).OverridePreviousFocus(a11y_widgets[previous_index]);
199       get_view_a11y(i).OverrideNextFocus(a11y_widgets[next_index]);
200       a11y_widgets[i]->GetContentsView()->NotifyAccessibilityEvent(
201           ax::mojom::Event::kTreeChanged, true);
202     }
203   }
204 
205  private:
206   // The associated overview session. Guaranteed to be non null for the lifetime
207   // of |this|.
208   OverviewSession* session_;
209 };
210 
OverviewSession(OverviewDelegate * delegate)211 OverviewSession::OverviewSession(OverviewDelegate* delegate)
212     : delegate_(delegate),
213       restore_focus_window_(window_util::GetFocusedWindow()),
214       overview_start_time_(base::Time::Now()),
215       highlight_controller_(
216           std::make_unique<OverviewHighlightController>(this)) {
217   DCHECK(delegate_);
218   Shell::Get()->AddPreTargetHandler(this);
219 }
220 
~OverviewSession()221 OverviewSession::~OverviewSession() {
222   // Don't delete |window_drag_controller_| yet since the stack might be still
223   // using it.
224   if (window_drag_controller_) {
225     window_drag_controller_->ResetOverviewSession();
226     base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
227         FROM_HERE, window_drag_controller_.release());
228   }
229 }
230 
231 // NOTE: The work done in Init() is not done in the constructor because it may
232 // cause other, unrelated classes, to make indirect method calls on a partially
233 // constructed object.
Init(const WindowList & windows,const WindowList & hide_windows)234 void OverviewSession::Init(const WindowList& windows,
235                            const WindowList& hide_windows) {
236   Shell::Get()->AddShellObserver(this);
237 
238   hide_overview_windows_ = std::make_unique<ScopedOverviewHideWindows>(
239       std::move(hide_windows), /*force_hidden=*/false);
240   if (restore_focus_window_)
241     restore_focus_window_->AddObserver(this);
242 
243   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
244   std::sort(root_windows.begin(), root_windows.end(),
245             [](const aura::Window* a, const aura::Window* b) {
246               // Since we don't know if windows are vertically or horizontally
247               // oriented we use both x and y position. This may be confusing
248               // if you have 3 or more monitors which are not strictly
249               // horizontal or vertical but that case is not yet supported.
250               return (a->GetBoundsInScreen().x() + a->GetBoundsInScreen().y()) <
251                      (b->GetBoundsInScreen().x() + b->GetBoundsInScreen().y());
252             });
253 
254   for (auto* root : root_windows) {
255     auto grid = std::make_unique<OverviewGrid>(root, windows, this);
256     num_items_ += grid->size();
257     grid_list_.push_back(std::move(grid));
258   }
259 
260   // The calls to OverviewGrid::PrepareForOverview() requires some
261   // LayoutManagers to perform layouts so that windows are correctly visible and
262   // properly animated in overview mode. Otherwise these layouts should be
263   // suppressed during overview mode so they don't conflict with overview mode
264   // animations.
265 
266   // Do not call PrepareForOverview until all items are added to window_list_
267   // as we don't want to cause any window updates until all windows in
268   // overview are observed. See http://crbug.com/384495.
269   for (std::unique_ptr<OverviewGrid>& overview_grid : grid_list_) {
270     overview_grid->PrepareForOverview();
271 
272     // Do not animate if there is any window that is being dragged in the
273     // grid.
274     if (enter_exit_overview_type_ == OverviewEnterExitType::kImmediateEnter) {
275       overview_grid->PositionWindows(/*animate=*/false);
276     } else {
277       // Exit only types should not appear here:
278       DCHECK_NE(enter_exit_overview_type_, OverviewEnterExitType::kFadeOutExit);
279 
280       overview_grid->PositionWindows(/*animate=*/true, /*ignored_items=*/{},
281                                      OverviewTransition::kEnter);
282     }
283   }
284 
285   UpdateNoWindowsWidget();
286 
287   // Create the widget that will receive focus while in overview mode for
288   // accessibility purposes. Add a button as the contents so that
289   // |accessibility_focus_annotator_| can put it on the accessibility focus
290   // cycler.
291   overview_focus_widget_ = std::make_unique<views::Widget>();
292   views::Widget::InitParams params;
293   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
294   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
295   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
296   params.accept_events = false;
297   params.bounds = gfx::Rect(0, 0, 2, 2);
298   params.layer_type = ui::LAYER_NOT_DRAWN;
299   params.name = "OverviewModeFocusWidget";
300   params.z_order = ui::ZOrderLevel::kFloatingWindow;
301   params.init_properties_container.SetProperty(ash::kExcludeInMruKey, true);
302   overview_focus_widget_->Init(std::move(params));
303   overview_focus_widget_->SetContentsView(
304       std::make_unique<OverviewFocusButton>());
305 
306   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_);
307 
308   SplitViewController::Get(Shell::GetPrimaryRootWindow())->AddObserver(this);
309 
310   display::Screen::GetScreen()->AddObserver(this);
311   base::RecordAction(base::UserMetricsAction("WindowSelector_Overview"));
312   // Send an a11y alert.
313   Shell::Get()->accessibility_controller()->TriggerAccessibilityAlert(
314       AccessibilityAlert::WINDOW_OVERVIEW_MODE_ENTERED);
315 
316   ignore_activations_ = false;
317 }
318 
319 // NOTE: The work done in Shutdown() is not done in the destructor because it
320 // may cause other, unrelated classes, to make indirect calls to
321 // restoring_minimized_windows() on a partially destructed object.
Shutdown()322 void OverviewSession::Shutdown() {
323   // This should have been set already when the process of ending overview mode
324   // began. See OverviewController::OnSelectionEnded().
325   DCHECK(is_shutting_down_);
326 
327   Shell::Get()->RemovePreTargetHandler(this);
328   Shell::Get()->RemoveShellObserver(this);
329 
330   // Stop observing screen metrics changes first to avoid auto-positioning
331   // windows in response to work area changes from window activation.
332   display::Screen::GetScreen()->RemoveObserver(this);
333 
334   // Stop observing split view state changes before restoring window focus.
335   // Otherwise the activation of the window triggers OnSplitViewStateChanged()
336   // that will call into this function again.
337   SplitViewController::Get(Shell::GetPrimaryRootWindow())->RemoveObserver(this);
338 
339   size_t remaining_items = 0;
340   for (std::unique_ptr<OverviewGrid>& overview_grid : grid_list_) {
341     // During shutdown, do not animate all windows in overview if we need to
342     // animate the snapped window.
343     if (overview_grid->should_animate_when_exiting() &&
344         enter_exit_overview_type_ != OverviewEnterExitType::kImmediateExit) {
345       overview_grid->CalculateWindowListAnimationStates(
346           selected_item_ &&
347                   selected_item_->overview_grid() == overview_grid.get()
348               ? selected_item_
349               : nullptr,
350           OverviewTransition::kExit, /*target_bounds=*/{});
351     }
352     for (const auto& overview_item : overview_grid->window_list())
353       overview_item->RestoreWindow(/*reset_transform=*/true);
354     remaining_items += overview_grid->size();
355   }
356 
357   // Setting focus after restoring windows' state avoids unnecessary animations.
358   // No need to restore if we are sliding to the home launcher screen, as all
359   // windows will be minimized.
360   const bool should_focus =
361       enter_exit_overview_type_ == OverviewEnterExitType::kNormal ||
362       enter_exit_overview_type_ == OverviewEnterExitType::kImmediateExit;
363   ResetFocusRestoreWindow(should_focus);
364   RemoveAllObservers();
365 
366   for (std::unique_ptr<OverviewGrid>& overview_grid : grid_list_)
367     overview_grid->Shutdown();
368 
369   DCHECK(num_items_ >= remaining_items);
370   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.OverviewClosedItems",
371                            num_items_ - remaining_items);
372   UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview",
373                              base::Time::Now() - overview_start_time_);
374 
375   grid_list_.clear();
376 
377   // Hide the focus widget on overview session end to prevent it from retaining
378   // focus and handling key press events now that overview session is not
379   // consuming them.
380   if (overview_focus_widget_)
381     overview_focus_widget_->Hide();
382 
383   if (no_windows_widget_) {
384     if (enter_exit_overview_type_ == OverviewEnterExitType::kImmediateExit) {
385       ImmediatelyCloseWidgetOnExit(std::move(no_windows_widget_));
386       return;
387     }
388 
389     // Fade out the no windows widget. This animation continues past the
390     // lifetime of |this|.
391     FadeOutWidgetFromOverview(std::move(no_windows_widget_),
392                               OVERVIEW_ANIMATION_RESTORE_WINDOW);
393   }
394 }
395 
OnGridEmpty()396 void OverviewSession::OnGridEmpty() {
397   if (!IsEmpty())
398     return;
399 
400   if (SplitViewController::Get(Shell::GetPrimaryRootWindow())
401           ->InTabletSplitViewMode()) {
402     UpdateNoWindowsWidget();
403   } else {
404     EndOverview();
405   }
406 }
407 
IncrementSelection(bool forward)408 void OverviewSession::IncrementSelection(bool forward) {
409   Move(/*reverse=*/!forward);
410 }
411 
AcceptSelection()412 bool OverviewSession::AcceptSelection() {
413   if (!highlight_controller_->GetHighlightedItem())
414     return false;
415   SelectWindow(highlight_controller_->GetHighlightedItem());
416   return true;
417 }
418 
SelectWindow(OverviewItem * item)419 void OverviewSession::SelectWindow(OverviewItem* item) {
420   aura::Window* window = item->GetWindow();
421   aura::Window::Windows window_list =
422       Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
423   if (!window_list.empty()) {
424     // Record WindowSelector_ActiveWindowChanged if the user is selecting a
425     // window other than the window that was active prior to entering overview
426     // mode (i.e., the window at the front of the MRU list).
427     if (window_list[0] != window) {
428       base::RecordAction(
429           base::UserMetricsAction("WindowSelector_ActiveWindowChanged"));
430       Shell::Get()->metrics()->task_switch_metrics_recorder().OnTaskSwitch(
431           TaskSwitchSource::OVERVIEW_MODE);
432     }
433     const auto it = std::find(window_list.begin(), window_list.end(), window);
434     if (it != window_list.end()) {
435       // Record 1-based index so that selecting a top MRU window will record 1.
436       UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.SelectionDepth",
437                                1 + it - window_list.begin());
438     }
439   }
440   item->EnsureVisible();
441   if (window->GetProperty(kPipOriginalWindowKey)) {
442     window_util::ExpandArcPipWindow();
443     return;
444   }
445   // If the selected window is a minimized window, un-minimize it first before
446   // activating it so that the window can use the scale-up animation instead of
447   // un-minimizing animation. The activation of the window will happen in an
448   // asynchronous manner on window state has been changed. That's because some
449   // windows (ARC app windows) have their window states changed async, so we
450   // need to wait until the window is fully unminimized before activation as
451   // opposed to having two consecutive calls.
452   auto* window_state = WindowState::Get(window);
453   if (window_state->IsMinimized()) {
454     ScopedAnimationDisabler disabler(window);
455     // The following instance self-destructs when the window state changed.
456     new AsyncWindowStateChangeObserver(
457         window, base::BindOnce([](WindowState* window_state) {
458           for (auto* window_iter : window_util::GetVisibleTransientTreeIterator(
459                    window_state->window())) {
460             window_iter->layer()->SetOpacity(1.0);
461           }
462           wm::ActivateWindow(window_state->window());
463         }));
464     window->Show();
465     return;
466   }
467 
468   wm::ActivateWindow(window);
469 }
470 
SetSplitViewDragIndicatorsDraggedWindow(aura::Window * dragged_window)471 void OverviewSession::SetSplitViewDragIndicatorsDraggedWindow(
472     aura::Window* dragged_window) {
473   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
474     grid->SetSplitViewDragIndicatorsDraggedWindow(dragged_window);
475 }
476 
UpdateSplitViewDragIndicatorsWindowDraggingStates(const aura::Window * root_window_being_dragged_in,SplitViewDragIndicators::WindowDraggingState state_on_root_window_being_dragged_in)477 void OverviewSession::UpdateSplitViewDragIndicatorsWindowDraggingStates(
478     const aura::Window* root_window_being_dragged_in,
479     SplitViewDragIndicators::WindowDraggingState
480         state_on_root_window_being_dragged_in) {
481   if (state_on_root_window_being_dragged_in ==
482       SplitViewDragIndicators::WindowDraggingState::kNoDrag) {
483     ResetSplitViewDragIndicatorsWindowDraggingStates();
484     return;
485   }
486   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
487     grid->SetSplitViewDragIndicatorsWindowDraggingState(
488         grid->root_window() == root_window_being_dragged_in
489             ? state_on_root_window_being_dragged_in
490             : SplitViewDragIndicators::WindowDraggingState::kOtherDisplay);
491   }
492 }
493 
ResetSplitViewDragIndicatorsWindowDraggingStates()494 void OverviewSession::ResetSplitViewDragIndicatorsWindowDraggingStates() {
495   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
496     grid->SetSplitViewDragIndicatorsWindowDraggingState(
497         SplitViewDragIndicators::WindowDraggingState::kNoDrag);
498   }
499 }
500 
RearrangeDuringDrag(OverviewItem * dragged_item)501 void OverviewSession::RearrangeDuringDrag(OverviewItem* dragged_item) {
502   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
503     DCHECK(grid->split_view_drag_indicators());
504     grid->RearrangeDuringDrag(
505         dragged_item,
506         grid->split_view_drag_indicators()->current_window_dragging_state());
507   }
508 }
509 
UpdateDropTargetsBackgroundVisibilities(OverviewItem * dragged_item,const gfx::PointF & location_in_screen)510 void OverviewSession::UpdateDropTargetsBackgroundVisibilities(
511     OverviewItem* dragged_item,
512     const gfx::PointF& location_in_screen) {
513   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
514     if (grid->GetDropTarget()) {
515       grid->UpdateDropTargetBackgroundVisibility(dragged_item,
516                                                  location_in_screen);
517     }
518   }
519 }
520 
GetGridWithRootWindow(aura::Window * root_window)521 OverviewGrid* OverviewSession::GetGridWithRootWindow(
522     aura::Window* root_window) {
523   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
524     if (grid->root_window() == root_window)
525       return grid.get();
526   }
527 
528   return nullptr;
529 }
530 
AddItem(aura::Window * window,bool reposition,bool animate,const base::flat_set<OverviewItem * > & ignored_items,size_t index)531 void OverviewSession::AddItem(
532     aura::Window* window,
533     bool reposition,
534     bool animate,
535     const base::flat_set<OverviewItem*>& ignored_items,
536     size_t index) {
537   // Early exit if a grid already contains |window|.
538   OverviewGrid* grid = GetGridWithRootWindow(window->GetRootWindow());
539   if (!grid || grid->GetOverviewItemContaining(window))
540     return;
541 
542   grid->AddItem(window, reposition, animate, ignored_items, index,
543                 /*use_spawn_animation=*/false, /*restack=*/false);
544   OnItemAdded(window);
545 }
546 
AppendItem(aura::Window * window,bool reposition,bool animate)547 void OverviewSession::AppendItem(aura::Window* window,
548                                  bool reposition,
549                                  bool animate) {
550   // Early exit if a grid already contains |window|.
551   OverviewGrid* grid = GetGridWithRootWindow(window->GetRootWindow());
552   if (!grid || grid->GetOverviewItemContaining(window))
553     return;
554 
555   grid->AppendItem(window, reposition, animate, /*use_spawn_animation=*/true);
556   OnItemAdded(window);
557 }
558 
AddItemInMruOrder(aura::Window * window,bool reposition,bool animate,bool restack)559 void OverviewSession::AddItemInMruOrder(aura::Window* window,
560                                         bool reposition,
561                                         bool animate,
562                                         bool restack) {
563   // Early exit if a grid already contains |window|.
564   OverviewGrid* grid = GetGridWithRootWindow(window->GetRootWindow());
565   if (!grid || grid->GetOverviewItemContaining(window))
566     return;
567 
568   grid->AddItemInMruOrder(window, reposition, animate, restack);
569   OnItemAdded(window);
570 }
571 
RemoveItem(OverviewItem * overview_item)572 void OverviewSession::RemoveItem(OverviewItem* overview_item) {
573   RemoveItem(overview_item, /*item_destroying=*/false, /*reposition=*/false);
574 }
575 
RemoveItem(OverviewItem * overview_item,bool item_destroying,bool reposition)576 void OverviewSession::RemoveItem(OverviewItem* overview_item,
577                                  bool item_destroying,
578                                  bool reposition) {
579   if (overview_item->GetWindow() == restore_focus_window_) {
580     restore_focus_window_->RemoveObserver(this);
581     restore_focus_window_ = nullptr;
582   }
583 
584   overview_item->overview_grid()->RemoveItem(overview_item, item_destroying,
585                                              reposition);
586   --num_items_;
587 
588   UpdateNoWindowsWidget();
589 
590   if (accessibility_focus_annotator_)
591     accessibility_focus_annotator_->UpdateAccessibilityFocus();
592 }
593 
RemoveDropTargets()594 void OverviewSession::RemoveDropTargets() {
595   for (std::unique_ptr<OverviewGrid>& grid : grid_list_) {
596     if (grid->GetDropTarget())
597       grid->RemoveDropTarget();
598   }
599 }
600 
InitiateDrag(OverviewItem * item,const gfx::PointF & location_in_screen,bool is_touch_dragging)601 void OverviewSession::InitiateDrag(OverviewItem* item,
602                                    const gfx::PointF& location_in_screen,
603                                    bool is_touch_dragging) {
604   if (Shell::Get()->overview_controller()->IsInStartAnimation() ||
605       SplitViewController::Get(Shell::GetPrimaryRootWindow())
606           ->IsDividerAnimating()) {
607     return;
608   }
609   highlight_controller_->SetFocusHighlightVisibility(false);
610   window_drag_controller_ = std::make_unique<OverviewWindowDragController>(
611       this, item, is_touch_dragging);
612   window_drag_controller_->InitiateDrag(location_in_screen);
613 
614   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
615     grid->OnSelectorItemDragStarted(item);
616 }
617 
Drag(OverviewItem * item,const gfx::PointF & location_in_screen)618 void OverviewSession::Drag(OverviewItem* item,
619                            const gfx::PointF& location_in_screen) {
620   DCHECK(window_drag_controller_);
621   DCHECK_EQ(item, window_drag_controller_->item());
622   window_drag_controller_->Drag(location_in_screen);
623 }
624 
CompleteDrag(OverviewItem * item,const gfx::PointF & location_in_screen)625 void OverviewSession::CompleteDrag(OverviewItem* item,
626                                    const gfx::PointF& location_in_screen) {
627   DCHECK(window_drag_controller_);
628   DCHECK_EQ(item, window_drag_controller_->item());
629 
630   // Note: The highlight should be updated first as completing a drag may cause
631   // a selection which would destroy |item|.
632   highlight_controller_->SetFocusHighlightVisibility(true);
633   const bool snap = window_drag_controller_->CompleteDrag(location_in_screen) ==
634                     OverviewWindowDragController::DragResult::kSnap;
635   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
636     grid->OnSelectorItemDragEnded(snap);
637 }
638 
StartNormalDragMode(const gfx::PointF & location_in_screen)639 void OverviewSession::StartNormalDragMode(
640     const gfx::PointF& location_in_screen) {
641   window_drag_controller_->StartNormalDragMode(location_in_screen);
642 }
643 
Fling(OverviewItem * item,const gfx::PointF & location_in_screen,float velocity_x,float velocity_y)644 void OverviewSession::Fling(OverviewItem* item,
645                             const gfx::PointF& location_in_screen,
646                             float velocity_x,
647                             float velocity_y) {
648   // Its possible a fling event is not paired with a tap down event. Ignore
649   // these flings.
650   if (!window_drag_controller_ || item != window_drag_controller_->item())
651     return;
652 
653   const bool snap = window_drag_controller_->Fling(location_in_screen,
654                                                    velocity_x, velocity_y) ==
655                     OverviewWindowDragController::DragResult::kSnap;
656   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
657     grid->OnSelectorItemDragEnded(snap);
658 }
659 
ActivateDraggedWindow()660 void OverviewSession::ActivateDraggedWindow() {
661   window_drag_controller_->ActivateDraggedWindow();
662 }
663 
ResetDraggedWindowGesture()664 void OverviewSession::ResetDraggedWindowGesture() {
665   window_drag_controller_->ResetGesture();
666   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
667     grid->OnSelectorItemDragEnded(/*snap=*/false);
668 }
669 
OnWindowDragStarted(aura::Window * dragged_window,bool animate)670 void OverviewSession::OnWindowDragStarted(aura::Window* dragged_window,
671                                           bool animate) {
672   OverviewGrid* target_grid =
673       GetGridWithRootWindow(dragged_window->GetRootWindow());
674   if (!target_grid)
675     return;
676   target_grid->OnWindowDragStarted(dragged_window, animate);
677 }
678 
OnWindowDragContinued(aura::Window * dragged_window,const gfx::PointF & location_in_screen,SplitViewDragIndicators::WindowDraggingState window_dragging_state)679 void OverviewSession::OnWindowDragContinued(
680     aura::Window* dragged_window,
681     const gfx::PointF& location_in_screen,
682     SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
683   OverviewGrid* target_grid =
684       GetGridWithRootWindow(dragged_window->GetRootWindow());
685   if (!target_grid)
686     return;
687   target_grid->OnWindowDragContinued(dragged_window, location_in_screen,
688                                      window_dragging_state);
689 }
690 
OnWindowDragEnded(aura::Window * dragged_window,const gfx::PointF & location_in_screen,bool should_drop_window_into_overview,bool snap)691 void OverviewSession::OnWindowDragEnded(aura::Window* dragged_window,
692                                         const gfx::PointF& location_in_screen,
693                                         bool should_drop_window_into_overview,
694                                         bool snap) {
695   OverviewGrid* target_grid =
696       GetGridWithRootWindow(dragged_window->GetRootWindow());
697   if (!target_grid)
698     return;
699   target_grid->OnWindowDragEnded(dragged_window, location_in_screen,
700                                  should_drop_window_into_overview, snap);
701 }
702 
SetVisibleDuringWindowDragging(bool visible,bool animate)703 void OverviewSession::SetVisibleDuringWindowDragging(bool visible,
704                                                      bool animate) {
705   for (auto& grid : grid_list_)
706     grid->SetVisibleDuringWindowDragging(visible, animate);
707 }
708 
PositionWindows(bool animate,const base::flat_set<OverviewItem * > & ignored_items)709 void OverviewSession::PositionWindows(
710     bool animate,
711     const base::flat_set<OverviewItem*>& ignored_items) {
712   for (std::unique_ptr<OverviewGrid>& grid : grid_list_)
713     grid->PositionWindows(animate, ignored_items);
714 
715   RefreshNoWindowsWidgetBounds(animate);
716 }
717 
IsWindowInOverview(const aura::Window * window)718 bool OverviewSession::IsWindowInOverview(const aura::Window* window) {
719   for (const std::unique_ptr<OverviewGrid>& grid : grid_list_) {
720     if (grid->GetOverviewItemContaining(window))
721       return true;
722   }
723   return false;
724 }
725 
GetOverviewItemForWindow(const aura::Window * window)726 OverviewItem* OverviewSession::GetOverviewItemForWindow(
727     const aura::Window* window) {
728   for (const std::unique_ptr<OverviewGrid>& grid : grid_list_) {
729     OverviewItem* item = grid->GetOverviewItemContaining(window);
730     if (item)
731       return item;
732   }
733 
734   return nullptr;
735 }
736 
SetWindowListNotAnimatedWhenExiting(aura::Window * root_window)737 void OverviewSession::SetWindowListNotAnimatedWhenExiting(
738     aura::Window* root_window) {
739   // Find the grid accociated with |root_window|.
740   OverviewGrid* grid = GetGridWithRootWindow(root_window);
741   if (grid)
742     grid->SetWindowListNotAnimatedWhenExiting();
743 }
744 
UpdateRoundedCornersAndShadow()745 void OverviewSession::UpdateRoundedCornersAndShadow() {
746   for (auto& grid : grid_list_)
747     for (auto& window : grid->window_list())
748       window->UpdateRoundedCornersAndShadow();
749 }
750 
OnStartingAnimationComplete(bool canceled,bool should_focus_overview)751 void OverviewSession::OnStartingAnimationComplete(bool canceled,
752                                                   bool should_focus_overview) {
753   for (auto& grid : grid_list_)
754     grid->OnStartingAnimationComplete(canceled);
755 
756   if (canceled)
757     return;
758 
759   if (overview_focus_widget_) {
760     if (should_focus_overview) {
761       overview_focus_widget_->Show();
762     } else {
763       overview_focus_widget_->ShowInactive();
764 
765       // Check if the active window is in overview. There is at least one
766       // workflow where it will be: the active window is being dragged, and the
767       // previous window carries over from clamshell mode to tablet split view.
768       if (IsWindowInOverview(window_util::GetActiveWindow()) &&
769           SplitViewController::Get(Shell::GetPrimaryRootWindow())
770               ->InSplitViewMode()) {
771         // We do not want an active window in overview. It will cause blatantly
772         // broken behavior as in the video linked in crbug.com/992223.
773         wm::ActivateWindow(
774             SplitViewController::Get(Shell::GetPrimaryRootWindow())
775                 ->GetDefaultSnappedWindow());
776       }
777     }
778   }
779 
780   accessibility_focus_annotator_ =
781       std::make_unique<AccessibilityFocusAnnotator>(this);
782   accessibility_focus_annotator_->UpdateAccessibilityFocus();
783 
784   Shell::Get()->overview_controller()->DelayedUpdateRoundedCornersAndShadow();
785 }
786 
OnWindowActivating(::wm::ActivationChangeObserver::ActivationReason reason,aura::Window * gained_active,aura::Window * lost_active)787 void OverviewSession::OnWindowActivating(
788     ::wm::ActivationChangeObserver::ActivationReason reason,
789     aura::Window* gained_active,
790     aura::Window* lost_active) {
791   if (ignore_activations_ || gained_active == GetOverviewFocusWindow())
792     return;
793 
794   // Activating the Desks bar should not end overview.
795   if (gained_active && gained_active->id() == kShellWindowId_DesksBarWindow)
796     return;
797 
798   if (DesksController::Get()->AreDesksBeingModified()) {
799     // Activating a desk from its mini view will activate its most-recently used
800     // window, but this should not result in ending overview mode now.
801     // Overview will be ended explicitly as part of the desk activation
802     // animation.
803     return;
804   }
805 
806   if (!gained_active) {
807     // Cancel overview session and do not restore focus when active window is
808     // set to nullptr. This happens when removing a display.
809     ResetFocusRestoreWindow(false);
810     EndOverview();
811     return;
812   }
813 
814   // If app list is open in clamshell mode, end overview. Note: we have special
815   // logic to end overview when app list (i.e., home launcher) is open in tablet
816   // mode, so do not handle it here.
817   if (gained_active == Shell::Get()->app_list_controller()->GetWindow() &&
818       !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
819     ResetFocusRestoreWindow(false);
820     EndOverview();
821     return;
822   }
823 
824   // Do not cancel overview mode if the window activation happens when split
825   // view mode is also active. SplitViewController will do the right thing to
826   // handle the window activation change. Check for split view mode without
827   // using |SplitViewController::state_| which is updated asynchronously when
828   // snapping an ARC window.
829   SplitViewController* split_view_controller =
830       SplitViewController::Get(gained_active);
831   if (split_view_controller->left_window() ||
832       split_view_controller->right_window()) {
833     return;
834   }
835 
836   // Do not cancel overview mode while a window or overview item is being
837   // dragged as evidenced by the presence of a drop target. (Dragging to close
838   // does not count; canceling overview mode is okay then.)
839   for (std::unique_ptr<OverviewGrid>& overview_grid : grid_list_) {
840     if (overview_grid->GetDropTarget())
841       return;
842   }
843 
844   auto* grid = GetGridWithRootWindow(gained_active->GetRootWindow());
845   DCHECK(grid);
846   const auto& windows = grid->window_list();
847   auto iter = std::find_if(
848       windows.begin(), windows.end(),
849       [gained_active](const std::unique_ptr<OverviewItem>& window) {
850         return window->Contains(gained_active);
851       });
852 
853   if (iter != windows.end())
854     selected_item_ = iter->get();
855 
856   // Don't restore focus on exit if a window was just activated.
857   ResetFocusRestoreWindow(false);
858   EndOverview();
859 }
860 
GetOverviewFocusWindow()861 aura::Window* OverviewSession::GetOverviewFocusWindow() {
862   if (overview_focus_widget_)
863     return overview_focus_widget_->GetNativeWindow();
864 
865   return nullptr;
866 }
867 
GetHighlightedWindow()868 aura::Window* OverviewSession::GetHighlightedWindow() {
869   OverviewItem* item = highlight_controller_->GetHighlightedItem();
870   if (!item)
871     return nullptr;
872   return item->GetWindow();
873 }
874 
SuspendReposition()875 void OverviewSession::SuspendReposition() {
876   for (auto& grid : grid_list_)
877     grid->set_suspend_reposition(true);
878 }
879 
ResumeReposition()880 void OverviewSession::ResumeReposition() {
881   for (auto& grid : grid_list_)
882     grid->set_suspend_reposition(false);
883 }
884 
IsEmpty() const885 bool OverviewSession::IsEmpty() const {
886   for (const auto& grid : grid_list_) {
887     if (!grid->empty())
888       return false;
889   }
890   return true;
891 }
892 
ResetFocusRestoreWindow(bool focus)893 void OverviewSession::ResetFocusRestoreWindow(bool focus) {
894   if (!restore_focus_window_)
895     return;
896 
897   // Do not restore focus to a window that exists on an inactive desk.
898   focus &= base::Contains(DesksController::Get()->active_desk()->windows(),
899                           restore_focus_window_);
900 
901   // Ensure the window is still in the window hierarchy and not in the middle
902   // of teardown.
903   if (focus && restore_focus_window_->GetRootWindow()) {
904     base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
905     wm::ActivateWindow(restore_focus_window_);
906   }
907 
908   restore_focus_window_->RemoveObserver(this);
909   restore_focus_window_ = nullptr;
910 }
911 
OnHighlightedItemActivated(OverviewItem * item)912 void OverviewSession::OnHighlightedItemActivated(OverviewItem* item) {
913   UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ArrowKeyPresses",
914                            num_key_presses_);
915   UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.WindowSelector.KeyPressesOverItemsRatio",
916                               (num_key_presses_ * 100) / num_items_, 1, 300,
917                               30);
918   base::RecordAction(
919       base::UserMetricsAction("WindowSelector_OverviewEnterKey"));
920   SelectWindow(item);
921 }
922 
OnHighlightedItemClosed(OverviewItem * item)923 void OverviewSession::OnHighlightedItemClosed(OverviewItem* item) {
924   base::RecordAction(
925       base::UserMetricsAction("WindowSelector_OverviewCloseKey"));
926   item->CloseWindow();
927 }
928 
OnRootWindowClosing(aura::Window * root)929 void OverviewSession::OnRootWindowClosing(aura::Window* root) {
930   auto iter = std::find_if(grid_list_.begin(), grid_list_.end(),
931                            [root](std::unique_ptr<OverviewGrid>& grid) {
932                              return grid->root_window() == root;
933                            });
934   DCHECK(iter != grid_list_.end());
935   (*iter)->Shutdown();
936   grid_list_.erase(iter);
937 }
938 
GetCurrentDraggedOverviewItem() const939 OverviewItem* OverviewSession::GetCurrentDraggedOverviewItem() const {
940   if (!window_drag_controller_)
941     return nullptr;
942   return window_drag_controller_->item();
943 }
944 
CanProcessEvent() const945 bool OverviewSession::CanProcessEvent() const {
946   return CanProcessEvent(/*sender=*/nullptr, /*from_touch_gesture=*/false);
947 }
948 
CanProcessEvent(OverviewItem * sender,bool from_touch_gesture) const949 bool OverviewSession::CanProcessEvent(OverviewItem* sender,
950                                       bool from_touch_gesture) const {
951   // Allow processing the event if no current window is being dragged.
952   const bool drag_in_progress = window_util::IsAnyWindowDragged();
953   if (!drag_in_progress)
954     return true;
955 
956   // At this point, if there is no sender, we can't process the event since
957   // |drag_in_progress| will be true.
958   if (!sender || !window_drag_controller_)
959     return false;
960 
961   // Allow processing the event if the sender is the one currently being
962   // dragged and the event is the same type as the current one.
963   if (sender == window_drag_controller_->item() &&
964       from_touch_gesture == window_drag_controller_->is_touch_dragging()) {
965     return true;
966   }
967 
968   return false;
969 }
970 
OnDisplayAdded(const display::Display & display)971 void OverviewSession::OnDisplayAdded(const display::Display& display) {
972   if (EndOverview())
973     return;
974   SplitViewController::Get(Shell::GetPrimaryRootWindow())->EndSplitView();
975   EndOverview();
976 }
977 
OnDisplayMetricsChanged(const display::Display & display,uint32_t metrics)978 void OverviewSession::OnDisplayMetricsChanged(const display::Display& display,
979                                               uint32_t metrics) {
980   if (window_drag_controller_)
981     ResetDraggedWindowGesture();
982   GetGridWithRootWindow(Shell::GetRootWindowForDisplayId(display.id()))
983       ->OnDisplayMetricsChanged();
984 
985   // The no windows widget is on the primary root window. If |display|
986   // corresponds to another root window, then we are done.
987   if (display.id() !=
988       GetRootWindowSettings(Shell::GetPrimaryRootWindow())->display_id) {
989     return;
990   }
991   // In case of split view mode, the no windows widget bounds will be updated in
992   // |OnSplitViewDividerPositionChanged|.
993   if (SplitViewController::Get(Shell::GetPrimaryRootWindow())
994           ->InSplitViewMode()) {
995     return;
996   }
997   RefreshNoWindowsWidgetBounds(/*animate=*/false);
998 }
999 
OnWindowDestroying(aura::Window * window)1000 void OverviewSession::OnWindowDestroying(aura::Window* window) {
1001   DCHECK_EQ(restore_focus_window_, window);
1002   restore_focus_window_->RemoveObserver(this);
1003   restore_focus_window_ = nullptr;
1004 }
1005 
OnKeyEvent(ui::KeyEvent * event)1006 void OverviewSession::OnKeyEvent(ui::KeyEvent* event) {
1007   // If app list is open when overview is active (it can happen in clamshell
1008   // mode, when we snap an overview window to one side of the screen and then
1009   // open the app list to select an app to snap to the other side), in this case
1010   // we let the app list to handle the key event.
1011   // TODO(crbug.com/952315): Explore better ways to handle this splitview +
1012   // overview + applist case.
1013   Shell* shell = Shell::Get();
1014   if (!shell->tablet_mode_controller()->InTabletMode() &&
1015       shell->app_list_controller()->IsVisible(base::nullopt)) {
1016     return;
1017   }
1018 
1019   // If any desk name is being modified, let the DeskNameView handle the key
1020   // events.
1021   // Note that Tab presses should commit any pending desk name changes.
1022   const bool is_key_press = event->type() == ui::ET_KEY_PRESSED;
1023   const bool should_commit_name_changes =
1024       is_key_press && event->key_code() == ui::VKEY_TAB;
1025   for (auto& grid : grid_list_) {
1026     if (grid->IsDeskNameBeingModified()) {
1027       if (!should_commit_name_changes)
1028         return;
1029 
1030       // Commit and proceed.
1031       grid->CommitDeskNameChanges();
1032       break;
1033     }
1034   }
1035 
1036   // Check if we can scroll with the event first as it can use release events as
1037   // well.
1038   if (ProcessForScrolling(*event)) {
1039     event->SetHandled();
1040     event->StopPropagation();
1041     return;
1042   }
1043 
1044   if (!is_key_press)
1045     return;
1046 
1047   switch (event->key_code()) {
1048     case ui::VKEY_BROWSER_BACK:
1049     case ui::VKEY_ESCAPE:
1050       EndOverview();
1051       break;
1052     case ui::VKEY_UP:
1053       ++num_key_presses_;
1054       Move(/*reverse=*/true);
1055       break;
1056     case ui::VKEY_DOWN:
1057       ++num_key_presses_;
1058       Move(/*reverse=*/false);
1059       break;
1060     case ui::VKEY_RIGHT:
1061       ++num_key_presses_;
1062       Move(/*reverse=*/false);
1063       break;
1064     case ui::VKEY_TAB: {
1065       const bool reverse = event->flags() & ui::EF_SHIFT_DOWN;
1066       ++num_key_presses_;
1067       Move(reverse);
1068       break;
1069     }
1070     case ui::VKEY_LEFT:
1071       ++num_key_presses_;
1072       Move(/*reverse=*/true);
1073       break;
1074     case ui::VKEY_W: {
1075       if (!(event->flags() & ui::EF_CONTROL_DOWN))
1076         return;
1077 
1078       if (!highlight_controller_->MaybeCloseHighlightedView())
1079         return;
1080       break;
1081     }
1082     case ui::VKEY_RETURN: {
1083       if (!highlight_controller_->MaybeActivateHighlightedView())
1084         return;
1085       break;
1086     }
1087     default: {
1088       // Window activation change happens after overview start animation is
1089       // finished for performance reasons. During the animation, the focused
1090       // window prior to entering overview still has focus so stop events from
1091       // reaching it. See https://crbug.com/951324 for more details.
1092       if (shell->overview_controller()->IsInStartAnimation())
1093         break;
1094       return;
1095     }
1096   }
1097 
1098   event->SetHandled();
1099   event->StopPropagation();
1100 }
1101 
OnShellDestroying()1102 void OverviewSession::OnShellDestroying() {
1103   // Cancel selection will call |Shutdown()|, which will remove observer.
1104   EndOverview();
1105 }
1106 
OnShelfAlignmentChanged(aura::Window * root_window,ShelfAlignment old_alignment)1107 void OverviewSession::OnShelfAlignmentChanged(aura::Window* root_window,
1108                                               ShelfAlignment old_alignment) {
1109   // Helper to check if a shelf alignment change results in different
1110   // visuals for overivew purposes.
1111   auto same_effective_alignment = [](ShelfAlignment prev,
1112                                      ShelfAlignment curr) -> bool {
1113     auto bottom = ShelfAlignment::kBottom;
1114     auto locked = ShelfAlignment::kBottomLocked;
1115     return (prev == bottom && curr == locked) ||
1116            (prev == locked && curr == bottom);
1117   };
1118 
1119   // On changing from kBottomLocked to kBottom shelf alignment or vice versa
1120   // (usually from entering/exiting lock screen), keep splitview if it's active.
1121   // Done here instead of using a SessionObserver so we can skip the
1122   // EndOverview() at the end of this function if necessary.
1123   ShelfAlignment current_alignment = Shelf::ForWindow(root_window)->alignment();
1124   if (SplitViewController::Get(root_window)->InSplitViewMode() &&
1125       same_effective_alignment(old_alignment, current_alignment)) {
1126     return;
1127   }
1128 
1129   // When the shelf alignment changes while in overview, the display work area
1130   // doesn't get updated anyways (see https://crbug.com/834400). In this case,
1131   // even updating the grid bounds won't make any difference, so we simply exit
1132   // overview.
1133   EndOverview();
1134 }
1135 
OnSplitViewStateChanged(SplitViewController::State previous_state,SplitViewController::State state)1136 void OverviewSession::OnSplitViewStateChanged(
1137     SplitViewController::State previous_state,
1138     SplitViewController::State state) {
1139   // Do nothing if overview is being shutdown.
1140   if (!Shell::Get()->overview_controller()->InOverviewSession())
1141     return;
1142 
1143   RefreshNoWindowsWidgetBounds(/*animate=*/false);
1144 }
1145 
OnSplitViewDividerPositionChanged()1146 void OverviewSession::OnSplitViewDividerPositionChanged() {
1147   RefreshNoWindowsWidgetBounds(/*animate=*/false);
1148 }
1149 
Move(bool reverse)1150 void OverviewSession::Move(bool reverse) {
1151   // Do not allow moving the highlight while in the middle of a drag.
1152   if (window_util::IsAnyWindowDragged())
1153     return;
1154 
1155   highlight_controller_->MoveHighlight(reverse);
1156 }
1157 
ProcessForScrolling(const ui::KeyEvent & event)1158 bool OverviewSession::ProcessForScrolling(const ui::KeyEvent& event) {
1159   if (!ShouldUseTabletModeGridLayout())
1160     return false;
1161 
1162   // TODO(sammiequon): This only works for tablet mode at the moment, so using
1163   // the primary display works. If this feature is adapted for multi display
1164   // then this needs to be revisited.
1165   auto* grid = GetGridWithRootWindow(Shell::GetPrimaryRootWindow());
1166   const bool press = (event.type() == ui::ET_KEY_PRESSED);
1167 
1168   if (!press) {
1169     if (is_keyboard_scrolling_grid_) {
1170       is_keyboard_scrolling_grid_ = false;
1171       grid->EndScroll();
1172       return true;
1173     }
1174     return false;
1175   }
1176 
1177   // Presses only at this point.
1178   if (event.key_code() != ui::VKEY_LEFT && event.key_code() != ui::VKEY_RIGHT)
1179     return false;
1180 
1181   if (!event.IsControlDown())
1182     return false;
1183 
1184   const bool repeat = event.is_repeat();
1185   const bool reverse = event.key_code() == ui::VKEY_LEFT;
1186   if (!repeat) {
1187     is_keyboard_scrolling_grid_ = true;
1188     grid->StartScroll();
1189     grid->UpdateScrollOffset(kKeyboardPressScrollingDp * (reverse ? 1 : -1));
1190     return true;
1191   }
1192 
1193   grid->UpdateScrollOffset(kKeyboardHoldScrollingDp * (reverse ? 1 : -1));
1194   return true;
1195 }
1196 
RemoveAllObservers()1197 void OverviewSession::RemoveAllObservers() {
1198   display::Screen::GetScreen()->RemoveObserver(this);
1199   if (restore_focus_window_)
1200     restore_focus_window_->RemoveObserver(this);
1201   restore_focus_window_ = nullptr;
1202 }
1203 
UpdateNoWindowsWidget()1204 void OverviewSession::UpdateNoWindowsWidget() {
1205   if (is_shutting_down_)
1206     return;
1207 
1208   // Hide the widget if there is an item in overview.
1209   if (!IsEmpty()) {
1210     no_windows_widget_.reset();
1211     return;
1212   }
1213 
1214   if (!no_windows_widget_) {
1215     // Create and fade in the widget.
1216     RoundedLabelWidget::InitParams params;
1217     params.name = "OverviewNoWindowsLabel";
1218     params.horizontal_padding = kNoItemsIndicatorHorizontalPaddingDp;
1219     params.vertical_padding = kNoItemsIndicatorVerticalPaddingDp;
1220     params.background_color = kNoItemsIndicatorBackgroundColor;
1221     params.foreground_color = kNoItemsIndicatorTextColor;
1222     params.rounding_dp = kNoItemsIndicatorRoundingDp;
1223     params.preferred_height = kNoItemsIndicatorHeightDp;
1224     params.message_id = IDS_ASH_OVERVIEW_NO_RECENT_ITEMS;
1225     params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
1226         desks_util::GetActiveDeskContainerId());
1227     params.hide_in_mini_view = true;
1228     no_windows_widget_ = std::make_unique<RoundedLabelWidget>();
1229     no_windows_widget_->Init(std::move(params));
1230 
1231     aura::Window* widget_window = no_windows_widget_->GetNativeWindow();
1232     widget_window->parent()->StackChildAtBottom(widget_window);
1233     ScopedOverviewAnimationSettings settings(OVERVIEW_ANIMATION_NO_RECENTS_FADE,
1234                                              widget_window);
1235     no_windows_widget_->SetOpacity(1.f);
1236   }
1237 
1238   RefreshNoWindowsWidgetBounds(/*animate=*/false);
1239 }
1240 
RefreshNoWindowsWidgetBounds(bool animate)1241 void OverviewSession::RefreshNoWindowsWidgetBounds(bool animate) {
1242   if (!no_windows_widget_)
1243     return;
1244 
1245   auto* grid = GetGridWithRootWindow(Shell::GetPrimaryRootWindow());
1246   DCHECK(grid);
1247   no_windows_widget_->SetBoundsCenteredIn(grid->GetGridEffectiveBounds(),
1248                                           animate);
1249 }
1250 
OnItemAdded(aura::Window * window)1251 void OverviewSession::OnItemAdded(aura::Window* window) {
1252   ++num_items_;
1253   UpdateNoWindowsWidget();
1254 
1255   OverviewGrid* grid = GetGridWithRootWindow(window->GetRootWindow());
1256   // The drop target window is non-activatable, so no need to transfer focus.
1257   if (grid && grid->IsDropTargetWindow(window))
1258     return;
1259 
1260   // Transfer focus from |window| to |overview_focus_widget_| to match the
1261   // behavior of entering overview mode in the beginning.
1262   DCHECK(overview_focus_widget_);
1263   // |overview_focus_widget_| might not visible yet as OnItemAdded() might be
1264   // called before OnStartingAnimationComplete() is called, so use Show()
1265   // instead of ActivateWindow() to show and activate the widget.
1266   overview_focus_widget_->Show();
1267 
1268   if (accessibility_focus_annotator_)
1269     accessibility_focus_annotator_->UpdateAccessibilityFocus();
1270 }
1271 
1272 }  // namespace ash
1273