| Index: ash/wm/screen_pinning_controller.cc
|
| diff --git a/ash/wm/screen_pinning_controller.cc b/ash/wm/screen_pinning_controller.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2cb568d9e52a385d5476cb3c69adc2838ba5103d
|
| --- /dev/null
|
| +++ b/ash/wm/screen_pinning_controller.cc
|
| @@ -0,0 +1,403 @@
|
| +// Copyright 2016 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/screen_pinning_controller.h"
|
| +
|
| +#include <algorithm>
|
| +#include <vector>
|
| +
|
| +#include "ash/aura/wm_window_aura.h"
|
| +#include "ash/common/shell_window_ids.h"
|
| +#include "ash/common/wm/window_state.h"
|
| +#include "ash/common/wm_shell_common.h"
|
| +#include "ash/common/wm_window.h"
|
| +#include "ash/common/wm_window_observer.h"
|
| +#include "ash/display/window_tree_host_manager.h"
|
| +#include "ash/shell.h"
|
| +#include "ash/wm/dim_window.h"
|
| +#include "base/auto_reset.h"
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| +#include "ui/aura/window_observer.h"
|
| +
|
| +namespace ash {
|
| +namespace {
|
| +
|
| +WmWindow* CreateDimWindow(WmWindow* container) {
|
| + DimWindow* dim_window = new DimWindow(WmWindowAura::GetAuraWindow(container));
|
| + dim_window->SetDimOpacity(1); // Set whole black.
|
| + WmWindow* result = WmWindowAura::Get(dim_window);
|
| + result->SetFullscreen();
|
| + result->Show();
|
| + return result;
|
| +}
|
| +
|
| +// Returns a list of WmWindows corresponding to SystemModalContainers,
|
| +// except ones whose root is shared with |pinned_window|.
|
| +std::vector<WmWindow*> GetSystemModalWindowsExceptPinned(
|
| + WmWindow* pinned_window) {
|
| + WmWindow* pinned_root = pinned_window->GetRootWindow();
|
| +
|
| + std::vector<WmWindow*> result;
|
| + for (WmWindow* system_modal : WmWindowAura::FromAuraWindows(
|
| + ash::Shell::GetContainersFromAllRootWindows(
|
| + kShellWindowId_SystemModalContainer, nullptr))) {
|
| + if (system_modal->GetRootWindow() == pinned_root)
|
| + continue;
|
| + result.push_back(system_modal);
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +void AddObserverToChildren(WmWindow* container,
|
| + aura::WindowObserver* observer) {
|
| + for (WmWindow* child : container->GetChildren()) {
|
| + WmWindowAura::GetAuraWindow(child)->AddObserver(observer);
|
| + }
|
| +}
|
| +
|
| +void RemoveObserverFromChildren(WmWindow* container,
|
| + aura::WindowObserver* observer) {
|
| + for (WmWindow* child : container->GetChildren()) {
|
| + WmWindowAura::GetAuraWindow(child)->RemoveObserver(observer);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Adapter to fire OnPinnedContainerWindowStackingChanged().
|
| +class ScreenPinningController::PinnedContainerChildWindowObserver
|
| + : public aura::WindowObserver {
|
| + public:
|
| + explicit PinnedContainerChildWindowObserver(
|
| + ScreenPinningController* controller)
|
| + : controller_(controller) {}
|
| +
|
| + void OnWindowStackingChanged(aura::Window* window) override {
|
| + controller_->OnPinnedContainerWindowStackingChanged(
|
| + WmWindowAura::Get(window));
|
| + }
|
| +
|
| + private:
|
| + ScreenPinningController* controller_;
|
| + DISALLOW_COPY_AND_ASSIGN(PinnedContainerChildWindowObserver);
|
| +};
|
| +
|
| +// Adapter to translate OnWindowAdded/OnWillRemoveWindow for the container
|
| +// containing the pinned window, to the corresponding controller's methods.
|
| +class ScreenPinningController::PinnedContainerWindowObserver
|
| + : public aura::WindowObserver {
|
| + public:
|
| + explicit PinnedContainerWindowObserver(ScreenPinningController* controller)
|
| + : controller_(controller) {}
|
| +
|
| + void OnWindowAdded(aura::Window* new_window) override {
|
| + controller_->OnWindowAddedToPinnedContainer(WmWindowAura::Get(new_window));
|
| + }
|
| + void OnWillRemoveWindow(aura::Window* window) override {
|
| + controller_->OnWillRemoveWindowFromPinnedContainer(
|
| + WmWindowAura::Get(window));
|
| + }
|
| + void OnWindowDestroying(aura::Window* window) override {
|
| + // Just in case. There is nothing we can do here.
|
| + window->RemoveObserver(this);
|
| + }
|
| +
|
| + private:
|
| + ScreenPinningController* controller_;
|
| + DISALLOW_COPY_AND_ASSIGN(PinnedContainerWindowObserver);
|
| +};
|
| +
|
| +// Adapter to fire OnSystemModalContainerWindowStackingChanged().
|
| +class ScreenPinningController::SystemModalContainerChildWindowObserver
|
| + : public aura::WindowObserver {
|
| + public:
|
| + explicit SystemModalContainerChildWindowObserver(
|
| + ScreenPinningController* controller)
|
| + : controller_(controller) {}
|
| +
|
| + void OnWindowStackingChanged(aura::Window* window) override {
|
| + controller_->OnSystemModalContainerWindowStackingChanged(
|
| + WmWindowAura::Get(window));
|
| + }
|
| +
|
| + private:
|
| + ScreenPinningController* controller_;
|
| + DISALLOW_COPY_AND_ASSIGN(SystemModalContainerChildWindowObserver);
|
| +};
|
| +
|
| +// Adapter to translate OnWindowAdded/OnWillRemoveWindow for the
|
| +// SystemModalContainer to the corresponding controller's methods.
|
| +class ScreenPinningController::SystemModalContainerWindowObserver
|
| + : public aura::WindowObserver {
|
| + public:
|
| + explicit SystemModalContainerWindowObserver(
|
| + ScreenPinningController* controller)
|
| + : controller_(controller) {}
|
| +
|
| + void OnWindowAdded(aura::Window* new_window) override {
|
| + controller_->OnWindowAddedToSystemModalContainer(
|
| + WmWindowAura::Get(new_window));
|
| + }
|
| + void OnWillRemoveWindow(aura::Window* window) override {
|
| + controller_->OnWillRemoveWindowFromSystemModalContainer(
|
| + WmWindowAura::Get(window));
|
| + }
|
| + void OnWindowDestroying(aura::Window* window) override {
|
| + // Just in case. There is nothing we can do here.
|
| + window->RemoveObserver(this);
|
| + }
|
| +
|
| + private:
|
| + ScreenPinningController* controller_;
|
| + DISALLOW_COPY_AND_ASSIGN(SystemModalContainerWindowObserver);
|
| +};
|
| +
|
| +// Tracks DimWindow's life.
|
| +class ScreenPinningController::DimWindowObserver : public aura::WindowObserver {
|
| + public:
|
| + explicit DimWindowObserver(ScreenPinningController* controller)
|
| + : controller_(controller) {}
|
| +
|
| + void OnWindowParentChanged(aura::Window* window,
|
| + aura::Window* parent) override {
|
| + // In case of parent change, there is nothing we can do for that change.
|
| + // Also, it is unsafe to delete the moving window here, because it could
|
| + // cause SEGV. Instead, we just hide it.
|
| + // Note that, the window is still tracked by the ScreenPinningController,
|
| + // so that it should be deleted on "unpin", at least.
|
| + window->Hide();
|
| + }
|
| +
|
| + void OnWindowDestroying(aura::Window* window) override {
|
| + controller_->OnDimWindowDestroying(WmWindowAura::Get(window));
|
| + }
|
| +
|
| + private:
|
| + ScreenPinningController* controller_;
|
| + DISALLOW_COPY_AND_ASSIGN(DimWindowObserver);
|
| +};
|
| +
|
| +ScreenPinningController::ScreenPinningController(
|
| + WmShellCommon* wm_shell_common,
|
| + WindowTreeHostManager* window_tree_host_manager)
|
| + : wm_shell_common_(wm_shell_common),
|
| + window_tree_host_manager_(window_tree_host_manager),
|
| + pinned_container_window_observer_(
|
| + new PinnedContainerWindowObserver(this)),
|
| + pinned_container_child_window_observer_(
|
| + new PinnedContainerChildWindowObserver(this)),
|
| + system_modal_container_window_observer_(
|
| + new SystemModalContainerWindowObserver(this)),
|
| + system_modal_container_child_window_observer_(
|
| + new SystemModalContainerChildWindowObserver(this)),
|
| + dim_window_observer_(new DimWindowObserver(this)) {
|
| + window_tree_host_manager_->AddObserver(this);
|
| +}
|
| +
|
| +ScreenPinningController::~ScreenPinningController() {
|
| + window_tree_host_manager_->RemoveObserver(this);
|
| +}
|
| +
|
| +bool ScreenPinningController::IsPinned() const {
|
| + return pinned_window_ != nullptr;
|
| +}
|
| +
|
| +void ScreenPinningController::SetPinnedWindow(WmWindow* pinned_window) {
|
| + if (pinned_window->GetWindowState()->IsPinned()) {
|
| + if (pinned_window_) {
|
| + LOG(DFATAL) << "Pinned mode is enabled, while it is already in "
|
| + << "the pinned mode";
|
| + return;
|
| + }
|
| +
|
| + WmWindow* container = pinned_window->GetParent();
|
| + std::vector<WmWindow*> system_modal_containers =
|
| + GetSystemModalWindowsExceptPinned(pinned_window);
|
| +
|
| + // Set up the container which has the pinned window.
|
| + pinned_window_ = pinned_window;
|
| + background_window_ = CreateDimWindow(container);
|
| + container->StackChildAtTop(pinned_window);
|
| + container->StackChildBelow(background_window_, pinned_window);
|
| +
|
| + // Set the dim windows to the system containers, other than the one which
|
| + // the root window of the pinned window holds.
|
| + for (WmWindow* system_modal : system_modal_containers) {
|
| + WmWindow* dim_window = CreateDimWindow(system_modal);
|
| + system_modal->StackChildAtBottom(dim_window);
|
| + dim_windows_.push_back(dim_window);
|
| + WmWindowAura::GetAuraWindow(dim_window)
|
| + ->AddObserver(dim_window_observer_.get());
|
| + }
|
| +
|
| + // Set observers.
|
| + WmWindowAura::GetAuraWindow(container)->AddObserver(
|
| + pinned_container_window_observer_.get());
|
| + AddObserverToChildren(container,
|
| + pinned_container_child_window_observer_.get());
|
| + for (WmWindow* system_modal : system_modal_containers) {
|
| + WmWindowAura::GetAuraWindow(system_modal)
|
| + ->AddObserver(system_modal_container_window_observer_.get());
|
| + AddObserverToChildren(
|
| + system_modal, system_modal_container_child_window_observer_.get());
|
| + }
|
| + } else {
|
| + if (pinned_window != pinned_window_) {
|
| + LOG(DFATAL) << "Pinned mode is being disabled, but for the different "
|
| + << "target window.";
|
| + return;
|
| + }
|
| +
|
| + WmWindow* container = pinned_window->GetParent();
|
| + std::vector<WmWindow*> system_modal_containers =
|
| + GetSystemModalWindowsExceptPinned(pinned_window_);
|
| +
|
| + // Unset observers.
|
| + for (WmWindow* system_modal :
|
| + GetSystemModalWindowsExceptPinned(pinned_window_)) {
|
| + RemoveObserverFromChildren(
|
| + system_modal, system_modal_container_child_window_observer_.get());
|
| + WmWindowAura::GetAuraWindow(system_modal)
|
| + ->RemoveObserver(system_modal_container_window_observer_.get());
|
| + }
|
| + RemoveObserverFromChildren(container,
|
| + pinned_container_child_window_observer_.get());
|
| + WmWindowAura::GetAuraWindow(container)->RemoveObserver(
|
| + pinned_container_window_observer_.get());
|
| +
|
| + // Delete the dim windows. This works, but do not use RAII, because
|
| + // the window is owned by the parent.
|
| + // Note: dim_windows_ will be updated during the deletion. So, copy is
|
| + // needed.
|
| + for (WmWindow* dim_window : std::vector<WmWindow*>(dim_windows_)) {
|
| + delete WmWindowAura::GetAuraWindow(dim_window);
|
| + }
|
| + DCHECK(dim_windows_.empty());
|
| + delete WmWindowAura::GetAuraWindow(background_window_);
|
| + background_window_ = nullptr;
|
| +
|
| + pinned_window_ = nullptr;
|
| + }
|
| +
|
| + wm_shell_common_->NotifyPinnedStateChanged(pinned_window);
|
| +}
|
| +
|
| +void ScreenPinningController::OnWindowAddedToPinnedContainer(
|
| + WmWindow* new_window) {
|
| + KeepPinnedWindowOnTop();
|
| + WmWindowAura::GetAuraWindow(new_window)
|
| + ->AddObserver(pinned_container_child_window_observer_.get());
|
| +}
|
| +
|
| +void ScreenPinningController::OnWillRemoveWindowFromPinnedContainer(
|
| + WmWindow* window) {
|
| + WmWindowAura::GetAuraWindow(window)->RemoveObserver(
|
| + pinned_container_child_window_observer_.get());
|
| + if (window == pinned_window_) {
|
| + pinned_window_->GetWindowState()->Restore();
|
| + return;
|
| + }
|
| + if (window == background_window_) {
|
| + background_window_ = nullptr;
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void ScreenPinningController::OnPinnedContainerWindowStackingChanged(
|
| + WmWindow* window) {
|
| + KeepPinnedWindowOnTop();
|
| +}
|
| +
|
| +void ScreenPinningController::OnWindowAddedToSystemModalContainer(
|
| + WmWindow* new_window) {
|
| + KeepDimWindowAtBottom(new_window->GetParent());
|
| + WmWindowAura::GetAuraWindow(new_window)
|
| + ->AddObserver(system_modal_container_child_window_observer_.get());
|
| +}
|
| +
|
| +void ScreenPinningController::OnWillRemoveWindowFromSystemModalContainer(
|
| + WmWindow* window) {
|
| + WmWindowAura::GetAuraWindow(window)->RemoveObserver(
|
| + system_modal_container_child_window_observer_.get());
|
| +}
|
| +
|
| +void ScreenPinningController::OnSystemModalContainerWindowStackingChanged(
|
| + WmWindow* window) {
|
| + KeepDimWindowAtBottom(window->GetParent());
|
| +}
|
| +
|
| +void ScreenPinningController::OnDimWindowDestroying(WmWindow* window) {
|
| + dim_windows_.erase(
|
| + std::find(dim_windows_.begin(), dim_windows_.end(), window));
|
| +}
|
| +
|
| +void ScreenPinningController::OnDisplayConfigurationChanged() {
|
| + // Note: this is called on display attached or detached.
|
| + if (!IsPinned())
|
| + return;
|
| +
|
| + // On display detaching, all necessary windows are transfered to the
|
| + // primary display's tree, and called this.
|
| + // So, delete the dim windows which are not a part of target system modal
|
| + // container.
|
| + // On display attaching, the new system modal container does not have the
|
| + // dim window. So create it.
|
| +
|
| + // First, delete unnecessary dim windows.
|
| + // The delete will update dim_windows_, so create the copy is needed.
|
| + for (WmWindow* dim_window : std::vector<WmWindow*>(dim_windows_)) {
|
| + if (!dim_window->GetTargetVisibility())
|
| + delete WmWindowAura::GetAuraWindow(dim_window);
|
| + }
|
| +
|
| + // Then, create missing dim_windows.
|
| + std::vector<WmWindow*> system_modal_containers =
|
| + GetSystemModalWindowsExceptPinned(pinned_window_);
|
| + for (WmWindow* system_modal : system_modal_containers) {
|
| + const std::vector<WmWindow*> children = system_modal->GetChildren();
|
| + if (!children.empty() && ContainsValue(dim_windows_, children[0])) {
|
| + // The system modal dialog has the dim window.
|
| + continue;
|
| + }
|
| +
|
| + // This is the new system modal dialog.
|
| + WmWindow* dim_window = CreateDimWindow(system_modal);
|
| + system_modal->StackChildAtBottom(dim_window);
|
| + dim_windows_.push_back(dim_window);
|
| + WmWindowAura::GetAuraWindow(dim_window)
|
| + ->AddObserver(dim_window_observer_.get());
|
| +
|
| + // Set observers to the tree.
|
| + WmWindowAura::GetAuraWindow(system_modal)
|
| + ->AddObserver(system_modal_container_window_observer_.get());
|
| + AddObserverToChildren(system_modal,
|
| + system_modal_container_child_window_observer_.get());
|
| + }
|
| +}
|
| +
|
| +void ScreenPinningController::KeepPinnedWindowOnTop() {
|
| + if (in_restacking_)
|
| + return;
|
| +
|
| + base::AutoReset<bool> auto_reset(&in_restacking_, true);
|
| + WmWindow* container = pinned_window_->GetParent();
|
| + container->StackChildAtTop(pinned_window_);
|
| + container->StackChildBelow(background_window_, pinned_window_);
|
| +}
|
| +
|
| +void ScreenPinningController::KeepDimWindowAtBottom(WmWindow* container) {
|
| + if (in_restacking_)
|
| + return;
|
| +
|
| + base::AutoReset<bool> auto_reset(&in_restacking_, true);
|
| + for (WmWindow* dim_window : dim_windows_) {
|
| + if (dim_window->GetParent() == container) {
|
| + container->StackChildAtBottom(dim_window);
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace ash
|
|
|