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