123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/wm/overview/overview_controller.h"
- #include <algorithm>
- #include <utility>
- #include "ash/frame_throttler/frame_throttling_controller.h"
- #include "ash/keyboard/ui/keyboard_ui_controller.h"
- #include "ash/public/cpp/window_properties.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "ash/wm/desks/desks_controller.h"
- #include "ash/wm/mru_window_tracker.h"
- #include "ash/wm/overview/delayed_animation_observer_impl.h"
- #include "ash/wm/overview/overview_constants.h"
- #include "ash/wm/overview/overview_grid.h"
- #include "ash/wm/overview/overview_item.h"
- #include "ash/wm/overview/overview_session.h"
- #include "ash/wm/overview/overview_utils.h"
- #include "ash/wm/overview/overview_wallpaper_controller.h"
- #include "ash/wm/screen_pinning_controller.h"
- #include "ash/wm/splitview/split_view_controller.h"
- #include "ash/wm/tablet_mode/tablet_mode_controller.h"
- #include "ash/wm/window_state.h"
- #include "ash/wm/window_util.h"
- #include "base/bind.h"
- #include "base/containers/cxx20_erase.h"
- #include "base/containers/unique_ptr_adapters.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/trace_event/trace_event.h"
- #include "ui/views/widget/widget.h"
- #include "ui/wm/core/window_util.h"
- #include "ui/wm/public/activation_client.h"
- namespace ash {
- namespace {
- // It can take up to two frames until the frame created in the UI thread that
- // triggered animation observer is drawn. Wait 50ms in attempt to let its draw
- // and swap finish.
- constexpr base::TimeDelta kOcclusionPauseDurationForStart =
- base::Milliseconds(50);
- // Wait longer when exiting overview mode in case when a user may re-enter
- // overview mode immediately, contents are ready.
- constexpr base::TimeDelta kOcclusionPauseDurationForEnd =
- base::Milliseconds(500);
- bool IsSplitViewDividerDraggedOrAnimated() {
- SplitViewController* split_view_controller =
- SplitViewController::Get(Shell::GetPrimaryRootWindow());
- return split_view_controller->is_resizing() ||
- split_view_controller->IsDividerAnimating();
- }
- // Returns the enter/exit type that should be used if kNormal enter/exit type
- // was originally requested - if the overview is expected to transition to/from
- // the home screen, the normal enter/exit mode is expected to be overridden by
- // either slide, or fade to home modes.
- // |enter| - Whether |original_type| is used for entering overview.
- // |windows| - The list of windows that are displayed in the overview UI.
- OverviewEnterExitType MaybeOverrideEnterExitTypeForHomeScreen(
- OverviewEnterExitType original_type,
- bool enter,
- const std::vector<aura::Window*>& windows) {
- if (original_type != OverviewEnterExitType::kNormal)
- return original_type;
- // Use normal type if home launcher is not available.
- if (!Shell::Get()->tablet_mode_controller()->InTabletMode())
- return original_type;
- // Transition to home screen only if all windows are minimized.
- for (const aura::Window* window : windows) {
- if (!WindowState::Get(window)->IsMinimized()) {
- return original_type;
- }
- }
- // The original type is overridden even if the list of windows is empty so
- // home screen knows to animate in during overview exit animation (home screen
- // controller uses different show/hide animations depending on the overview
- // exit/enter types).
- return enter ? OverviewEnterExitType::kFadeInEnter
- : OverviewEnterExitType::kFadeOutExit;
- }
- } // namespace
- OverviewController::OverviewController()
- : occlusion_pause_duration_for_end_(kOcclusionPauseDurationForEnd),
- overview_wallpaper_controller_(
- std::make_unique<OverviewWallpaperController>()),
- delayed_animation_task_delay_(kTransition) {
- Shell::Get()->activation_client()->AddObserver(this);
- }
- OverviewController::~OverviewController() {
- Shell::Get()->activation_client()->RemoveObserver(this);
- overview_wallpaper_controller_.reset();
- // Destroy widgets that may be still animating if shell shuts down soon after
- // exiting overview mode.
- for (auto& animation_observer : delayed_animations_)
- animation_observer->Shutdown();
- for (auto& animation_observer : start_animations_)
- animation_observer->Shutdown();
- if (overview_session_) {
- overview_session_->Shutdown();
- overview_session_.reset();
- }
- }
- bool OverviewController::StartOverview(OverviewStartAction action,
- OverviewEnterExitType type) {
- // No need to start overview if overview is currently active.
- if (InOverviewSession())
- return true;
- if (!CanEnterOverview())
- return false;
- ToggleOverview(type);
- RecordOverviewStartAction(action);
- return true;
- }
- bool OverviewController::EndOverview(OverviewEndAction action,
- OverviewEnterExitType type) {
- // No need to end overview if overview is already ended.
- if (!InOverviewSession())
- return true;
- if (!CanEndOverview(type))
- return false;
- ToggleOverview(type);
- RecordOverviewEndAction(action);
- // If there is an undo toast active and the toast was created when ChromeVox
- // was enabled, then we need to close the toast when overview closes.
- DesksController::Get()->MaybeDismissPersistentDeskRemovalToast();
- return true;
- }
- bool OverviewController::InOverviewSession() const {
- return overview_session_ && !overview_session_->is_shutting_down();
- }
- void OverviewController::IncrementSelection(bool forward) {
- DCHECK(InOverviewSession());
- overview_session_->IncrementSelection(forward);
- }
- bool OverviewController::AcceptSelection() {
- DCHECK(InOverviewSession());
- return overview_session_->AcceptSelection();
- }
- bool OverviewController::IsInStartAnimation() {
- return !start_animations_.empty();
- }
- bool OverviewController::IsCompletingShutdownAnimations() const {
- return !delayed_animations_.empty();
- }
- void OverviewController::PauseOcclusionTracker() {
- if (occlusion_tracker_pauser_)
- return;
- reset_pauser_task_.Cancel();
- occlusion_tracker_pauser_ =
- std::make_unique<aura::WindowOcclusionTracker::ScopedPause>();
- }
- void OverviewController::UnpauseOcclusionTracker(base::TimeDelta delay) {
- reset_pauser_task_.Reset(base::BindOnce(&OverviewController::ResetPauser,
- weak_ptr_factory_.GetWeakPtr()));
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, reset_pauser_task_.callback(), delay);
- }
- void OverviewController::AddObserver(OverviewObserver* observer) {
- observers_.AddObserver(observer);
- }
- void OverviewController::RemoveObserver(OverviewObserver* observer) {
- observers_.RemoveObserver(observer);
- }
- void OverviewController::DelayedUpdateRoundedCornersAndShadow() {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&OverviewController::UpdateRoundedCornersAndShadow,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void OverviewController::AddExitAnimationObserver(
- std::unique_ptr<DelayedAnimationObserver> animation_observer) {
- // No delayed animations should be created when overview mode is set to exit
- // immediately.
- DCHECK(IsCompletingShutdownAnimations() ||
- overview_session_->enter_exit_overview_type() !=
- OverviewEnterExitType::kImmediateExit);
- animation_observer->SetOwner(this);
- delayed_animations_.push_back(std::move(animation_observer));
- }
- void OverviewController::RemoveAndDestroyExitAnimationObserver(
- DelayedAnimationObserver* animation_observer) {
- const bool previous_empty = delayed_animations_.empty();
- base::EraseIf(delayed_animations_,
- base::MatchesUniquePtr(animation_observer));
- // If something has been removed and its the last observer, unblur the
- // wallpaper and let observers know. This function may be called while still
- // in overview (ie. splitview restores one window but leaves overview active)
- // so check that |overview_session_| is null before notifying.
- if (!overview_session_ && !previous_empty && delayed_animations_.empty())
- OnEndingAnimationComplete(/*canceled=*/false);
- }
- void OverviewController::AddEnterAnimationObserver(
- std::unique_ptr<DelayedAnimationObserver> animation_observer) {
- animation_observer->SetOwner(this);
- start_animations_.push_back(std::move(animation_observer));
- }
- void OverviewController::RemoveAndDestroyEnterAnimationObserver(
- DelayedAnimationObserver* animation_observer) {
- const bool previous_empty = start_animations_.empty();
- base::EraseIf(start_animations_, base::MatchesUniquePtr(animation_observer));
- if (!previous_empty && start_animations_.empty())
- OnStartingAnimationComplete(/*canceled=*/false);
- }
- void OverviewController::OnWindowActivating(ActivationReason reason,
- aura::Window* gained_active,
- aura::Window* lost_active) {
- if (InOverviewSession())
- overview_session_->OnWindowActivating(reason, gained_active, lost_active);
- }
- std::vector<aura::Window*>
- OverviewController::GetWindowsListInOverviewGridsForTest() {
- std::vector<aura::Window*> windows;
- for (const std::unique_ptr<OverviewGrid>& grid :
- overview_session_->grid_list()) {
- for (const auto& overview_item : grid->window_list())
- windows.push_back(overview_item->GetWindow());
- }
- return windows;
- }
- void OverviewController::ToggleOverview(OverviewEnterExitType type) {
- // Hide the virtual keyboard as it obstructs the overview mode.
- // Don't need to hide if it's the a11y keyboard, as overview mode
- // can accept text input and it resizes correctly with the a11y keyboard.
- keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
- auto windows =
- Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
- // Hidden windows are a subset of the window excluded from overview in
- // window_util::ShouldExcludeForOverview. Excluded window won't be on the grid
- // but their visibility will remain untouched. Hidden windows will be also
- // excluded and their visibility will be set to false for the duration of
- // overview mode.
- auto should_hide_for_overview = [](aura::Window* w) -> bool {
- // Explicity hidden windows always get hidden.
- if (w->GetProperty(kHideInOverviewKey))
- return true;
- // Since overview allows moving windows, don't show window that can't be
- // moved. If they are a transient ancestor of a postionable window then they
- // can be shown and moved with their transient root.
- return w == wm::GetTransientRoot(w) &&
- !WindowState::Get(w)->IsUserPositionable();
- };
- std::vector<aura::Window*> hide_windows(windows.size());
- auto end = std::copy_if(windows.begin(), windows.end(), hide_windows.begin(),
- should_hide_for_overview);
- hide_windows.resize(end - hide_windows.begin());
- base::EraseIf(windows, window_util::ShouldExcludeForOverview);
- // Overview windows will handle showing their transient related windows, so if
- // a window in |windows| has a transient root also in |windows|, we can remove
- // it as the transient root will handle showing the window.
- // Additionally, |windows| may contain transient children and not their
- // transient root. This can lead to situations where the transient child is
- // destroyed causing its respective overview item to be destroyed, leaving its
- // transient root with no overview item. Creating the overview item with the
- // transient root instead of the transient child fixes this. See
- // crbug.com/972015.
- window_util::EnsureTransientRoots(&windows);
- if (InOverviewSession()) {
- DCHECK(CanEndOverview(type));
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ui", "OverviewController::ExitOverview",
- this);
- // Suspend occlusion tracker until the exit animation is complete.
- PauseOcclusionTracker();
- // We may want to slide out the overview grid in some cases, even if not
- // explicitly stated.
- OverviewEnterExitType new_type =
- MaybeOverrideEnterExitTypeForHomeScreen(type, /*enter=*/false, windows);
- overview_session_->set_enter_exit_overview_type(new_type);
- overview_session_->set_is_shutting_down(true);
- if (!start_animations_.empty())
- OnStartingAnimationComplete(/*canceled=*/true);
- start_animations_.clear();
- if (type == OverviewEnterExitType::kFadeOutExit) {
- // FadeOutExit is used for transition to the home launcher. Minimize the
- // windows without animations to prevent them from getting maximized
- // during overview exit. Minimized widgets will get created in their
- // place, and those widgets will fade out of overview.
- std::vector<aura::Window*> windows_to_minimize(windows.size());
- auto it =
- std::copy_if(windows.begin(), windows.end(),
- windows_to_minimize.begin(), [](aura::Window* window) {
- return !WindowState::Get(window)->IsMinimized();
- });
- windows_to_minimize.resize(
- std::distance(windows_to_minimize.begin(), it));
- window_util::MinimizeAndHideWithoutAnimation(windows_to_minimize);
- }
- // Do not show mask and show during overview shutdown.
- overview_session_->UpdateRoundedCornersAndShadow();
- for (auto& observer : observers_)
- observer.OnOverviewModeEnding(overview_session_.get());
- overview_session_->Shutdown();
- const bool should_end_immediately =
- overview_session_->enter_exit_overview_type() ==
- OverviewEnterExitType::kImmediateExit;
- if (should_end_immediately) {
- for (const auto& animation : delayed_animations_)
- animation->Shutdown();
- delayed_animations_.clear();
- OnEndingAnimationComplete(/*canceled=*/false);
- }
- // Don't delete |overview_session_| yet since the stack is still using it.
- base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
- FROM_HERE, overview_session_.release());
- last_overview_session_time_ = base::Time::Now();
- for (auto& observer : observers_)
- observer.OnOverviewModeEnded();
- if (!should_end_immediately && delayed_animations_.empty())
- OnEndingAnimationComplete(/*canceled=*/false);
- } else {
- DCHECK(CanEnterOverview());
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ui", "OverviewController::EnterOverview",
- this);
- auto* active_window = window_util::GetActiveWindow();
- if (active_window) {
- auto* active_widget =
- views::Widget::GetWidgetForNativeView(active_window);
- if (active_widget)
- paint_as_active_lock_ = active_widget->LockPaintAsActive();
- }
- Shell::Get()->frame_throttling_controller()->StartThrottling(windows);
- // Clear any animations that may be running from last overview end.
- for (const auto& animation : delayed_animations_)
- animation->Shutdown();
- if (!delayed_animations_.empty())
- OnEndingAnimationComplete(/*canceled=*/true);
- delayed_animations_.clear();
- for (auto& observer : observers_)
- observer.OnOverviewModeWillStart();
- should_focus_overview_ = true;
- const SplitViewController::State split_view_state =
- SplitViewController::Get(Shell::GetPrimaryRootWindow())->state();
- // Prevent overview from stealing focus if |split_view_state| is
- // |SplitViewController::State::kLeftSnapped| or
- // |SplitViewController::State::kRightSnapped|. Here are all the cases where
- // |split_view_state| will now have one of those two values:
- // 1. The active window is maximized in tablet mode. The user presses Alt+[.
- // 2. The active window is maximized in tablet mode. The user presses Alt+].
- // 3. The active window is snapped on the right in tablet split view.
- // Another window is snapped on the left in tablet split view. The user
- // presses Alt+[.
- // 4. The active window is snapped on the left in tablet split view. Another
- // window is snapped on the right in tablet split view. The user presses
- // Alt+].
- // 5. Overview starts because of a snapped window carrying over from
- // clamshell mode to tablet mode.
- // 6. Overview starts on transition between user sessions.
- //
- // Note: We have to check the split view state before
- // |SplitViewController::OnOverviewModeStarting|, because in case of
- // |SplitViewController::State::kBothSnapped|, that function will insert one
- // of the two snapped windows to overview.
- if (split_view_state == SplitViewController::State::kLeftSnapped ||
- split_view_state == SplitViewController::State::kRightSnapped) {
- should_focus_overview_ = false;
- } else {
- // Avoid stealing activation from a dragged active window.
- aura::Window* active_window = window_util::GetActiveWindow();
- if (active_window && WindowState::Get(active_window)->is_dragged()) {
- DCHECK(window_util::ShouldExcludeForOverview(active_window));
- should_focus_overview_ = false;
- }
- }
- // Suspend occlusion tracker until the enter animation is complete.
- PauseOcclusionTracker();
- overview_session_ = std::make_unique<OverviewSession>(this);
- // We may want to slide in the overview grid in some cases, even if not
- // explicitly stated.
- OverviewEnterExitType new_type =
- MaybeOverrideEnterExitTypeForHomeScreen(type, /*enter=*/true, windows);
- overview_session_->set_enter_exit_overview_type(new_type);
- for (auto& observer : observers_)
- observer.OnOverviewModeStarting();
- overview_session_->Init(windows, hide_windows);
- // When fading in from home, start animating blur immediately (if animation
- // is required) - with this transition the item widgets are positioned in
- // the overview immediately, so delaying blur start until start animations
- // finish looks janky.
- overview_wallpaper_controller_->Blur(
- /*animate=*/new_type == OverviewEnterExitType::kFadeInEnter);
- // For app dragging, there are no start animations so add a delay to delay
- // animations observing when the start animation ends, such as the shelf,
- // shadow and rounded corners.
- if (new_type == OverviewEnterExitType::kImmediateEnter &&
- !delayed_animation_task_delay_.is_zero()) {
- auto force_delay_observer =
- std::make_unique<ForceDelayObserver>(delayed_animation_task_delay_);
- AddEnterAnimationObserver(std::move(force_delay_observer));
- }
- if (start_animations_.empty())
- OnStartingAnimationComplete(/*canceled=*/false);
- if (!last_overview_session_time_.is_null()) {
- UMA_HISTOGRAM_LONG_TIMES("Ash.Overview.TimeBetweenUse",
- base::Time::Now() - last_overview_session_time_);
- }
- }
- }
- bool OverviewController::CanEnterOverview() {
- // Prevent entering overview while the divider is dragged or animated.
- if (IsSplitViewDividerDraggedOrAnimated())
- return false;
- // Prevent entering overview if a desk animation is underway. The overview
- // animation would be completely covered anyway, and doing so could put us in
- // a strange state. Note that exiting overview is allowed as it is part of the
- // animation.
- if (DesksController::Get()->animation()) {
- // The one exception to this rule is in tablet mode, having a window snapped
- // to one side. Moving to this desk, we will want to open overview on the
- // other side. For clamshell we don't need to enter overview as having a
- // window snapped to one side and showing the wallpaper on the other is
- // fine.
- auto* split_view_controller =
- SplitViewController::Get(Shell::GetPrimaryRootWindow());
- if (!split_view_controller->InTabletSplitViewMode() ||
- split_view_controller->state() ==
- SplitViewController::State::kBothSnapped) {
- return false;
- }
- }
- // Don't allow a window overview if the user session is not active (e.g.
- // locked or in user-adding screen) or a modal dialog is open or running in
- // kiosk app session.
- SessionControllerImpl* session_controller =
- Shell::Get()->session_controller();
- return session_controller->GetSessionState() ==
- session_manager::SessionState::ACTIVE &&
- !Shell::IsSystemModalWindowOpen() &&
- !Shell::Get()->screen_pinning_controller()->IsPinned() &&
- !session_controller->IsRunningInAppMode();
- }
- bool OverviewController::CanEndOverview(OverviewEnterExitType type) {
- // Prevent ending overview while the divider is dragged or animated.
- if (IsSplitViewDividerDraggedOrAnimated())
- return false;
- // Do not allow ending overview if we're in single split mode unless swiping
- // up from the shelf in tablet mode, or ending overview immediately without
- // animations.
- SplitViewController* split_view_controller =
- SplitViewController::Get(Shell::GetPrimaryRootWindow());
- if (split_view_controller->InTabletSplitViewMode() &&
- split_view_controller->state() !=
- SplitViewController::State::kBothSnapped &&
- InOverviewSession() && overview_session_->IsEmpty() &&
- type != OverviewEnterExitType::kImmediateExit) {
- return false;
- }
- return true;
- }
- void OverviewController::OnStartingAnimationComplete(bool canceled) {
- DCHECK(overview_session_);
- // For kFadeInEnter, wallpaper blur is initiated on transition start,
- // so it doesn't have to be requested again on starting animation end.
- if (!canceled && overview_session_->enter_exit_overview_type() !=
- OverviewEnterExitType::kFadeInEnter) {
- overview_wallpaper_controller_->Blur(/*animate=*/true);
- }
- for (auto& observer : observers_)
- observer.OnOverviewModeStartingAnimationComplete(canceled);
- // Observers should not do anything which may cause overview to quit
- // explicitly (i.e. ToggleOverview()) or implicity (i.e. activation change).
- DCHECK(overview_session_);
- overview_session_->OnStartingAnimationComplete(canceled,
- should_focus_overview_);
- UnpauseOcclusionTracker(kOcclusionPauseDurationForStart);
- TRACE_EVENT_NESTABLE_ASYNC_END1("ui", "OverviewController::EnterOverview",
- this, "canceled", canceled);
- }
- void OverviewController::OnEndingAnimationComplete(bool canceled) {
- for (auto& observer : observers_)
- observer.OnOverviewModeEndingAnimationComplete(canceled);
- UnpauseOcclusionTracker(occlusion_pause_duration_for_end_);
- // Unblur when animation is completed (or right away if there was no
- // delayed animation) unless it's canceled, in which case, we should keep
- // the blur. Also resume the activation frame state.
- if (!canceled) {
- overview_wallpaper_controller_->Unblur();
- paint_as_active_lock_.reset();
- }
- // Ends the manual frame throttling at the end of overview exit.
- Shell::Get()->frame_throttling_controller()->EndThrottling();
- TRACE_EVENT_NESTABLE_ASYNC_END1("ui", "OverviewController::ExitOverview",
- this, "canceled", canceled);
- }
- void OverviewController::ResetPauser() {
- occlusion_tracker_pauser_.reset();
- }
- void OverviewController::UpdateRoundedCornersAndShadow() {
- if (overview_session_)
- overview_session_->UpdateRoundedCornersAndShadow();
- }
- } // namespace ash
|