Commit 5ace5a1e authored by Ahmed Fakhry's avatar Ahmed Fakhry Committed by Commit Bot

Virtual Desks: The desk remove animation

BUG=977434, 866622

Change-Id: I56569fb480cdcee8c483dd6100be880cb1ae094c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1720797Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682914}
parent a25b308a
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <utility> #include <utility>
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_controller.h"
#include "ash/wm/window_transient_descendant_iterator.h" #include "ash/wm/window_transient_descendant_iterator.h"
#include "base/time/time.h" #include "base/time/time.h"
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "ash/wm/splitview/split_view_utils.h" #include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/window_util.h" #include "ash/wm/window_util.h"
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/stl_util.h" #include "base/stl_util.h"
...@@ -84,6 +85,232 @@ void RemoveWindowsFromOverview(const base::flat_set<aura::Window*>& windows) { ...@@ -84,6 +85,232 @@ void RemoveWindowsFromOverview(const base::flat_set<aura::Window*>& windows) {
} // namespace } // namespace
// -----------------------------------------------------------------------------
// DesksController::AbstractDeskSwitchAnimation:
// An abstract class that handles the shared operations need to be performed
// when doing an animation that causes a desk switch animation. Subclasses
// such as DeskActivationAnimation and DeskRemovalAnimation implement the
// abstract interface of this class to handle the unique operations specific to
// each animation type.
class DesksController::DeskAnimationBase
: public RootWindowDeskSwitchAnimator::Delegate {
public:
~DeskAnimationBase() override = default;
// Launches the animation. This should be done once all animators
// are created and added to `desk_switch_animators_`. This is to avoid any
// potential race conditions that might happen if one animator finished phase
// (1) of the animation while other animators are still being constructed.
void Launch() {
DCHECK(!desk_switch_animators_.empty());
for (auto& animator : desk_switch_animators_)
animator->TakeStartingDeskScreenshot();
}
// RootWindowDeskSwitchAnimator::Delegate:
void OnStartingDeskScreenshotTaken(const Desk* ending_desk) override {
DCHECK(!desk_switch_animators_.empty());
// Once all starting desk screenshots on all roots are taken and placed on
// the screens, do the actual desk activation logic.
for (const auto& animator : desk_switch_animators_) {
if (!animator->starting_desk_screenshot_taken())
return;
}
// Extend the compositors' timeouts in order to prevents any repaints until
// the desks are switched and overview mode exits.
const auto roots = Shell::GetAllRootWindows();
for (auto* root : roots)
root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(true);
OnStartingDeskScreenshotTakenInternal(ending_desk);
for (auto* root : roots)
root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(false);
// Continue the second phase of the animation by taking the ending desk
// screenshot and actually animating the layers.
for (auto& animator : desk_switch_animators_)
animator->TakeEndingDeskScreenshot();
}
void OnEndingDeskScreenshotTaken() override {
DCHECK(!desk_switch_animators_.empty());
// Once all ending desk screenshots on all roots are taken, start the
// animation on all roots at the same time, so that they look synchrnoized.
for (const auto& animator : desk_switch_animators_) {
if (!animator->ending_desk_screenshot_taken())
return;
}
for (auto& animator : desk_switch_animators_)
animator->StartAnimation();
}
void OnDeskSwitchAnimationFinished() override {
DCHECK(!desk_switch_animators_.empty());
// Once all desk switch animations on all roots finish, destroy all the
// animators.
for (const auto& animator : desk_switch_animators_) {
if (!animator->animation_finished())
return;
}
OnDeskSwitchAnimationFinishedInternal();
desk_switch_animators_.clear();
for (auto& observer : controller_->observers_)
observer.OnDeskSwitchAnimationFinished();
controller_->OnAnimationFinished(this);
}
protected:
DeskAnimationBase(DesksController* controller) : controller_(controller) {}
// Abstract functions that can be overridden by child classes to do different
// things when phase (1), and phase (3) completes. Note that
// `OnDeskSwitchAnimationFinishedInternal()` will be called before the desks
// screenshot layers, stored in `desk_switch_animators_`, are destroyed.
virtual void OnStartingDeskScreenshotTakenInternal(
const Desk* ending_desk) = 0;
virtual void OnDeskSwitchAnimationFinishedInternal() = 0;
DesksController* const controller_;
// An animator object per each root. Once all the animations are complete,
// this list is cleared.
std::vector<std::unique_ptr<RootWindowDeskSwitchAnimator>>
desk_switch_animators_;
private:
DISALLOW_COPY_AND_ASSIGN(DeskAnimationBase);
};
// -----------------------------------------------------------------------------
// DesksController::DeskActivationAnimation:
class DesksController::DeskActivationAnimation
: public DesksController::DeskAnimationBase {
public:
DeskActivationAnimation(DesksController* controller,
const Desk* ending_desk,
bool move_left)
: DeskAnimationBase(controller) {
for (auto* root : Shell::GetAllRootWindows()) {
desk_switch_animators_.emplace_back(
std::make_unique<RootWindowDeskSwitchAnimator>(root, ending_desk,
this, move_left,
/*for_remove=*/false));
}
}
~DeskActivationAnimation() override = default;
// DesksController::AbstractDeskSwitchAnimation:
void OnStartingDeskScreenshotTakenInternal(const Desk* ending_desk) override {
// The order here matters. Overview must end before ending split view before
// switching desks. That's because we don't want TabletModeWindowManager
// maximizing all windows because we cleared the snapped ones in
// split_view_controller first. See:
// `TabletModeWindowManager::OnOverviewModeEndingAnimationComplete()`.
// See also test coverage for this case in:
// `TabletModeDesksTest.SnappedStateRetainedOnSwitchingDesksFromOverview`.
const bool in_overview =
Shell::Get()->overview_controller()->InOverviewSession();
if (in_overview) {
// Exit overview mode immediately without any animations before taking the
// ending desk screenshot. This makes sure that the ending desk
// screenshot will only show the windows in that desk, not overview stuff.
Shell::Get()->overview_controller()->EndOverview(
OverviewSession::EnterExitOverviewType::kImmediateExit);
}
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
split_view_controller->EndSplitView(
SplitViewController::EndReason::kDesksChange);
controller_->ActivateDeskInternal(ending_desk,
/*update_window_activation=*/true);
MaybeRestoreSplitView(/*refresh_snapped_windows=*/true);
}
void OnDeskSwitchAnimationFinishedInternal() override {}
private:
DISALLOW_COPY_AND_ASSIGN(DeskActivationAnimation);
};
// -----------------------------------------------------------------------------
// DesksController::DeskRemovalAnimation:
class DesksController::DeskRemovalAnimation
: public DesksController::DeskAnimationBase {
public:
DeskRemovalAnimation(DesksController* controller,
const Desk* desk_to_remove,
const Desk* desk_to_activate,
bool move_left,
DesksCreationRemovalSource source)
: DeskAnimationBase(controller),
desk_to_remove_(desk_to_remove),
request_source_(source) {
DCHECK(!Shell::Get()->overview_controller()->InOverviewSession());
DCHECK_EQ(controller_->active_desk(), desk_to_remove_);
for (auto* root : Shell::GetAllRootWindows()) {
desk_switch_animators_.emplace_back(
std::make_unique<RootWindowDeskSwitchAnimator>(root, desk_to_activate,
this, move_left,
/*for_remove=*/true));
}
}
~DeskRemovalAnimation() override = default;
// DesksController::AbstractDeskSwitchAnimation:
void OnStartingDeskScreenshotTakenInternal(const Desk* ending_desk) override {
DCHECK_EQ(controller_->active_desk(), desk_to_remove_);
// We are removing the active desk, which may have split view active.
// We will restore the split view state of the newly activated desk at the
// end of the animation.
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
split_view_controller->EndSplitView(
SplitViewController::EndReason::kDesksChange);
// At the end of phase (1), we activate the target desk (i.e. the desk that
// will be activated after the active desk `desk_to_remove_` is removed).
// This means that phase (2) will take a screenshot of that desk before we
// move the windows of `desk_to_remove_` to that target desk.
controller_->ActivateDeskInternal(ending_desk,
/*update_window_activation=*/false);
}
void OnDeskSwitchAnimationFinishedInternal() override {
// Do the actual desk removal behind the scenes before the screenshot layers
// are destroyed.
controller_->RemoveDeskInternal(desk_to_remove_, request_source_);
MaybeRestoreSplitView(/*refresh_snapped_windows=*/true);
}
private:
const Desk* const desk_to_remove_;
const DesksCreationRemovalSource request_source_;
DISALLOW_COPY_AND_ASSIGN(DeskRemovalAnimation);
};
// -----------------------------------------------------------------------------
// DesksController:
DesksController::DesksController() { DesksController::DesksController() {
Shell::Get()->activation_client()->AddObserver(this); Shell::Get()->activation_client()->AddObserver(this);
...@@ -106,7 +333,7 @@ DesksController* DesksController::Get() { ...@@ -106,7 +333,7 @@ DesksController* DesksController::Get() {
} }
void DesksController::Shutdown() { void DesksController::Shutdown() {
desk_switch_animators_.clear(); animations_.clear();
} }
void DesksController::AddObserver(Observer* observer) { void DesksController::AddObserver(Observer* observer) {
...@@ -118,7 +345,7 @@ void DesksController::RemoveObserver(Observer* observer) { ...@@ -118,7 +345,7 @@ void DesksController::RemoveObserver(Observer* observer) {
} }
bool DesksController::AreDesksBeingModified() const { bool DesksController::AreDesksBeingModified() const {
return are_desks_being_modified_ || !desk_switch_animators_.empty(); return are_desks_being_modified_ || !animations_.empty();
} }
bool DesksController::CanCreateDesks() const { bool DesksController::CanCreateDesks() const {
...@@ -167,135 +394,29 @@ void DesksController::NewDesk(DesksCreationRemovalSource source) { ...@@ -167,135 +394,29 @@ void DesksController::NewDesk(DesksCreationRemovalSource source) {
void DesksController::RemoveDesk(const Desk* desk, void DesksController::RemoveDesk(const Desk* desk,
DesksCreationRemovalSource source) { DesksCreationRemovalSource source) {
DCHECK(CanRemoveDesks()); DCHECK(CanRemoveDesks());
DCHECK(HasDesk(desk));
base::AutoReset<bool> in_progress(&are_desks_being_modified_, true); base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
auto iter = std::find_if(
desks_.begin(), desks_.end(),
[desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
DCHECK(iter != desks_.end());
// Used by accessibility to indicate the desk that has been removed.
const int removed_desk_number = std::distance(desks_.begin(), iter) + 1;
// Keep the removed desk alive until the end of this function.
std::unique_ptr<Desk> removed_desk = std::move(*iter);
DCHECK_EQ(removed_desk.get(), desk);
auto iter_after = desks_.erase(iter);
DCHECK(!desks_.empty());
auto* overview_controller = Shell::Get()->overview_controller(); auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession(); const bool in_overview = overview_controller->InOverviewSession();
const std::vector<aura::Window*> removed_desk_windows = if (!in_overview && active_desk_ == desk) {
removed_desk->windows(); // When removing the active desk outside of overview, we trigger the remove
// desk animation. We will activate the desk to its left if any, otherwise,
// No need to spend time refreshing the mini_views of the removed desk. // we activate one on the right.
auto removed_desk_mini_views_pauser = const int current_desk_index = GetDeskIndex(active_desk_);
removed_desk->GetScopedNotifyContentChangedDisabler(); const int target_desk_index =
current_desk_index + ((current_desk_index > 0) ? -1 : 1);
// - Move windows in removed desk (if any) to the currently active desk. DCHECK_GE(target_desk_index, 0);
// - If the active desk is the one being removed, activate the desk to its DCHECK_LT(target_desk_index, static_cast<int>(desks_.size()));
// left, if no desk to the left, activate one on the right. const bool move_left = current_desk_index < target_desk_index;
const bool will_switch_desks = (removed_desk.get() == active_desk_); animations_.emplace_back(std::make_unique<DeskRemovalAnimation>(
if (!will_switch_desks) { this, desk, desks_[target_desk_index].get(), move_left, source));
// We will refresh the mini_views of the active desk only once at the end. animations_.back()->Launch();
auto active_desk_mini_view_pauser = return;
active_desk_->GetScopedNotifyContentChangedDisabler();
removed_desk->MoveWindowsToDesk(active_desk_);
// If overview mode is active, we add the windows of the removed desk to the
// overview grid in the order of their MRU. Note that this can only be done
// after the windows have moved to the active desk above, so that building
// the window MRU list should contain those windows.
if (in_overview)
AppendWindowsToOverview(removed_desk_windows, /*should_animate=*/true);
} else {
Desk* target_desk = nullptr;
if (iter_after == desks_.begin()) {
// Nothing before this desk.
target_desk = (*iter_after).get();
} else {
// Back up to select the desk on the left.
target_desk = (*(--iter_after)).get();
}
DCHECK(target_desk);
// The target desk, which is about to become active, will have its
// mini_views refreshed at the end.
auto target_desk_mini_view_pauser =
target_desk->GetScopedNotifyContentChangedDisabler();
// Exit split view if active, before activating the new desk. We will
// restore the split view state of the newly activated desk at the end.
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
split_view_controller->EndSplitView(
SplitViewController::EndReason::kDesksChange);
// The removed desk is the active desk, so temporarily remove its windows
// from the overview grid which will result in removing the
// "OverviewModeLabel" widgets created by overview mode for these windows.
// This way the removed desk tracks only real windows, which are now ready
// to be moved to the target desk.
if (in_overview)
RemoveWindowsFromOverview(removed_desk_windows);
// If overview mode is active, change desk activation without changing
// window activation. Activation should remain on the dummy
// "OverviewModeFocusedWidget" while overview mode is active.
removed_desk->MoveWindowsToDesk(target_desk);
ActivateDesk(target_desk, DesksSwitchSource::kDeskRemoved);
// Desk activation should not change overview mode state.
DCHECK_EQ(in_overview, overview_controller->InOverviewSession());
// Now that the windows from the removed and target desks merged, add them
// all without animation to the grid in the order of their MRU.
if (in_overview)
AppendWindowsToOverview(target_desk->windows(), /*should_animate=*/false);
} }
// It's OK now to refresh the mini_views of *only* the active desk, and only RemoveDeskInternal(desk, source);
// if windows from the removed desk moved to it.
DCHECK(active_desk_->should_notify_content_changed());
if (!removed_desk_windows.empty())
active_desk_->NotifyContentChanged();
for (auto& observer : observers_)
observer.OnDeskRemoved(removed_desk.get());
available_container_ids_.push(removed_desk->container_id());
// Avoid having stale backdrop state as a desk is removed while in overview
// mode, since the backdrop controller won't update the backdrop window as
// the removed desk's windows move out from the container. Therefore, we need
// to update it manually.
if (in_overview)
removed_desk->UpdateDeskBackdrops();
// Restoring split view may start or end overview mode, therefore do this at
// the end to avoid getting into a bad state.
if (will_switch_desks)
MaybeRestoreSplitView(/*refresh_snapped_windows=*/true);
UMA_HISTOGRAM_ENUMERATION(kRemoveDeskHistogramName, source);
ReportDesksCountHistogram();
ReportNumberOfWindowsPerDeskHistogram();
int active_desk_number = GetDeskIndex(active_desk_) + 1;
if (active_desk_number == removed_desk_number)
active_desk_number++;
Shell::Get()
->accessibility_controller()
->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED,
base::NumberToString16(removed_desk_number),
base::NumberToString16(active_desk_number)));
DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
} }
void DesksController::ActivateDesk(const Desk* desk, DesksSwitchSource source) { void DesksController::ActivateDesk(const Desk* desk, DesksSwitchSource source) {
...@@ -329,18 +450,9 @@ void DesksController::ActivateDesk(const Desk* desk, DesksSwitchSource source) { ...@@ -329,18 +450,9 @@ void DesksController::ActivateDesk(const Desk* desk, DesksSwitchSource source) {
// desks. Therefore, desks at lower indices are located on the left of desks // desks. Therefore, desks at lower indices are located on the left of desks
// with higher indices. // with higher indices.
const bool move_left = GetDeskIndex(active_desk_) < GetDeskIndex(desk); const bool move_left = GetDeskIndex(active_desk_) < GetDeskIndex(desk);
for (auto* root : Shell::GetAllRootWindows()) { animations_.emplace_back(
desk_switch_animators_.emplace_back( std::make_unique<DeskActivationAnimation>(this, desk, move_left));
std::make_unique<RootWindowDeskSwitchAnimator>(root, desk, this, animations_.back()->Launch();
move_left));
}
// Once all animators are created, start them all by taking the starting desk
// screenshots. This is to avoid any potential race conditions that might
// happen if one animator finished phase (1) of the animation while other
// animators are still being constructed.
for (auto& animator : desk_switch_animators_)
animator->TakeStartingDeskScreenshot();
} }
bool DesksController::ActivateAdjacentDesk(bool going_left, bool DesksController::ActivateAdjacentDesk(bool going_left,
...@@ -423,86 +535,6 @@ void DesksController::OnRootWindowClosing(aura::Window* root_window) { ...@@ -423,86 +535,6 @@ void DesksController::OnRootWindowClosing(aura::Window* root_window) {
desk->OnRootWindowClosing(root_window); desk->OnRootWindowClosing(root_window);
} }
void DesksController::OnStartingDeskScreenshotTaken(const Desk* ending_desk) {
DCHECK(!desk_switch_animators_.empty());
// Once all starting desk screenshots on all roots are taken and placed on the
// screens, do the actual desk activation logic.
for (const auto& animator : desk_switch_animators_) {
if (!animator->starting_desk_screenshot_taken())
return;
}
// Extend the compositors' timeouts in order to prevents any repaints until
// the desks are switched and overview mode exits.
const auto roots = Shell::GetAllRootWindows();
for (auto* root : roots)
root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(true);
// The order here matters. Overview must end before ending split view before
// switching desks. That's because we don't want TabletModeWindowManager
// maximizing all windows because we cleared the snapped ones in
// split_view_controller first. See:
// `TabletModeWindowManager::OnOverviewModeEndingAnimationComplete()`.
// See also test coverage for this case in:
// `TabletModeDesksTest.SnappedStateRetainedOnSwitchingDesksFromOverview`.
const bool in_overview =
Shell::Get()->overview_controller()->InOverviewSession();
if (in_overview) {
// Exit overview mode immediately without any animations before taking the
// ending desk screenshot. This makes sure that the ending desk
// screenshot will only show the windows in that desk, not overview stuff.
Shell::Get()->overview_controller()->EndOverview(
OverviewSession::EnterExitOverviewType::kImmediateExit);
}
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
split_view_controller->EndSplitView(
SplitViewController::EndReason::kDesksChange);
ActivateDeskInternal(ending_desk, /*update_window_activation=*/true);
MaybeRestoreSplitView(/*refresh_snapped_windows=*/true);
for (auto* root : roots)
root->GetHost()->compositor()->SetAllowLocksToExtendTimeout(false);
// Continue the second phase of the animation by taking the ending desk
// screenshot and actually animating the layers.
for (auto& animator : desk_switch_animators_)
animator->TakeEndingDeskScreenshot();
}
void DesksController::OnEndingDeskScreenshotTaken() {
DCHECK(!desk_switch_animators_.empty());
// Once all ending desk screenshots on all roots are taken, start the
// animation on all roots at the same time, so that they look synchrnoized.
for (const auto& animator : desk_switch_animators_) {
if (!animator->ending_desk_screenshot_taken())
return;
}
for (auto& animator : desk_switch_animators_)
animator->StartAnimation();
}
void DesksController::OnDeskSwitchAnimationFinished() {
DCHECK(!desk_switch_animators_.empty());
// Once all desk switch animations on all roots finish, destroy all the
// animators.
for (const auto& animator : desk_switch_animators_) {
if (!animator->animation_finished())
return;
}
desk_switch_animators_.clear();
for (auto& observer : observers_)
observer.OnDeskSwitchAnimationFinished();
}
void DesksController::OnWindowActivating(ActivationReason reason, void DesksController::OnWindowActivating(ActivationReason reason,
aura::Window* gaining_active, aura::Window* gaining_active,
aura::Window* losing_active) { aura::Window* losing_active) {
...@@ -523,6 +555,10 @@ void DesksController::OnWindowActivated(ActivationReason reason, ...@@ -523,6 +555,10 @@ void DesksController::OnWindowActivated(ActivationReason reason,
aura::Window* gained_active, aura::Window* gained_active,
aura::Window* lost_active) {} aura::Window* lost_active) {}
void DesksController::OnAnimationFinished(DeskAnimationBase* animation) {
base::EraseIf(animations_, base::MatchesUniquePtr(animation));
}
bool DesksController::HasDesk(const Desk* desk) const { bool DesksController::HasDesk(const Desk* desk) const {
auto iter = std::find_if( auto iter = std::find_if(
desks_.begin(), desks_.end(), desks_.begin(), desks_.end(),
...@@ -564,6 +600,140 @@ void DesksController::ActivateDeskInternal(const Desk* desk, ...@@ -564,6 +600,140 @@ void DesksController::ActivateDeskInternal(const Desk* desk,
observer.OnDeskActivationChanged(active_desk_, old_active); observer.OnDeskActivationChanged(active_desk_, old_active);
} }
void DesksController::RemoveDeskInternal(const Desk* desk,
DesksCreationRemovalSource source) {
DCHECK(CanRemoveDesks());
base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
auto iter = std::find_if(
desks_.begin(), desks_.end(),
[desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
DCHECK(iter != desks_.end());
// Used by accessibility to indicate the desk that has been removed.
const int removed_desk_number = std::distance(desks_.begin(), iter) + 1;
// Keep the removed desk alive until the end of this function.
std::unique_ptr<Desk> removed_desk = std::move(*iter);
DCHECK_EQ(removed_desk.get(), desk);
auto iter_after = desks_.erase(iter);
DCHECK(!desks_.empty());
auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession();
const std::vector<aura::Window*> removed_desk_windows =
removed_desk->windows();
// No need to spend time refreshing the mini_views of the removed desk.
auto removed_desk_mini_views_pauser =
removed_desk->GetScopedNotifyContentChangedDisabler();
// - Move windows in removed desk (if any) to the currently active desk.
// - If the active desk is the one being removed, activate the desk to its
// left, if no desk to the left, activate one on the right.
const bool will_switch_desks = (removed_desk.get() == active_desk_);
if (!will_switch_desks) {
// We will refresh the mini_views of the active desk only once at the end.
auto active_desk_mini_view_pauser =
active_desk_->GetScopedNotifyContentChangedDisabler();
removed_desk->MoveWindowsToDesk(active_desk_);
// If overview mode is active, we add the windows of the removed desk to the
// overview grid in the order of their MRU. Note that this can only be done
// after the windows have moved to the active desk above, so that building
// the window MRU list should contain those windows.
if (in_overview)
AppendWindowsToOverview(removed_desk_windows, /*should_animate=*/true);
} else {
Desk* target_desk = nullptr;
if (iter_after == desks_.begin()) {
// Nothing before this desk.
target_desk = (*iter_after).get();
} else {
// Back up to select the desk on the left.
target_desk = (*(--iter_after)).get();
}
DCHECK(target_desk);
// The target desk, which is about to become active, will have its
// mini_views refreshed at the end.
auto target_desk_mini_view_pauser =
target_desk->GetScopedNotifyContentChangedDisabler();
// Exit split view if active, before activating the new desk. We will
// restore the split view state of the newly activated desk at the end.
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
split_view_controller->EndSplitView(
SplitViewController::EndReason::kDesksChange);
// The removed desk is the active desk, so temporarily remove its windows
// from the overview grid which will result in removing the
// "OverviewModeLabel" widgets created by overview mode for these windows.
// This way the removed desk tracks only real windows, which are now ready
// to be moved to the target desk.
if (in_overview)
RemoveWindowsFromOverview(removed_desk_windows);
// If overview mode is active, change desk activation without changing
// window activation. Activation should remain on the dummy
// "OverviewModeFocusedWidget" while overview mode is active.
removed_desk->MoveWindowsToDesk(target_desk);
ActivateDesk(target_desk, DesksSwitchSource::kDeskRemoved);
// Desk activation should not change overview mode state.
DCHECK_EQ(in_overview, overview_controller->InOverviewSession());
// Now that the windows from the removed and target desks merged, add them
// all without animation to the grid in the order of their MRU.
if (in_overview)
AppendWindowsToOverview(target_desk->windows(), /*should_animate=*/false);
}
// It's OK now to refresh the mini_views of *only* the active desk, and only
// if windows from the removed desk moved to it.
DCHECK(active_desk_->should_notify_content_changed());
if (!removed_desk_windows.empty())
active_desk_->NotifyContentChanged();
for (auto& observer : observers_)
observer.OnDeskRemoved(removed_desk.get());
available_container_ids_.push(removed_desk->container_id());
// Avoid having stale backdrop state as a desk is removed while in overview
// mode, since the backdrop controller won't update the backdrop window as
// the removed desk's windows move out from the container. Therefore, we need
// to update it manually.
if (in_overview)
removed_desk->UpdateDeskBackdrops();
// Restoring split view may start or end overview mode, therefore do this at
// the end to avoid getting into a bad state.
if (will_switch_desks)
MaybeRestoreSplitView(/*refresh_snapped_windows=*/true);
UMA_HISTOGRAM_ENUMERATION(kRemoveDeskHistogramName, source);
ReportDesksCountHistogram();
ReportNumberOfWindowsPerDeskHistogram();
int active_desk_number = GetDeskIndex(active_desk_) + 1;
if (active_desk_number == removed_desk_number)
active_desk_number++;
Shell::Get()
->accessibility_controller()
->TriggerAccessibilityAlertWithMessage(l10n_util::GetStringFUTF8(
IDS_ASH_VIRTUAL_DESKS_ALERT_DESK_REMOVED,
base::NumberToString16(removed_desk_number),
base::NumberToString16(active_desk_number)));
DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
}
const Desk* DesksController::FindDeskOfWindow(aura::Window* window) const { const Desk* DesksController::FindDeskOfWindow(aura::Window* window) const {
DCHECK(window); DCHECK(window);
......
...@@ -26,9 +26,7 @@ class Desk; ...@@ -26,9 +26,7 @@ class Desk;
// Defines a controller for creating, destroying and managing virtual desks and // Defines a controller for creating, destroying and managing virtual desks and
// their windows. // their windows.
class ASH_EXPORT DesksController class ASH_EXPORT DesksController : public wm::ActivationChangeObserver {
: public RootWindowDeskSwitchAnimator::Delegate,
public ::wm::ActivationChangeObserver {
public: public:
class Observer { class Observer {
public: public:
...@@ -93,6 +91,8 @@ class ASH_EXPORT DesksController ...@@ -93,6 +91,8 @@ class ASH_EXPORT DesksController
// Removes and deletes the given |desk|. |desk| must already exist, and // Removes and deletes the given |desk|. |desk| must already exist, and
// CanRemoveDesks() must be checked before this. // CanRemoveDesks() must be checked before this.
// This will trigger the `DeskRemovalAnimation` if the active desk is being
// removed outside of overview.
void RemoveDesk(const Desk* desk, DesksCreationRemovalSource source); void RemoveDesk(const Desk* desk, DesksCreationRemovalSource source);
// Performs the desk switch animation on all root windows to activate the // Performs the desk switch animation on all root windows to activate the
...@@ -100,6 +100,7 @@ class ASH_EXPORT DesksController ...@@ -100,6 +100,7 @@ class ASH_EXPORT DesksController
// an existing desk. The active window on the currently active desk will be // an existing desk. The active window on the currently active desk will be
// deactivated, and the most-recently used window from the newly-activated // deactivated, and the most-recently used window from the newly-activated
// desk will be activated. // desk will be activated.
// This will trigger the `DeskActivationAnimation`.
void ActivateDesk(const Desk* desk, DesksSwitchSource source); void ActivateDesk(const Desk* desk, DesksSwitchSource source);
// Activates the desk to the left or right of the current desk, if it exists. // Activates the desk to the left or right of the current desk, if it exists.
...@@ -122,11 +123,6 @@ class ASH_EXPORT DesksController ...@@ -122,11 +123,6 @@ class ASH_EXPORT DesksController
void OnRootWindowAdded(aura::Window* root_window); void OnRootWindowAdded(aura::Window* root_window);
void OnRootWindowClosing(aura::Window* root_window); void OnRootWindowClosing(aura::Window* root_window);
// RootWindowDeskSwitchAnimator::Delegate:
void OnStartingDeskScreenshotTaken(const Desk* ending_desk) override;
void OnEndingDeskScreenshotTaken() override;
void OnDeskSwitchAnimationFinished() override;
// ::wm::ActivationChangeObserver: // ::wm::ActivationChangeObserver:
void OnWindowActivating(ActivationReason reason, void OnWindowActivating(ActivationReason reason,
aura::Window* gaining_active, aura::Window* gaining_active,
...@@ -136,6 +132,12 @@ class ASH_EXPORT DesksController ...@@ -136,6 +132,12 @@ class ASH_EXPORT DesksController
aura::Window* lost_active) override; aura::Window* lost_active) override;
private: private:
class DeskAnimationBase;
class DeskActivationAnimation;
class DeskRemovalAnimation;
void OnAnimationFinished(DeskAnimationBase* animation);
bool HasDesk(const Desk* desk) const; bool HasDesk(const Desk* desk) const;
int GetDeskIndex(const Desk* desk) const; int GetDeskIndex(const Desk* desk) const;
...@@ -151,6 +153,9 @@ class ASH_EXPORT DesksController ...@@ -151,6 +153,9 @@ class ASH_EXPORT DesksController
// active. // active.
void ActivateDeskInternal(const Desk* desk, bool update_window_activation); void ActivateDeskInternal(const Desk* desk, bool update_window_activation);
// Removes `desk` without animation.
void RemoveDeskInternal(const Desk* desk, DesksCreationRemovalSource source);
// Returns the desk to which |window| belongs or nullptr if it doesn't belong // Returns the desk to which |window| belongs or nullptr if it doesn't belong
// to any desk. // to any desk.
const Desk* FindDeskOfWindow(aura::Window* window) const; const Desk* FindDeskOfWindow(aura::Window* window) const;
...@@ -170,10 +175,8 @@ class ASH_EXPORT DesksController ...@@ -170,10 +175,8 @@ class ASH_EXPORT DesksController
// mode as a result of desks modifications. // mode as a result of desks modifications.
bool are_desks_being_modified_ = false; bool are_desks_being_modified_ = false;
// An animator object per each root. Once all the animations are complete, // List of on-going desks animations.
// this list is cleared. std::vector<std::unique_ptr<DeskAnimationBase>> animations_;
std::vector<std::unique_ptr<RootWindowDeskSwitchAnimator>>
desk_switch_animators_;
// A free list of desk container IDs to be used for newly-created desks. New // A free list of desk container IDs to be used for newly-created desks. New
// desks pops from this queue and removed desks's associated container IDs are // desks pops from this queue and removed desks's associated container IDs are
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
#include "ash/wm/desks/desks_test_util.h" #include "ash/wm/desks/desks_test_util.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h" #include "ash/wm/desks/desk.h"
#include "ash/wm/overview/overview_controller.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace ash { namespace ash {
...@@ -45,4 +47,15 @@ void ActivateDesk(const Desk* desk) { ...@@ -45,4 +47,15 @@ void ActivateDesk(const Desk* desk) {
ASSERT_TRUE(desk->is_active()); ASSERT_TRUE(desk->is_active());
} }
void RemoveDesk(const Desk* desk) {
auto* controller = DesksController::Get();
const bool in_overview =
Shell::Get()->overview_controller()->InOverviewSession();
const bool should_wait = controller->active_desk() == desk && !in_overview;
DeskSwitchAnimationWaiter waiter;
controller->RemoveDesk(desk, DesksCreationRemovalSource::kButton);
if (should_wait)
waiter.Wait();
}
} // namespace ash } // namespace ash
...@@ -37,6 +37,10 @@ class DeskSwitchAnimationWaiter : public DesksController::Observer { ...@@ -37,6 +37,10 @@ class DeskSwitchAnimationWaiter : public DesksController::Observer {
// complete before returning. // complete before returning.
void ActivateDesk(const Desk* desk); void ActivateDesk(const Desk* desk);
// Removes the given |desk| and waits for the desk-removal animation to finish
// if one would launch.
void RemoveDesk(const Desk* desk);
} // namespace ash } // namespace ash
#endif // ASH_WM_DESKS_DESKS_TEST_UTIL_H_ #endif // ASH_WM_DESKS_DESKS_TEST_UTIL_H_
...@@ -55,10 +55,6 @@ void NewDesk() { ...@@ -55,10 +55,6 @@ void NewDesk() {
DesksController::Get()->NewDesk(DesksCreationRemovalSource::kButton); DesksController::Get()->NewDesk(DesksCreationRemovalSource::kButton);
} }
void RemoveDesk(const Desk* desk) {
DesksController::Get()->RemoveDesk(desk, DesksCreationRemovalSource::kButton);
}
std::unique_ptr<aura::Window> CreateTransientWindow( std::unique_ptr<aura::Window> CreateTransientWindow(
aura::Window* transient_parent, aura::Window* transient_parent,
const gfx::Rect& bounds) { const gfx::Rect& bounds) {
...@@ -2024,7 +2020,9 @@ TEST_F(DesksAcceleratorsTest, RemoveDesk) { ...@@ -2024,7 +2020,9 @@ TEST_F(DesksAcceleratorsTest, RemoveDesk) {
Desk* desk_3 = controller->desks()[2].get(); Desk* desk_3 = controller->desks()[2].get();
EXPECT_TRUE(desk_1->is_active()); EXPECT_TRUE(desk_1->is_active());
const int flags = ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN; const int flags = ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN;
DeskSwitchAnimationWaiter waiter;
SendAccelerator(ui::VKEY_OEM_MINUS, flags); SendAccelerator(ui::VKEY_OEM_MINUS, flags);
waiter.Wait();
ASSERT_EQ(2u, controller->desks().size()); ASSERT_EQ(2u, controller->desks().size());
EXPECT_TRUE(desk_2->is_active()); EXPECT_TRUE(desk_2->is_active());
...@@ -2038,6 +2036,35 @@ TEST_F(DesksAcceleratorsTest, RemoveDesk) { ...@@ -2038,6 +2036,35 @@ TEST_F(DesksAcceleratorsTest, RemoveDesk) {
EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_TRUE(overview_controller->InOverviewSession());
} }
TEST_F(DesksAcceleratorsTest, RemoveRightmostDesk) {
auto* controller = DesksController::Get();
// Create a few desks and remove them outside and inside overview using the
// shortcut.
NewDesk();
NewDesk();
ASSERT_EQ(3u, controller->desks().size());
Desk* desk_1 = controller->desks()[0].get();
Desk* desk_2 = controller->desks()[1].get();
Desk* desk_3 = controller->desks()[2].get();
ActivateDesk(desk_3);
EXPECT_TRUE(desk_3->is_active());
const int flags = ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN;
{
DeskSwitchAnimationWaiter waiter;
SendAccelerator(ui::VKEY_OEM_MINUS, flags);
waiter.Wait();
}
ASSERT_EQ(2u, controller->desks().size());
EXPECT_TRUE(desk_2->is_active());
{
DeskSwitchAnimationWaiter waiter;
SendAccelerator(ui::VKEY_OEM_MINUS, flags);
waiter.Wait();
}
ASSERT_EQ(1u, controller->desks().size());
EXPECT_TRUE(desk_1->is_active());
}
TEST_F(DesksAcceleratorsTest, LeftRightDeskActivation) { TEST_F(DesksAcceleratorsTest, LeftRightDeskActivation) {
auto* controller = DesksController::Get(); auto* controller = DesksController::Get();
NewDesk(); NewDesk();
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "ash/wm/desks/root_window_desk_switch_animator.h" #include "ash/wm/desks/root_window_desk_switch_animator.h"
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_controller.h"
#include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/frame_sinks/copy_output_result.h"
#include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2.h"
...@@ -14,6 +16,7 @@ ...@@ -14,6 +16,7 @@
#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_tree_owner.h" #include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/wm/core/window_util.h"
namespace ash { namespace ash {
...@@ -30,6 +33,12 @@ constexpr int kMaxScreenshotRetries = 2; ...@@ -30,6 +33,12 @@ constexpr int kMaxScreenshotRetries = 2;
constexpr base::TimeDelta kAnimationDuration = constexpr base::TimeDelta kAnimationDuration =
base::TimeDelta::FromMilliseconds(300); base::TimeDelta::FromMilliseconds(300);
// The amount, by which the detached old layers of the removed desk's windows,
// is translated vertically during the for-remove desk switch animation.
constexpr int kRemovedDeskWindowYTranslation = 20;
constexpr base::TimeDelta kRemovedDeskWindowTranslationDuration =
base::TimeDelta::FromMilliseconds(100);
// Create the layer that will be the parent of the screenshot layer, with a // Create the layer that will be the parent of the screenshot layer, with a
// solid black color to act as the background showing behind the two // solid black color to act as the background showing behind the two
// screenshot layers in the |kDesksSpacing| region between them. // screenshot layers in the |kDesksSpacing| region between them.
...@@ -100,14 +109,18 @@ RootWindowDeskSwitchAnimator::RootWindowDeskSwitchAnimator( ...@@ -100,14 +109,18 @@ RootWindowDeskSwitchAnimator::RootWindowDeskSwitchAnimator(
aura::Window* root, aura::Window* root,
const Desk* ending_desk, const Desk* ending_desk,
Delegate* delegate, Delegate* delegate,
bool move_left) bool move_left,
bool for_remove)
: root_window_(root), : root_window_(root),
starting_desk_(DesksController::Get()->active_desk()),
ending_desk_(ending_desk), ending_desk_(ending_desk),
delegate_(delegate), delegate_(delegate),
animation_layer_owner_(CreateAnimationLayerOwner(root)), animation_layer_owner_(CreateAnimationLayerOwner(root)),
x_translation_offset_(root->layer()->size().width() + kDesksSpacing), x_translation_offset_(root->layer()->size().width() + kDesksSpacing),
move_left_(move_left) { move_left_(move_left),
for_remove_(for_remove) {
DCHECK(root_window_); DCHECK(root_window_);
DCHECK(starting_desk_);
DCHECK(ending_desk_); DCHECK(ending_desk_);
DCHECK(delegate_); DCHECK(delegate_);
} }
...@@ -122,6 +135,23 @@ RootWindowDeskSwitchAnimator::~RootWindowDeskSwitchAnimator() { ...@@ -122,6 +135,23 @@ RootWindowDeskSwitchAnimator::~RootWindowDeskSwitchAnimator() {
} }
void RootWindowDeskSwitchAnimator::TakeStartingDeskScreenshot() { void RootWindowDeskSwitchAnimator::TakeStartingDeskScreenshot() {
if (for_remove_) {
// The active desk is about to be removed. Recreate and detach its old
// layers to animate them in a jump-like animation.
auto* desk_container =
starting_desk_->GetDeskContainerForRoot(root_window_);
old_windows_layer_tree_owner_ = wm::RecreateLayers(desk_container);
root_window_->layer()->Add(old_windows_layer_tree_owner_->root());
root_window_->layer()->StackAtTop(old_windows_layer_tree_owner_->root());
// We don't take a screenshot of the soon-to-be-removed desk, we use an
// empty black solid color layer.
auto black_layer = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
black_layer->SetColor(SK_ColorBLACK);
CompleteAnimationPhase1WithLayer(std::move(black_layer));
return;
}
TakeScreenshot( TakeScreenshot(
root_window_, root_window_,
base::BindOnce( base::BindOnce(
...@@ -179,6 +209,22 @@ void RootWindowDeskSwitchAnimator::StartAnimation() { ...@@ -179,6 +209,22 @@ void RootWindowDeskSwitchAnimator::StartAnimation() {
settings.SetTransitionDuration(kAnimationDuration); settings.SetTransitionDuration(kAnimationDuration);
settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN); settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
animation_layer->SetTransform(animation_layer_ending_transfrom); animation_layer->SetTransform(animation_layer_ending_transfrom);
if (for_remove_) {
DCHECK(old_windows_layer_tree_owner_);
auto* old_windows_layer = old_windows_layer_tree_owner_->root();
DCHECK(old_windows_layer);
// Translate the old layers of removed desk's windows back down by
// `kRemovedDeskWindowYTranslation`.
gfx::Transform transform = old_windows_layer->GetTargetTransform();
ui::ScopedLayerAnimationSettings settings(old_windows_layer->GetAnimator());
settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
settings.SetTransitionDuration(kRemovedDeskWindowTranslationDuration);
settings.SetTweenType(gfx::Tween::EASE_IN);
transform.Translate(0, kRemovedDeskWindowYTranslation);
old_windows_layer->SetTransform(transform);
}
} }
void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() { void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() {
...@@ -188,27 +234,11 @@ void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() { ...@@ -188,27 +234,11 @@ void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() {
delegate_->OnDeskSwitchAnimationFinished(); delegate_->OnDeskSwitchAnimationFinished();
} }
void RootWindowDeskSwitchAnimator::OnStartingDeskScreenshotTaken( void RootWindowDeskSwitchAnimator::CompleteAnimationPhase1WithLayer(
std::unique_ptr<viz::CopyOutputResult> copy_result) { std::unique_ptr<ui::Layer> layer) {
if (!copy_result || copy_result->IsEmpty()) { DCHECK(layer);
// A frame may be activated before the screenshot requests are satisfied,
// leading to us getting an empty |result|. Rerequest the screenshot.
// (See viz::Surface::ActivateFrame()).
if (++starting_desk_screenshot_retries_ <= kMaxScreenshotRetries) {
TakeStartingDeskScreenshot();
} else {
LOG(ERROR) << "Received multiple empty screenshots of the starting desk.";
NOTREACHED();
starting_desk_screenshot_taken_ = true;
delegate_->OnStartingDeskScreenshotTaken(ending_desk_);
}
return;
}
ui::Layer* starting_desk_screenshot_layer =
CreateLayerFromScreenshotResult(std::move(copy_result)).release();
ui::Layer* starting_desk_screenshot_layer = layer.release();
gfx::Rect screenshot_bounds(root_window_->layer()->size()); gfx::Rect screenshot_bounds(root_window_->layer()->size());
gfx::Transform animation_layer_starting_transfrom; gfx::Transform animation_layer_starting_transfrom;
...@@ -251,12 +281,53 @@ void RootWindowDeskSwitchAnimator::OnStartingDeskScreenshotTaken( ...@@ -251,12 +281,53 @@ void RootWindowDeskSwitchAnimator::OnStartingDeskScreenshotTaken(
// etc.) are not visible to the user. // etc.) are not visible to the user.
auto* root_layer = root_window_->layer(); auto* root_layer = root_window_->layer();
root_layer->Add(animation_layer); root_layer->Add(animation_layer);
root_layer->StackAtTop(animation_layer);
if (for_remove_) {
DCHECK(old_windows_layer_tree_owner_);
auto* old_windows_layer = old_windows_layer_tree_owner_->root();
DCHECK(old_windows_layer);
root_layer->StackBelow(animation_layer, old_windows_layer);
// Translate the old layers of the removed desk's windows up by
// `kRemovedDeskWindowYTranslation`.
gfx::Transform transform = old_windows_layer->GetTargetTransform();
ui::ScopedLayerAnimationSettings settings(old_windows_layer->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(kRemovedDeskWindowTranslationDuration);
settings.SetTweenType(gfx::Tween::EASE_OUT);
transform.Translate(0, -kRemovedDeskWindowYTranslation);
old_windows_layer->SetTransform(transform);
} else {
root_layer->StackAtTop(animation_layer);
}
starting_desk_screenshot_taken_ = true; starting_desk_screenshot_taken_ = true;
delegate_->OnStartingDeskScreenshotTaken(ending_desk_); delegate_->OnStartingDeskScreenshotTaken(ending_desk_);
} }
void RootWindowDeskSwitchAnimator::OnStartingDeskScreenshotTaken(
std::unique_ptr<viz::CopyOutputResult> copy_result) {
if (!copy_result || copy_result->IsEmpty()) {
// A frame may be activated before the screenshot requests are satisfied,
// leading to us getting an empty |result|. Rerequest the screenshot.
// (See viz::Surface::ActivateFrame()).
if (++starting_desk_screenshot_retries_ <= kMaxScreenshotRetries) {
TakeStartingDeskScreenshot();
} else {
LOG(ERROR) << "Received multiple empty screenshots of the starting desk.";
NOTREACHED();
starting_desk_screenshot_taken_ = true;
delegate_->OnStartingDeskScreenshotTaken(ending_desk_);
}
return;
}
CompleteAnimationPhase1WithLayer(
CreateLayerFromScreenshotResult(std::move(copy_result)));
}
void RootWindowDeskSwitchAnimator::OnEndingDeskScreenshotTaken( void RootWindowDeskSwitchAnimator::OnEndingDeskScreenshotTaken(
std::unique_ptr<viz::CopyOutputResult> copy_result) { std::unique_ptr<viz::CopyOutputResult> copy_result) {
if (!copy_result || copy_result->IsEmpty()) { if (!copy_result || copy_result->IsEmpty()) {
......
...@@ -17,6 +17,7 @@ class Window; ...@@ -17,6 +17,7 @@ class Window;
namespace ui { namespace ui {
class LayerTreeOwner; class LayerTreeOwner;
class Layer;
} // namespace ui } // namespace ui
namespace viz { namespace viz {
...@@ -129,7 +130,8 @@ class Desk; ...@@ -129,7 +130,8 @@ class Desk;
// be visible again. // be visible again.
// //
// This cooperative interaction between the animators and their owner // This cooperative interaction between the animators and their owner
// (DesksController) is needed for the following reasons: // (DesksController::AbstractDeskSwitchAnimation) is needed for the following
// reasons:
// 1- The new desk is only activated after all starting desk screenshots on all // 1- The new desk is only activated after all starting desk screenshots on all
// roots have been taken and placed on top of everything (between phase (1) // roots have been taken and placed on top of everything (between phase (1)
// and (2)), so that the effects of desk activation (windows hiding and // and (2)), so that the effects of desk activation (windows hiding and
...@@ -138,6 +140,20 @@ class Desk; ...@@ -138,6 +140,20 @@ class Desk;
// root windows are ready (between phase (2) and (3)). This is needed to // root windows are ready (between phase (2) and (3)). This is needed to
// synchronize the animations on all displays together (otherwise the // synchronize the animations on all displays together (otherwise the
// animations will lag behind each other). // animations will lag behind each other).
//
// When this animator is used to implement the remove-active-desk animation
// (which also involves switching desks; from the to-be-removed desk to another
// desk), `for_remove` is set to true in the constructor. The animation is
// slightly tweaked to do the following:
// - Instead of taking a screenshot of the starting desk, we replace it by a
// black solid color layer, to indicate the desk is being removed.
// - The layer tree of the active-desk container is recreated, and the old
// layers are detached and animated vertically by
// `kRemovedDeskWindowYTranslation`.
// - That old layer tree is then translated back down by the same amount while
// the desks screenshots are animating horizontally.
// This gives the effect that the removed desk windows are jumping from their
// desk to the target desk.
class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
public: public:
class Delegate { class Delegate {
...@@ -162,7 +178,8 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -162,7 +178,8 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
RootWindowDeskSwitchAnimator(aura::Window* root, RootWindowDeskSwitchAnimator(aura::Window* root,
const Desk* ending_desk, const Desk* ending_desk,
Delegate* delegate, Delegate* delegate,
bool move_left); bool move_left,
bool for_remove);
~RootWindowDeskSwitchAnimator() override; ~RootWindowDeskSwitchAnimator() override;
...@@ -174,7 +191,7 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -174,7 +191,7 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
} }
bool animation_finished() const { return animation_finished_; } bool animation_finished() const { return animation_finished_; }
// Begins pahse (1) of the animation by taking a screenshot of the starting // Begins phase (1) of the animation by taking a screenshot of the starting
// desk content. Delegate::OnStartingDeskScreenshotTaken() will be called once // desk content. Delegate::OnStartingDeskScreenshotTaken() will be called once
// the screenshot is taken and placed on top of everything on the screen. // the screenshot is taken and placed on top of everything on the screen.
void TakeStartingDeskScreenshot(); void TakeStartingDeskScreenshot();
...@@ -195,6 +212,15 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -195,6 +212,15 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
void OnImplicitAnimationsCompleted() override; void OnImplicitAnimationsCompleted() override;
private: private:
// Completes the first phase of the animation using the given |layer| as the
// screenshot layer of the starting desk. This layer will be parented to the
// animation layer, which will be setup with its initial transform according
// to |move_left|. If |for_remove_| is true, the detached old layer tree of
// the soon-to-be-removed-desk's windows will be translated up vertically to
// simulate a jump from the removed desk to the target desk.
// |Delegate::OnStartingDeskScreenshotTaken()| will be called at the end.
void CompleteAnimationPhase1WithLayer(std::unique_ptr<ui::Layer> layer);
void OnStartingDeskScreenshotTaken( void OnStartingDeskScreenshotTaken(
std::unique_ptr<viz::CopyOutputResult> copy_result); std::unique_ptr<viz::CopyOutputResult> copy_result);
void OnEndingDeskScreenshotTaken( void OnEndingDeskScreenshotTaken(
...@@ -203,11 +229,20 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -203,11 +229,20 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
// The root window that this animator is associated with. // The root window that this animator is associated with.
aura::Window* const root_window_; aura::Window* const root_window_;
// The active desk at the start of the animation.
const Desk* const starting_desk_;
// The desk to activate and animate to with this animator. // The desk to activate and animate to with this animator.
const Desk* const ending_desk_; const Desk* const ending_desk_;
Delegate* const delegate_; Delegate* const delegate_;
// The owner of the layer tree of the old detached layers of the removed
// desk's windows. This is only valid if |for_remove_| is true. This layer
// tree is animated to simulate that the windows are jumping from the removed
// desk to the target desk.
std::unique_ptr<ui::LayerTreeOwner> old_windows_layer_tree_owner_;
// The owner of the layer tree that contains the parent "animation layer" and // The owner of the layer tree that contains the parent "animation layer" and
// both its child starting and ending desks "screenshot layers". // both its child starting and ending desks "screenshot layers".
std::unique_ptr<ui::LayerTreeOwner> animation_layer_owner_; std::unique_ptr<ui::LayerTreeOwner> animation_layer_owner_;
...@@ -226,6 +261,9 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -226,6 +261,9 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
// means the starting desk is on the left of the ending desk. // means the starting desk is on the left of the ending desk.
const bool move_left_; const bool move_left_;
// True if this animator is handling the remove-active-desk animation.
const bool for_remove_;
// True when phase (1) finishes. // True when phase (1) finishes.
bool starting_desk_screenshot_taken_ = false; bool starting_desk_screenshot_taken_ = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment