blob: 5c731d9e3a426d83141299351b5d280ceb383066 [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/wm/desks/desks_controller.h"
#include <utility>
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_util.h"
#include "base/logging.h"
namespace ash {
DesksController::DesksController() {
for (int id : desks_util::GetDesksContainersIds())
available_container_ids_.push(id);
// There's always one default desk.
NewDesk();
active_desk_ = desks_.back().get();
active_desk_->Activate();
}
DesksController::~DesksController() = default;
// static
DesksController* DesksController::Get() {
return Shell::Get()->desks_controller();
}
void DesksController::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void DesksController::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool DesksController::CanCreateDesks() const {
// TODO(afakhry): Disable creating new desks in tablet mode.
return desks_.size() < desks_util::kMaxNumberOfDesks;
}
bool DesksController::CanRemoveDesks() const {
return desks_.size() > 1;
}
void DesksController::NewDesk() {
DCHECK(CanCreateDesks());
DCHECK(!available_container_ids_.empty());
desks_.emplace_back(std::make_unique<Desk>(available_container_ids_.front()));
available_container_ids_.pop();
for (auto& observer : observers_)
observer.OnDeskAdded(desks_.back().get());
}
void DesksController::RemoveDesk(const Desk* desk) {
DCHECK(CanRemoveDesks());
auto iter = std::find_if(
desks_.begin(), desks_.end(),
[desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
DCHECK(iter != desks_.end());
// 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());
// - 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.
if (removed_desk.get() != active_desk_) {
removed_desk->MoveWindowsToDesk(active_desk_);
} 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);
removed_desk->MoveWindowsToDesk(target_desk);
ActivateDesk(target_desk);
}
for (auto& observer : observers_)
observer.OnDeskRemoved(removed_desk.get());
available_container_ids_.push(removed_desk->container_id());
DCHECK_LE(available_container_ids_.size(), desks_util::kMaxNumberOfDesks);
}
void DesksController::ActivateDesk(const Desk* desk) {
DCHECK(HasDesk(desk));
if (desk == active_desk_)
return;
// Mark the new desk as active first, so that deactivating windows on the
// `old_active` desk do not activate other windows on the same desk. See
// `ash::IsWindowConsideredVisibleForActivation()`.
Desk* old_active = active_desk_;
active_desk_ = const_cast<Desk*>(desk);
// There should always be an active desk at any time.
DCHECK(old_active);
old_active->Deactivate();
active_desk_->Activate();
// TODO(afakhry): Do desk activation animation.
}
void DesksController::OnRootWindowAdded(aura::Window* root_window) {
for (auto& desk : desks_)
desk->OnRootWindowAdded(root_window);
}
void DesksController::OnRootWindowClosing(aura::Window* root_window) {
for (auto& desk : desks_)
desk->OnRootWindowClosing(root_window);
}
bool DesksController::HasDesk(const Desk* desk) const {
auto iter = std::find_if(
desks_.begin(), desks_.end(),
[desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
return iter != desks_.end();
}
} // namespace ash