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 |