Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2557)

Unified Diff: ash/wm/pinned_controller.cc

Issue 2072853002: Implement "pinned" mode in ash. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix linux build breakage Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698