| // Copyright 2019 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/public/cpp/autotest_desks_api.h" |
| |
| #include "ash/shell.h" |
| #include "ash/wm/desks/desk.h" |
| #include "ash/wm/desks/desk_animation_base.h" |
| #include "ash/wm/desks/desks_controller.h" |
| #include "ash/wm/desks/desks_histogram_enums.h" |
| #include "ash/wm/desks/root_window_desk_switch_animator.h" |
| #include "base/callback.h" |
| #include "base/callback_forward.h" |
| #include "base/check.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_observer.h" |
| #include "ui/compositor/layer_animation_sequence.h" |
| #include "ui/compositor/layer_animator.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| class DeskAnimationObserver : public DesksController::Observer { |
| public: |
| DeskAnimationObserver(base::OnceClosure on_desk_animation_complete) |
| : on_desk_animation_complete_(std::move(on_desk_animation_complete)) { |
| DesksController::Get()->AddObserver(this); |
| } |
| |
| ~DeskAnimationObserver() override { |
| DesksController::Get()->RemoveObserver(this); |
| } |
| |
| DeskAnimationObserver(const DeskAnimationObserver& other) = delete; |
| DeskAnimationObserver& operator=(const DeskAnimationObserver& rhs) = delete; |
| |
| // DesksController::Observer: |
| void OnDeskAdded(const Desk* desk) override {} |
| void OnDeskRemoved(const Desk* desk) override {} |
| void OnDeskActivationChanged(const Desk* activated, |
| const Desk* deactivated) override {} |
| void OnDeskSwitchAnimationLaunching() override {} |
| void OnDeskSwitchAnimationFinished() override { |
| std::move(on_desk_animation_complete_).Run(); |
| delete this; |
| } |
| |
| private: |
| base::OnceClosure on_desk_animation_complete_; |
| }; |
| |
| // Self deleting desk animation observer which takes a target index and then |
| // waits until the animation layer has scheduled a new animation (ending |
| // screenshot will have been taken at this point). If the next desk is not the |
| // target desk, activate the adjacent desk (replacing the current animation). If |
| // the next desk is the target desk, wait until the animation is finished, then |
| // run the given callback. |
| class ChainedDeskAnimationObserver : public ui::LayerAnimationObserver, |
| public DesksController::Observer { |
| public: |
| ChainedDeskAnimationObserver(bool going_left, |
| int target_index, |
| base::OnceClosure on_desk_animation_complete) |
| : going_left_(going_left), |
| target_index_(target_index), |
| on_desk_animation_complete_(std::move(on_desk_animation_complete)) { |
| DesksController::Get()->AddObserver(this); |
| } |
| ChainedDeskAnimationObserver(const ChainedDeskAnimationObserver& other) = |
| delete; |
| ChainedDeskAnimationObserver& operator=( |
| const ChainedDeskAnimationObserver& rhs) = delete; |
| ~ChainedDeskAnimationObserver() override { |
| DesksController::Get()->RemoveObserver(this); |
| } |
| |
| // ui::LayerAnimationObserver: |
| void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {} |
| void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {} |
| void OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* sequence) override { |
| if ((sequence->properties() & ui::LayerAnimationElement::TRANSFORM) == 0) |
| return; |
| |
| auto* controller = DesksController::Get(); |
| const bool activated = controller->ActivateAdjacentDesk( |
| going_left_, DesksSwitchSource::kDeskSwitchShortcut); |
| DCHECK(activated); |
| |
| // If the animation goes to the last expected desk, remove the observer so |
| // that the next scheduled animation does not try activating another desk. |
| // We will then wait for the entire desk switch animation to finish and then |
| // run |on_desk_animation_complete_|. |
| if (controller->GetDeskIndex(controller->GetTargetActiveDesk()) == |
| target_index_) { |
| animation_layer_->GetAnimator()->RemoveObserver(this); |
| } |
| } |
| |
| // DesksController::Observer: |
| void OnDeskAdded(const Desk* desk) override {} |
| void OnDeskRemoved(const Desk* desk) override {} |
| void OnDeskActivationChanged(const Desk* activated, |
| const Desk* deactivated) override { |
| // The first activation changed happens when the initial ending screenshot |
| // is being taken. This is the first point when the animation layer we want |
| // to observe is guaranteed to be set as a child to the root window layer. |
| if (animation_layer_) |
| return; |
| |
| animation_layer_ = DesksController::Get() |
| ->GetAnimationForTesting() |
| ->GetFirstDeskSwitchAnimatorForTesting() |
| ->GetAnimationLayerForTesting(); |
| animation_layer_->GetAnimator()->AddObserver(this); |
| } |
| void OnDeskSwitchAnimationLaunching() override {} |
| void OnDeskSwitchAnimationFinished() override { |
| std::move(on_desk_animation_complete_).Run(); |
| delete this; |
| } |
| |
| private: |
| const bool going_left_; |
| const int target_index_; |
| base::OnceClosure on_desk_animation_complete_; |
| ui::Layer* animation_layer_ = nullptr; |
| }; |
| |
| } // namespace |
| |
| AutotestDesksApi::AutotestDesksApi() = default; |
| |
| AutotestDesksApi::~AutotestDesksApi() = default; |
| |
| bool AutotestDesksApi::CreateNewDesk() { |
| if (!DesksController::Get()->CanCreateDesks()) |
| return false; |
| |
| DesksController::Get()->NewDesk(DesksCreationRemovalSource::kButton); |
| return true; |
| } |
| |
| bool AutotestDesksApi::ActivateDeskAtIndex(int index, |
| base::OnceClosure on_complete) { |
| DCHECK(!on_complete.is_null()); |
| |
| if (index < 0) |
| return false; |
| |
| auto* controller = DesksController::Get(); |
| if (index >= int{controller->desks().size()}) |
| return false; |
| |
| const Desk* target_desk = controller->desks()[index].get(); |
| if (target_desk == controller->active_desk()) |
| return false; |
| |
| new DeskAnimationObserver(std::move(on_complete)); |
| controller->ActivateDesk(target_desk, DesksSwitchSource::kMiniViewButton); |
| return true; |
| } |
| |
| bool AutotestDesksApi::RemoveActiveDesk(base::OnceClosure on_complete) { |
| DCHECK(!on_complete.is_null()); |
| |
| auto* controller = DesksController::Get(); |
| if (!controller->CanRemoveDesks()) |
| return false; |
| |
| new DeskAnimationObserver(std::move(on_complete)); |
| controller->RemoveDesk(controller->active_desk(), |
| DesksCreationRemovalSource::kButton); |
| return true; |
| } |
| |
| bool AutotestDesksApi::ActivateAdjacentDesksToTargetIndex( |
| int index, |
| base::OnceClosure on_complete) { |
| DCHECK(!on_complete.is_null()); |
| |
| if (index < 0) |
| return false; |
| |
| auto* controller = DesksController::Get(); |
| if (index >= int{controller->desks().size()}) |
| return false; |
| |
| const Desk* target_desk = controller->desks()[index].get(); |
| if (target_desk == controller->active_desk()) |
| return false; |
| |
| int active_index = controller->GetDeskIndex(controller->active_desk()); |
| const bool going_left = index < active_index; |
| new ChainedDeskAnimationObserver(going_left, index, std::move(on_complete)); |
| const bool activated = controller->ActivateAdjacentDesk( |
| going_left, DesksSwitchSource::kDeskSwitchShortcut); |
| DCHECK(activated); |
| return true; |
| } |
| |
| } // namespace ash |