Chromium Code Reviews| Index: ash/wm/pinned_controller.cc |
| diff --git a/ash/wm/pinned_controller.cc b/ash/wm/pinned_controller.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dacb2ba58831a008ca1bca752472fff008eb643d |
| --- /dev/null |
| +++ b/ash/wm/pinned_controller.cc |
| @@ -0,0 +1,239 @@ |
| +// 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 <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_window.h" |
| +#include "ash/shell.h" |
| +#include "ash/wm/dim_window.h" |
| +#include "ash/wm/pinned_controller.h" |
| +#include "base/auto_reset.h" |
| +#include "base/logging.h" |
| + |
| +namespace ash { |
| +namespace wm { |
| +namespace { |
| + |
| +WmWindow* CreateDimWindow(WmWindow* container) { |
| + DimWindow* dim_window = new DimWindow(WmWindowAura::GetAuraWindow(container)); |
| + dim_window->SetDimOpacity(1); // Set whole black. |
| + dim_window->Show(); |
| + WmWindow* result = WmWindowAura::Get(dim_window); |
| + result->SetFullscreen(); |
| + 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(); |
| + |
| + ash::Shell* shell = ash::Shell::GetInstance(); |
| + std::vector<WmWindow*> result; |
| + for (WmWindow* system_modal : |
| + WmWindowAura::FromAuraWindows(shell->GetContainersFromAllRootWindows( |
| + kShellWindowId_SystemModalContainer, nullptr))) { |
| + if (system_modal->GetRootWindow() == pinned_root) |
| + continue; |
| + result.push_back(system_modal); |
| + } |
| + return result; |
| +} |
| + |
| +template <typename T> |
| +bool Contains(const std::vector<T>& container, const T& target) { |
| + return std::find(container.begin(), container.end(), target) != |
| + container.end(); |
| +} |
|
oshima
2016/06/17 10:26:54
use ContainsValue in stl_util.h
hidehiko
2016/06/17 17:19:22
Good to know. Done.
|
| + |
| +} // namespace |
| + |
| +PinnedController::PinnedController() = default; |
| +PinnedController::~PinnedController() = default; |
| + |
| +bool PinnedController::IsPinned() const { |
| + return pinned_window_ != nullptr; |
| +} |
| + |
| +void PinnedController::OnPinnedStateChanged(WmWindow* pinned_window) { |
| + if (pinned_window->GetWindowState()->IsPinned()) { |
| + if (pinned_window_) { |
| + LOG(ERROR) << "Pinned mode is enabled, while it is already in " |
| + << "the pinned mode"; |
|
oshima
2016/06/17 10:26:54
can this happen? If not, DCHECK
hidehiko
2016/06/17 17:19:22
Replaced by LOG(DFATAL).
|
| + 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); |
|
oshima
2016/06/17 10:26:54
Another option is to create a layer above, and use
hidehiko
2016/06/17 17:19:22
Can we keep it?
The window can have a transparent
oshima
2016/06/18 04:47:44
It'll be visually same. It's not a big deal so if
|
| + |
| + // 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->StackChildAtTop(dim_window); |
|
oshima
2016/06/17 10:26:54
This may hide existing modal window if any.
hidehiko
2016/06/17 17:19:22
Good catch. As we decided not to hide system modal
|
| + dim_windows_.push_back(dim_window); |
| + } |
|
oshima
2016/06/17 10:26:54
bye the way, we can't (and shouldn't) suppress scr
hidehiko
2016/06/17 17:19:22
No, as it is consistent with the referenced behavi
oshima
2016/06/18 04:47:44
Acknowledged.
|
| + |
| + // Set observers. |
| + container->AddObserver(this); |
| + for (WmWindow* child : container->GetChildren()) |
| + child->AddObserver(this); |
| + for (WmWindow* dim_window : dim_windows_) |
| + dim_window->AddObserver(this); |
| + for (WmWindow* system_modal : system_modal_containers) |
| + system_modal->AddObserver(this); |
| + } else { |
| + if (pinned_window != pinned_window_) { |
| + LOG(ERROR) << "Pinned mode is being disabled, but for the different " |
| + << "target window."; |
|
oshima
2016/06/17 10:26:54
ditto
hidehiko
2016/06/17 17:19:22
Done.
|
| + return; |
| + } |
| + |
| + WmWindow* container = pinned_window->GetParent(); |
| + std::vector<WmWindow*> system_modal_containers = |
| + GetSystemModalWindowsExceptPinned(pinned_window_); |
| + |
| + // Unset observers. |
| + for (WmWindow* system_modal : system_modal_containers) |
| + system_modal->RemoveObserver(this); |
| + for (WmWindow* dim_window : dim_windows_) |
| + dim_window->RemoveObserver(this); |
| + for (WmWindow* child : container->GetChildren()) |
| + child->RemoveObserver(this); |
| + container->RemoveObserver(this); |
| + |
| + // Delete the dim windows. This works, but do not use RAII, because |
| + // the window is owned by the parent. |
| + for (WmWindow* dim_window : dim_windows_) { |
| + dim_window->RemoveObserver(this); |
| + delete WmWindowAura::GetAuraWindow(dim_window); |
| + } |
| + dim_windows_.clear(); |
| + delete WmWindowAura::GetAuraWindow(background_window_); |
| + background_window_ = nullptr; |
| + |
| + pinned_window_ = nullptr; |
| + } |
| +} |
| + |
| +void PinnedController::OnWindowTreeChanging(WmWindow* window, |
| + const TreeChangeParams& params) { |
| + DCHECK(IsPinned()); |
| + DCHECK(params.target != pinned_window_); |
| + DCHECK(params.target != background_window_); |
| + DCHECK(!Contains(dim_windows_, params.target)); |
| + |
| + // Here, only events to remove a window directly from a container should be |
| + // handled. |
| + if ((window != pinned_window_->GetParent() && |
| + window->GetShellWindowId() != kShellWindowId_SystemModalContainer) || |
| + window != params.old_parent) |
| + return; |
| + |
| + // If a window is being removed from the parent, stop observing it. |
|
oshima
2016/06/17 10:56:51
new_parent == nullptr is removal.
I actually wond
hidehiko
2016/06/17 17:19:22
Good to know. I'm tempted to use it, but it is a s
oshima
2016/06/18 04:47:44
You can use it for this type of feature too. Good
hidehiko
2016/06/18 05:32:45
Yes, that is why I was actually tempted to use.
|
| + params.target->RemoveObserver(this); |
| +} |
| + |
| +void PinnedController::OnWindowTreeChanged(WmWindow* window, |
| + const TreeChangeParams& params) { |
| + DCHECK(IsPinned()); |
| + DCHECK(params.target != pinned_window_); |
| + DCHECK(params.target != background_window_); |
| + DCHECK(!Contains(dim_windows_, params.target)); |
| + |
| + // Here, only events to add a window directly into a container should be |
| + // handled. |
|
oshima
2016/06/17 10:56:51
What if someone add a window to AlwaysOnTop?
hidehiko
2016/06/17 17:19:22
Good catch. It should be handled by WorkspaceLayou
|
| + if ((window != pinned_window_->GetParent() && |
| + window->GetShellWindowId() != kShellWindowId_SystemModalContainer) || |
| + window != params.new_parent) |
| + return; |
| + |
| + // Observe the window to see the stacking change. |
| + // Also, keep the pinned window at the top. |
| + params.target->AddObserver(this); |
| + if (window == pinned_window_->GetParent()) |
| + KeepPinnedWindowOnTop(); |
| + else |
| + KeepDimWindowOnTop(window); |
| +} |
| + |
| +void PinnedController::OnWindowStackingChanged(WmWindow* window) { |
| + DCHECK(IsPinned()); |
| + |
| + if (in_restacking_) |
| + return; |
| + |
| + if (window->GetParent() == pinned_window_->GetParent()) |
| + KeepPinnedWindowOnTop(); |
| + else if (window->GetParent()->GetShellWindowId() == |
| + kShellWindowId_SystemModalContainer) |
| + KeepDimWindowOnTop(window->GetParent()); |
| +} |
| + |
| +void PinnedController::OnWindowDestroying(WmWindow* window) { |
|
oshima
2016/06/17 10:56:51
or you can listen to OnWndowAdded/Removed on conta
hidehiko
2016/06/17 17:19:22
Done. Though it needs to fully change the callback
|
| + DCHECK(IsPinned()); |
| + |
| + if (window == pinned_window_) { |
| + pinned_window_->GetWindowState()->Restore(); |
| + } else if (window == background_window_) { |
| + background_window_ = nullptr; |
| + } else if (Contains(dim_windows_, window)) { |
| + dim_windows_.erase( |
| + std::find(dim_windows_.begin(), dim_windows_.end(), window)); |
| + } |
| +} |
| + |
| +void PinnedController::OnDisplayConfigurationChanged() { |
| + // Note: this is called on display attached or detached. |
| + if (!IsPinned()) |
| + return; |
| + |
| + std::vector<WmWindow*> system_modal_containers = |
| + GetSystemModalWindowsExceptPinned(pinned_window_); |
| + for (WmWindow* system_modal : system_modal_containers) { |
| + if (Contains(dim_windows_, system_modal->GetChildren().back())) |
| + continue; |
| + WmWindow* dim_window = CreateDimWindow(system_modal); |
| + system_modal->StackChildAtTop(dim_window); |
| + dim_windows_.push_back(dim_window); |
| + system_modal->AddObserver(this); |
| + dim_window->AddObserver(this); |
| + } |
| +} |
| + |
| +void PinnedController::KeepPinnedWindowOnTop() { |
| + DCHECK(!in_restacking_); |
| + |
| + base::AutoReset<bool> auto_reset(&in_restacking_, true); |
| + WmWindow* container = pinned_window_->GetParent(); |
| + container->StackChildAtTop(pinned_window_); |
| + container->StackChildBelow(background_window_, pinned_window_); |
| +} |
| + |
| +void PinnedController::KeepDimWindowOnTop(WmWindow* container) { |
| + DCHECK(!in_restacking_); |
| + |
| + base::AutoReset<bool> auto_reset(&in_restacking_, true); |
| + for (WmWindow* dim_window : dim_windows_) { |
| + if (dim_window->GetParent() == container) { |
| + container->StackChildAtTop(dim_window); |
| + break; |
| + } |
| + } |
| +} |
| + |
| +} // namespace wm |
| +} // namespace ash |