blob: 22a6e7f7fd86e2605c68e3955c04eec728704a3f [file]
// 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/common/shelf/shelf_window_watcher.h"
#include <memory>
#include <utility>
#include "ash/common/shelf/shelf_constants.h"
#include "ash/common/shelf/shelf_model.h"
#include "ash/common/shelf/shelf_window_watcher_item_delegate.h"
#include "ash/common/wm/window_state.h"
#include "ash/common/wm_shell.h"
#include "ash/common/wm_window.h"
#include "ash/common/wm_window_property.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/image/image_skia.h"
namespace ash {
namespace {
// Update the ShelfItem from relevant window properties.
void UpdateShelfItemForWindow(ShelfItem* item, WmWindow* window) {
item->type = static_cast<ShelfItemType>(
window->GetIntProperty(WmWindowProperty::SHELF_ITEM_TYPE));
const int icon =
window->GetIntProperty(WmWindowProperty::SHELF_ICON_RESOURCE_ID);
if (icon != kInvalidImageResourceID)
item->image = *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(icon);
}
// Returns true if |window| has a ShelfItem added by ShelfWindowWatcher.
bool HasShelfItemForWindow(WmWindow* window) {
return window->GetIntProperty(WmWindowProperty::SHELF_ITEM_TYPE) !=
TYPE_UNDEFINED &&
window->GetIntProperty(WmWindowProperty::SHELF_ID) != kInvalidShelfID;
}
} // namespace
ShelfWindowWatcher::ContainerWindowObserver::ContainerWindowObserver(
ShelfWindowWatcher* window_watcher)
: window_watcher_(window_watcher) {}
ShelfWindowWatcher::ContainerWindowObserver::~ContainerWindowObserver() {}
void ShelfWindowWatcher::ContainerWindowObserver::OnWindowTreeChanged(
WmWindow* window,
const TreeChangeParams& params) {
if (!params.old_parent && params.new_parent &&
params.new_parent->GetShellWindowId() ==
kShellWindowId_DefaultContainer) {
// A new window was created in the default container.
window_watcher_->OnUserWindowAdded(params.target);
}
}
void ShelfWindowWatcher::ContainerWindowObserver::OnWindowDestroying(
WmWindow* window) {
window_watcher_->OnContainerWindowDestroying(window);
}
////////////////////////////////////////////////////////////////////////////////
ShelfWindowWatcher::UserWindowObserver::UserWindowObserver(
ShelfWindowWatcher* window_watcher)
: window_watcher_(window_watcher) {}
ShelfWindowWatcher::UserWindowObserver::~UserWindowObserver() {}
void ShelfWindowWatcher::UserWindowObserver::OnWindowPropertyChanged(
WmWindow* window,
WmWindowProperty property) {
if (property == WmWindowProperty::SHELF_ITEM_TYPE ||
property == WmWindowProperty::SHELF_ICON_RESOURCE_ID) {
window_watcher_->OnUserWindowPropertyChanged(window);
}
}
void ShelfWindowWatcher::UserWindowObserver::OnWindowDestroying(
WmWindow* window) {
window_watcher_->OnUserWindowDestroying(window);
}
////////////////////////////////////////////////////////////////////////////////
ShelfWindowWatcher::ShelfWindowWatcher(ShelfModel* model)
: model_(model),
container_window_observer_(this),
user_window_observer_(this),
observed_container_windows_(&container_window_observer_),
observed_user_windows_(&user_window_observer_) {
WmShell::Get()->AddActivationObserver(this);
for (WmWindow* root : WmShell::Get()->GetAllRootWindows()) {
observed_container_windows_.Add(
root->GetChildByShellWindowId(kShellWindowId_DefaultContainer));
}
display::Screen::GetScreen()->AddObserver(this);
}
ShelfWindowWatcher::~ShelfWindowWatcher() {
display::Screen::GetScreen()->RemoveObserver(this);
WmShell::Get()->RemoveActivationObserver(this);
}
void ShelfWindowWatcher::AddShelfItem(WmWindow* window) {
ShelfItem item;
ShelfID id = model_->next_id();
item.status = window->IsActive() ? STATUS_ACTIVE : STATUS_RUNNING;
UpdateShelfItemForWindow(&item, window);
window->SetIntProperty(WmWindowProperty::SHELF_ID, id);
std::unique_ptr<ShelfItemDelegate> item_delegate(
new ShelfWindowWatcherItemDelegate(window));
model_->SetShelfItemDelegate(id, std::move(item_delegate));
model_->Add(item);
}
void ShelfWindowWatcher::RemoveShelfItem(WmWindow* window) {
int shelf_id = window->GetIntProperty(WmWindowProperty::SHELF_ID);
DCHECK_NE(shelf_id, kInvalidShelfID);
int index = model_->ItemIndexByID(shelf_id);
DCHECK_GE(index, 0);
model_->RemoveItemAt(index);
window->SetIntProperty(WmWindowProperty::SHELF_ID, kInvalidShelfID);
}
void ShelfWindowWatcher::OnContainerWindowDestroying(WmWindow* container) {
observed_container_windows_.Remove(container);
}
void ShelfWindowWatcher::UpdateShelfItemStatus(WmWindow* window,
bool is_active) {
int index = GetShelfItemIndexForWindow(window);
DCHECK_GE(index, 0);
ShelfItem item = model_->items()[index];
item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING;
model_->Set(index, item);
}
int ShelfWindowWatcher::GetShelfItemIndexForWindow(WmWindow* window) const {
return model_->ItemIndexByID(
window->GetIntProperty(WmWindowProperty::SHELF_ID));
}
void ShelfWindowWatcher::OnUserWindowAdded(WmWindow* window) {
// The window may already be tracked from when it was added to a different
// display or because an existing window added its shelf item properties.
if (observed_user_windows_.IsObserving(window))
return;
observed_user_windows_.Add(window);
// Add a ShelfItem if |window| has a valid ShelfItemType on creation.
if (window->GetIntProperty(WmWindowProperty::SHELF_ITEM_TYPE) !=
TYPE_UNDEFINED &&
window->GetIntProperty(WmWindowProperty::SHELF_ID) == kInvalidShelfID) {
AddShelfItem(window);
}
}
void ShelfWindowWatcher::OnUserWindowDestroying(WmWindow* window) {
if (observed_user_windows_.IsObserving(window))
observed_user_windows_.Remove(window);
if (HasShelfItemForWindow(window))
RemoveShelfItem(window);
}
void ShelfWindowWatcher::OnUserWindowPropertyChanged(WmWindow* window) {
if (window->GetIntProperty(WmWindowProperty::SHELF_ITEM_TYPE) ==
TYPE_UNDEFINED) {
// Removes ShelfItem for |window| when it has a ShelfItem.
if (window->GetIntProperty(WmWindowProperty::SHELF_ID) != kInvalidShelfID)
RemoveShelfItem(window);
return;
}
// Update an existing ShelfItem for |window| when a property has changed.
if (HasShelfItemForWindow(window)) {
int index = GetShelfItemIndexForWindow(window);
DCHECK_GE(index, 0);
ShelfItem item = model_->items()[index];
UpdateShelfItemForWindow(&item, window);
model_->Set(index, item);
return;
}
// Creates a new ShelfItem for |window|.
AddShelfItem(window);
}
void ShelfWindowWatcher::OnWindowActivated(WmWindow* gained_active,
WmWindow* lost_active) {
if (gained_active && HasShelfItemForWindow(gained_active))
UpdateShelfItemStatus(gained_active, true);
if (lost_active && HasShelfItemForWindow(lost_active))
UpdateShelfItemStatus(lost_active, false);
}
void ShelfWindowWatcher::OnDisplayAdded(const display::Display& new_display) {
WmWindow* root = WmShell::Get()->GetRootWindowForDisplayId(new_display.id());
// When the primary root window's display get removed, the existing root
// window is taken over by the new display and the observer is already set.
WmWindow* container =
root->GetChildByShellWindowId(kShellWindowId_DefaultContainer);
if (!observed_container_windows_.IsObserving(container))
observed_container_windows_.Add(container);
}
void ShelfWindowWatcher::OnDisplayRemoved(const display::Display& old_display) {
}
void ShelfWindowWatcher::OnDisplayMetricsChanged(const display::Display&,
uint32_t) {}
} // namespace ash