blob: 68a4addf363a441778d04a02aac357ca352d7fea [file] [log] [blame]
// 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