summaryrefslogtreecommitdiffstats
path: root/ui/views/corewm
diff options
context:
space:
mode:
Diffstat (limited to 'ui/views/corewm')
-rw-r--r--ui/views/corewm/base_focus_rules.cc7
-rw-r--r--ui/views/corewm/focus_controller.cc7
-rw-r--r--ui/views/corewm/shadow_controller.cc7
-rw-r--r--ui/views/corewm/shadow_controller_unittest.cc3
-rw-r--r--ui/views/corewm/transient_window_controller.cc42
-rw-r--r--ui/views/corewm/transient_window_controller.h38
-rw-r--r--ui/views/corewm/transient_window_manager.cc147
-rw-r--r--ui/views/corewm/transient_window_manager.h96
-rw-r--r--ui/views/corewm/transient_window_manager_unittest.cc577
-rw-r--r--ui/views/corewm/transient_window_stacking_client.cc56
-rw-r--r--ui/views/corewm/transient_window_stacking_client.h7
-rw-r--r--ui/views/corewm/transient_window_stacking_client_unittest.cc64
-rw-r--r--ui/views/corewm/window_modality_controller.cc16
-rw-r--r--ui/views/corewm/window_util.cc38
-rw-r--r--ui/views/corewm/window_util.h18
-rw-r--r--ui/views/corewm/wm_state.cc10
-rw-r--r--ui/views/corewm/wm_state.h2
17 files changed, 1095 insertions, 40 deletions
diff --git a/ui/views/corewm/base_focus_rules.cc b/ui/views/corewm/base_focus_rules.cc
index 9520c74..066627d 100644
--- a/ui/views/corewm/base_focus_rules.cc
+++ b/ui/views/corewm/base_focus_rules.cc
@@ -9,6 +9,7 @@
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/views/corewm/window_modality_controller.h"
+#include "ui/views/corewm/window_util.h"
namespace views {
namespace corewm {
@@ -120,15 +121,15 @@ aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
if (modal_transient)
return GetActivatableWindow(modal_transient);
- if (child->transient_parent()) {
+ if (views::corewm::GetTransientParent(child)) {
// To avoid infinite recursion, if |child| has a transient parent
// whose own modal transient is |child| itself, just return |child|.
aura::Window* parent_modal_transient =
- GetModalTransient(child->transient_parent());
+ GetModalTransient(views::corewm::GetTransientParent(child));
if (parent_modal_transient == child)
return child;
- return GetActivatableWindow(child->transient_parent());
+ return GetActivatableWindow(views::corewm::GetTransientParent(child));
}
parent = parent->parent();
diff --git a/ui/views/corewm/focus_controller.cc b/ui/views/corewm/focus_controller.cc
index bf77b03..a3107a1 100644
--- a/ui/views/corewm/focus_controller.cc
+++ b/ui/views/corewm/focus_controller.cc
@@ -13,6 +13,7 @@
#include "ui/aura/window_tracker.h"
#include "ui/events/event.h"
#include "ui/views/corewm/focus_rules.h"
+#include "ui/views/corewm/window_util.h"
namespace views {
namespace corewm {
@@ -25,10 +26,10 @@ void StackTransientParentsBelowModalWindow(aura::Window* window) {
if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
return;
- aura::Window* transient_parent = window->transient_parent();
+ aura::Window* transient_parent = views::corewm::GetTransientParent(window);
while (transient_parent) {
transient_parent->parent()->StackChildAtTop(transient_parent);
- transient_parent = transient_parent->transient_parent();
+ transient_parent = views::corewm::GetTransientParent(transient_parent);
}
}
@@ -37,7 +38,7 @@ void StackWindowLayerAbove(aura::Window* window, aura::Window* relative_to) {
// Stack |window| above the last transient child of |relative_to| that shares
// the same parent.
const aura::Window::Windows& window_transients(
- relative_to->transient_children());
+ GetTransientChildren(relative_to));
for (aura::Window::Windows::const_iterator i = window_transients.begin();
i != window_transients.end(); ++i) {
aura::Window* transient = *i;
diff --git a/ui/views/corewm/shadow_controller.cc b/ui/views/corewm/shadow_controller.cc
index eb09616..7327f92 100644
--- a/ui/views/corewm/shadow_controller.cc
+++ b/ui/views/corewm/shadow_controller.cc
@@ -19,6 +19,7 @@
#include "ui/compositor/layer.h"
#include "ui/views/corewm/shadow.h"
#include "ui/views/corewm/shadow_types.h"
+#include "ui/views/corewm/window_util.h"
using std::make_pair;
@@ -59,10 +60,10 @@ Shadow::Style GetShadowStyleForWindowLosingActive(
aura::Window* gaining_active) {
if (gaining_active && aura::client::GetHideOnDeactivate(gaining_active)) {
aura::Window::Windows::const_iterator it =
- std::find(losing_active->transient_children().begin(),
- losing_active->transient_children().end(),
+ std::find(GetTransientChildren(losing_active).begin(),
+ GetTransientChildren(losing_active).end(),
gaining_active);
- if (it != losing_active->transient_children().end())
+ if (it != GetTransientChildren(losing_active).end())
return Shadow::STYLE_ACTIVE;
}
return Shadow::STYLE_INACTIVE;
diff --git a/ui/views/corewm/shadow_controller_unittest.cc b/ui/views/corewm/shadow_controller_unittest.cc
index fb2c1d9..a01237a 100644
--- a/ui/views/corewm/shadow_controller_unittest.cc
+++ b/ui/views/corewm/shadow_controller_unittest.cc
@@ -16,6 +16,7 @@
#include "ui/compositor/layer.h"
#include "ui/views/corewm/shadow.h"
#include "ui/views/corewm/shadow_types.h"
+#include "ui/views/corewm/window_util.h"
#include "ui/views/corewm/wm_state.h"
namespace views {
@@ -205,7 +206,7 @@ TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) {
window2->Init(ui::LAYER_TEXTURED);
ParentWindow(window2.get());
window2->SetBounds(gfx::Rect(11, 21, 301, 401));
- window1->AddTransientChild(window2.get());
+ AddTransientChild(window1.get(), window2.get());
aura::client::SetHideOnDeactivate(window2.get(), true);
window2->Show();
ActivateWindow(window2.get());
diff --git a/ui/views/corewm/transient_window_controller.cc b/ui/views/corewm/transient_window_controller.cc
new file mode 100644
index 0000000..13a5b1f
--- /dev/null
+++ b/ui/views/corewm/transient_window_controller.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 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 "ui/views/corewm/transient_window_controller.h"
+
+#include "ui/views/corewm/transient_window_manager.h"
+
+namespace views {
+namespace corewm {
+
+TransientWindowController::TransientWindowController() {
+}
+
+TransientWindowController::~TransientWindowController() {
+}
+
+void TransientWindowController::AddTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void TransientWindowController::RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+aura::Window* TransientWindowController::GetTransientParent(
+ aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* TransientWindowController::GetTransientParent(
+ const aura::Window* window) {
+ const TransientWindowManager* window_manager =
+ TransientWindowManager::Get(window);
+ return window_manager ? window_manager->transient_parent() : NULL;
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_controller.h b/ui/views/corewm/transient_window_controller.h
new file mode 100644
index 0000000..88d473d
--- /dev/null
+++ b/ui/views/corewm/transient_window_controller.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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.
+
+#ifndef UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
+#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
+
+#include "ui/aura/client/transient_window_client.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+namespace corewm {
+
+// TransientWindowClient implementation. Uses TransientWindowManager to handle
+// tracking transient per window.
+class VIEWS_EXPORT TransientWindowController
+ : public aura::client::TransientWindowClient {
+ public:
+ TransientWindowController();
+ virtual ~TransientWindowController();
+
+ // TransientWindowClient:
+ virtual void AddTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual void RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual aura::Window* GetTransientParent(aura::Window* window) OVERRIDE;
+ virtual const aura::Window* GetTransientParent(
+ const aura::Window* window) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowController);
+};
+
+} // namespace corewm
+} // namespace views
+
+#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
diff --git a/ui/views/corewm/transient_window_manager.cc b/ui/views/corewm/transient_window_manager.cc
new file mode 100644
index 0000000..35ee95d
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 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 "ui/views/corewm/transient_window_manager.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/auto_reset.h"
+#include "base/stl_util.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/views/corewm/transient_window_stacking_client.h"
+#include "ui/views/corewm/window_util.h"
+
+using aura::Window;
+
+namespace views {
+namespace corewm {
+
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL);
+
+TransientWindowManager::~TransientWindowManager() {
+}
+
+// static
+TransientWindowManager* TransientWindowManager::Get(Window* window) {
+ TransientWindowManager* manager = window->GetProperty(kPropertyKey);
+ if (!manager) {
+ manager = new TransientWindowManager(window);
+ window->SetProperty(kPropertyKey, manager);
+ }
+ return manager;
+}
+
+// static
+const TransientWindowManager* TransientWindowManager::Get(
+ const Window* window) {
+ return window->GetProperty(kPropertyKey);
+}
+
+void TransientWindowManager::AddTransientChild(Window* child) {
+ // TransientWindowStackingClient does the stacking of transient windows. If it
+ // isn't installed stacking is going to be wrong.
+ DCHECK(TransientWindowStackingClient::instance_);
+
+ TransientWindowManager* child_manager = Get(child);
+ if (child_manager->transient_parent_)
+ Get(child_manager->transient_parent_)->RemoveTransientChild(child);
+ DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
+ child) == transient_children_.end());
+ transient_children_.push_back(child);
+ child_manager->transient_parent_ = window_;
+ FOR_EACH_OBSERVER(WindowObserver, window_->observers_,
+ OnAddTransientChild(window_, child));
+}
+
+void TransientWindowManager::RemoveTransientChild(Window* child) {
+ Windows::iterator i =
+ std::find(transient_children_.begin(), transient_children_.end(), child);
+ DCHECK(i != transient_children_.end());
+ transient_children_.erase(i);
+ TransientWindowManager* child_manager = Get(child);
+ DCHECK_EQ(window_, child_manager->transient_parent_);
+ child_manager->transient_parent_ = NULL;
+ FOR_EACH_OBSERVER(WindowObserver, window_->observers_,
+ OnRemoveTransientChild(window_, child));
+}
+
+bool TransientWindowManager::IsStackingTransient(
+ const aura::Window* child,
+ const aura::Window* target) const {
+ return stacking_pair_ && stacking_pair_->child == child &&
+ stacking_pair_->target == target;
+}
+
+TransientWindowManager::TransientWindowManager(Window* window)
+ : window_(window),
+ transient_parent_(NULL),
+ stacking_pair_(NULL) {
+ window_->AddObserver(this);
+}
+
+void TransientWindowManager::OnChildStackingChanged(aura::Window* child) {
+ // Do nothing if we initiated the stacking change.
+ // TODO(sky): change args to OnWindowStackingChanged() so that this lookup
+ // can be simpler.
+ if (stacking_pair_ && stacking_pair_->child == child) {
+ Windows::const_iterator child_i = std::find(
+ window_->children().begin(), window_->children().end(), child);
+ DCHECK(child_i != window_->children().end());
+ if (child_i != window_->children().begin() &&
+ (*(child_i - 1) == stacking_pair_->target))
+ return;
+ }
+
+ // Stack any transient children that share the same parent to be in front of
+ // |child|. The existing stacking order is preserved by iterating backwards
+ // and always stacking on top.
+ Window::Windows children(window_->children());
+ for (Window::Windows::reverse_iterator it = children.rbegin();
+ it != children.rend(); ++it) {
+ if ((*it) != child && HasTransientAncestor(*it, child)) {
+ StackingPair pair(*it, child);
+ base::AutoReset<StackingPair*> resetter(&stacking_pair_, &pair);
+ window_->StackChildAbove((*it), child);
+ }
+ }
+}
+
+void TransientWindowManager::OnWindowVisibilityChanging(Window* window,
+ bool visible) {
+ // TODO(sky): move handling of becoming visible here.
+ if (!visible) {
+ std::for_each(transient_children_.begin(), transient_children_.end(),
+ std::mem_fun(&Window::Hide));
+ }
+}
+
+void TransientWindowManager::OnWindowStackingChanged(Window* window) {
+ TransientWindowManager* parent_manager = Get(window->parent());
+ parent_manager->OnChildStackingChanged(window);
+}
+
+void TransientWindowManager::OnWindowDestroying(Window* window) {
+ // TODO(sky): remove notes after safely landing and baking.
+
+ // Removes ourselves from our transient parent (if it hasn't been done by the
+ // RootWindow).
+ // NOTE: This use to be done after children where removed, now it is before.
+ if (transient_parent_) {
+ TransientWindowManager::Get(transient_parent_)->RemoveTransientChild(
+ window_);
+ }
+
+ // Destroy transient children, only after we've removed ourselves from our
+ // parent, as destroying an active transient child may otherwise attempt to
+ // refocus us.
+ // NOTE: this use to be after removed from parent, now its before.
+ Windows transient_children(transient_children_);
+ STLDeleteElements(&transient_children);
+ DCHECK(transient_children_.empty());
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_manager.h b/ui/views/corewm/transient_window_manager.h
new file mode 100644
index 0000000..72c9f99
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager.h
@@ -0,0 +1,96 @@
+// Copyright 2014 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.
+
+#ifndef UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
+#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
+
+#include <vector>
+
+#include "ui/aura/window_observer.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+namespace corewm {
+
+// TransientWindowManager manages the set of transient children for a window
+// along with the transient parent. Transient children get the following
+// behavior:
+// . The transient parent destroys any transient children when it is
+// destroyed. This means a transient child is destroyed if either its parent
+// or transient parent is destroyed.
+// . If a transient child and its transient parent share the same parent, then
+// transient children are always ordered above the transient parent.
+// Transient windows are typically used for popups and menus.
+// TODO(sky): when we nuke TransientWindowClient rename this to
+// TransientWindowController.
+class VIEWS_EXPORT TransientWindowManager : public aura::WindowObserver {
+ public:
+ typedef std::vector<aura::Window*> Windows;
+
+ virtual ~TransientWindowManager();
+
+ // Returns the TransientWindowManager for |window|. This never returns NULL.
+ static TransientWindowManager* Get(aura::Window* window);
+
+ // Returns the TransientWindowManager for |window| only if it already exists.
+ // WARNING: this may return NULL.
+ static const TransientWindowManager* Get(const aura::Window* window);
+
+ // Adds or removes a transient child.
+ void AddTransientChild(aura::Window* child);
+ void RemoveTransientChild(aura::Window* child);
+
+ const Windows& transient_children() const { return transient_children_; }
+
+ aura::Window* transient_parent() { return transient_parent_; }
+ const aura::Window* transient_parent() const { return transient_parent_; }
+
+ // Returns true if in the process of stacking |child| on top of |target|. That
+ // is, when the stacking order of a window changes (OnWindowStackingChanged())
+ // the transients may get restacked as well. This function can be used to
+ // detect if TransientWindowManager is in the process of stacking a transient
+ // as the result of window stacking changing.
+ bool IsStackingTransient(const aura::Window* child,
+ const aura::Window* target) const;
+
+ private:
+ // Used to identify when a stacking change needs to restack transients.
+ struct StackingPair {
+ StackingPair(const aura::Window* child, const aura::Window* target)
+ : child(child),
+ target(target) {}
+
+ // The window being moved.
+ const aura::Window* child;
+
+ // |child| is being stacked on top of this.
+ const aura::Window* target;
+ };
+
+ explicit TransientWindowManager(aura::Window* window);
+
+ // Invoked whne |child|'s stacking order changes.
+ void OnChildStackingChanged(aura::Window* child);
+
+ // WindowObserver:
+ virtual void OnWindowVisibilityChanging(aura::Window* window,
+ bool visible) OVERRIDE;
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+
+ aura::Window* window_;
+ aura::Window* transient_parent_;
+ Windows transient_children_;
+
+ // If non-null we're actively restacking transient as the result of a
+ // transient ancestor changing. This is a pointer to a value on the stack.
+ StackingPair* stacking_pair_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManager);
+};
+
+} // namespace corewm
+} // namespace views
+
+#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
diff --git a/ui/views/corewm/transient_window_manager_unittest.cc b/ui/views/corewm/transient_window_manager_unittest.cc
new file mode 100644
index 0000000..b942eb1
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager_unittest.cc
@@ -0,0 +1,577 @@
+// Copyright 2014 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 "ui/views/corewm/transient_window_manager.h"
+
+#include "ui/aura/client/visibility_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/views/corewm/window_util.h"
+#include "ui/views/test/views_test_base.h"
+
+using aura::Window;
+
+using aura::test::ChildWindowIDsAsString;
+using aura::test::CreateTestWindowWithId;
+
+namespace views {
+namespace corewm {
+
+class TransientWindowManagerTest : public views::ViewsTestBase {
+ public:
+ TransientWindowManagerTest() {}
+ virtual ~TransientWindowManagerTest() {}
+
+ protected:
+ // Creates a transient window that is transient to |parent|.
+ Window* CreateTransientChild(int id, Window* parent) {
+ Window* window = new Window(NULL);
+ window->set_id(id);
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window->Init(ui::LAYER_TEXTURED);
+ aura::client::ParentWindowWithContext(window, GetContext(), gfx::Rect());
+ AddTransientChild(parent, window);
+ return window;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest);
+};
+
+// Various assertions for transient children.
+TEST_F(TransientWindowManagerTest, TransientChildren) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ scoped_ptr<Window> w3(CreateTestWindowWithId(3, parent.get()));
+ Window* w2 = CreateTestWindowWithId(2, parent.get());
+ // w2 is now owned by w1.
+ AddTransientChild(w1.get(), w2);
+ // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(3u, parent->children().size());
+ EXPECT_EQ(w2, parent->children().back());
+
+ // Destroy w1, which should also destroy w3 (since it's a transient child).
+ w1.reset();
+ w2 = NULL;
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+
+ w1.reset(CreateTestWindowWithId(4, parent.get()));
+ w2 = CreateTestWindowWithId(5, w3.get());
+ AddTransientChild(w1.get(), w2);
+ parent->StackChildAtTop(w3.get());
+ // Stack w1 at the top (end), this shouldn't affect w2 since it has a
+ // different parent.
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(2u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+ EXPECT_EQ(w1.get(), parent->children()[1]);
+
+ // Hiding parent should hide transient children.
+ EXPECT_TRUE(w2->IsVisible());
+ w1->Hide();
+ EXPECT_FALSE(w2->IsVisible());
+}
+
+// Tests that transient children are stacked as a unit when using stack above.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupAbove) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtTop(w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w22);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w213, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent above its transient child.
+ parent->StackChildAbove(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAbove(w2.get(), w1.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w213);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient children are stacked as a unit when using stack below.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupBelow) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
+ // This also tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtBottom(w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAtBottom(w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w11, w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w22, w21);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent below its transient child.
+ parent->StackChildBelow(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w1.get(), w2.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+namespace {
+
+// Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when
+// OnWindowDestroyed() is invoked so that destruction order can be verified.
+class DestroyedTrackingDelegate : public aura::test::TestWindowDelegate {
+ public:
+ explicit DestroyedTrackingDelegate(const std::string& name,
+ std::vector<std::string>* results)
+ : name_(name),
+ results_(results) {}
+
+ virtual void OnWindowDestroyed() OVERRIDE {
+ results_->push_back(name_);
+ }
+
+ private:
+ const std::string name_;
+ std::vector<std::string>* results_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate);
+};
+
+} // namespace
+
+// Verifies the delegate is notified of destruction after transients are
+// destroyed.
+TEST_F(TransientWindowManagerTest, NotifyDelegateAfterDeletingTransients) {
+ std::vector<std::string> destruction_order;
+
+ DestroyedTrackingDelegate parent_delegate("parent", &destruction_order);
+ scoped_ptr<Window> parent(new Window(&parent_delegate));
+ parent->Init(ui::LAYER_NOT_DRAWN);
+
+ DestroyedTrackingDelegate transient_delegate("transient", &destruction_order);
+ Window* transient = new Window(&transient_delegate); // Owned by |parent|.
+ transient->Init(ui::LAYER_NOT_DRAWN);
+ AddTransientChild(parent.get(), transient);
+ parent.reset();
+
+ ASSERT_EQ(2u, destruction_order.size());
+ EXPECT_EQ("transient", destruction_order[0]);
+ EXPECT_EQ("parent", destruction_order[1]);
+}
+
+TEST_F(TransientWindowManagerTest, StackTransientsWhoseLayersHaveNoDelegate) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(GetContext()));
+
+ // Remove the delegates of a couple of transients, as if they are closing
+ // and animating out.
+ window11->layer()->set_delegate(NULL);
+ window13->layer()->set_delegate(NULL);
+
+ // Move window1 to the front. All transients should move with it, and their
+ // order should be preserved.
+ GetContext()->StackChildAtTop(window1.get());
+
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransients) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 11 above 12.
+ GetContext()->StackChildAbove(window11.get(), window12.get());
+ EXPECT_EQ("1 12 11 13", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 13 below 12.
+ GetContext()->StackChildBelow(window13.get(), window12.get());
+ EXPECT_EQ("1 13 12 11", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 11 above 1.
+ GetContext()->StackChildAbove(window11.get(), window1.get());
+ EXPECT_EQ("1 11 13 12", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 12 below 13.
+ GetContext()->StackChildBelow(window12.get(), window13.get());
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransientsNoLayerDelegate) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(GetContext()));
+
+ window1->layer()->set_delegate(NULL);
+
+ // Stack 1 at top.
+ GetContext()->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+class StackingMadrigalLayoutManager : public aura::LayoutManager {
+ public:
+ explicit StackingMadrigalLayoutManager(Window* root_window)
+ : root_window_(root_window) {
+ root_window_->SetLayoutManager(this);
+ }
+ virtual ~StackingMadrigalLayoutManager() {
+ }
+
+ private:
+ // Overridden from LayoutManager:
+ virtual void OnWindowResized() OVERRIDE {}
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {}
+ virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {}
+ virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {}
+ virtual void OnChildWindowVisibilityChanged(Window* child,
+ bool visible) OVERRIDE {
+ Window::Windows::const_iterator it = root_window_->children().begin();
+ Window* last_window = NULL;
+ for (; it != root_window_->children().end(); ++it) {
+ if (*it == child && last_window) {
+ if (!visible)
+ root_window_->StackChildAbove(last_window, *it);
+ else
+ root_window_->StackChildAbove(*it, last_window);
+ break;
+ }
+ last_window = *it;
+ }
+ }
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ SetChildBoundsDirect(child, requested_bounds);
+ }
+
+ Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager);
+};
+
+class StackingMadrigalVisibilityClient : public aura::client::VisibilityClient {
+ public:
+ explicit StackingMadrigalVisibilityClient(Window* root_window)
+ : ignored_window_(NULL) {
+ aura::client::SetVisibilityClient(root_window, this);
+ }
+ virtual ~StackingMadrigalVisibilityClient() {
+ }
+
+ void set_ignored_window(Window* ignored_window) {
+ ignored_window_ = ignored_window;
+ }
+
+ private:
+ // Overridden from client::VisibilityClient:
+ virtual void UpdateLayerVisibility(Window* window, bool visible) OVERRIDE {
+ if (!visible) {
+ if (window == ignored_window_)
+ window->layer()->set_delegate(NULL);
+ else
+ window->layer()->SetVisible(visible);
+ } else {
+ window->layer()->SetVisible(visible);
+ }
+ }
+
+ Window* ignored_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalVisibilityClient);
+};
+
+// This test attempts to reconstruct a circumstance that can happen when the
+// aura client attempts to manipulate the visibility and delegate of a layer
+// independent of window visibility.
+// A use case is where the client attempts to keep a window visible onscreen
+// even after code has called Hide() on the window. The use case for this would
+// be that window hides are animated (e.g. the window fades out). To prevent
+// spurious updating the client code may also clear window's layer's delegate,
+// so that the window cannot attempt to paint or update it further. The window
+// uses the presence of a NULL layer delegate as a signal in stacking to note
+// that the window is being manipulated by such a use case and its stacking
+// should not be adjusted.
+// One issue that can arise when a window opens two transient children, and the
+// first is hidden. Subsequent attempts to activate the transient parent can
+// result in the transient parent being stacked above the second transient
+// child. A fix is made to Window::StackAbove to prevent this, and this test
+// verifies this fix.
+TEST_F(TransientWindowManagerTest, StackingMadrigal) {
+ new StackingMadrigalLayoutManager(GetContext());
+ StackingMadrigalVisibilityClient visibility_client(GetContext());
+
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+
+ visibility_client.set_ignored_window(window11.get());
+
+ window11->Show();
+ window11->Hide();
+
+ // As a transient, window11 should still be stacked above window1, even when
+ // hidden.
+ EXPECT_TRUE(aura::test::WindowIsAbove(window11.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window11.get(), window1.get()));
+
+ // A new transient should still be above window1. It will appear behind
+ // window11 because we don't stack windows on top of targets with NULL
+ // delegates.
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ window12->Show();
+
+ EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get()));
+
+ // In earlier versions of the StackChildAbove() method, attempting to stack
+ // window1 above window12 at this point would actually restack the layers
+ // resulting in window12's layer being below window1's layer (though the
+ // windows themselves would still be correctly stacked, so events would pass
+ // through.)
+ GetContext()->StackChildAbove(window1.get(), window12.get());
+
+ // Both window12 and its layer should be stacked above window1.
+ EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get()));
+}
+
+// Test for an issue where attempting to stack a primary window on top of a
+// transient with a NULL layer delegate causes that primary window to be moved,
+// but the layer order not changed to match. http://crbug.com/112562
+TEST_F(TransientWindowManagerTest, StackOverClosingTransient) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> transient1(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> transient2(CreateTransientChild(21, window2.get()));
+
+ // Both windows and layers are stacked in creation order.
+ Window* root = GetContext();
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], transient1.get());
+ EXPECT_EQ(root->children()[2], window2.get());
+ EXPECT_EQ(root->children()[3], transient2.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient1->layer());
+ EXPECT_EQ(root->layer()->children()[2], window2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient2->layer());
+ EXPECT_EQ("1 11 2 21", ChildWindowIDsAsString(GetContext()));
+
+ // This brings window1 and its transient to the front.
+ root->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 21 1 11", ChildWindowIDsAsString(GetContext()));
+
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Pretend we're closing the top-most transient, then bring window2 to the
+ // front. This mimics activating a browser window while the status bubble
+ // is fading out. The transient should stay topmost.
+ transient1->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Close the transient. Remaining windows are stable.
+ transient1.reset();
+
+ ASSERT_EQ(3u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ ASSERT_EQ(3u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+
+ // Open another window on top.
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Pretend we're closing the topmost non-transient window, then bring
+ // window2 to the top. It should not move.
+ window3->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Bring window1 to the top. It should move ahead of window2, but not
+ // ahead of window3 (with NULL delegate).
+ root->StackChildAtTop(window1.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_stacking_client.cc b/ui/views/corewm/transient_window_stacking_client.cc
index e3373ff..ef08cc0 100644
--- a/ui/views/corewm/transient_window_stacking_client.cc
+++ b/ui/views/corewm/transient_window_stacking_client.cc
@@ -6,6 +6,9 @@
#include <algorithm>
+#include "ui/views/corewm/transient_window_manager.h"
+#include "ui/views/corewm/window_util.h"
+
using aura::Window;
namespace views {
@@ -17,7 +20,7 @@ namespace {
// siblings of |window|. Returns true if any ancestors were found, false if not.
bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) {
Window* parent = window->parent();
- for (; window; window = window->transient_parent()) {
+ for (; window; window = GetTransientParent(window)) {
if (window->parent() == parent)
ancestors->push_back(window);
}
@@ -51,27 +54,53 @@ void FindCommonTransientAncestor(Window** window1, Window** window2) {
}
}
-// Returns true if |window| has |ancestor| as a transient ancestor. A transient
-// ancestor is found by following the transient parent chain of the window.
-bool HasTransientAncestor(const Window* window, const Window* ancestor) {
- if (window->transient_parent() == ancestor)
- return true;
- return window->transient_parent() ?
- HasTransientAncestor(window->transient_parent(), ancestor) : false;
+// Adjusts |target| so that we don't attempt to stack on top of a window with a
+// NULL delegate.
+void SkipNullDelegates(Window::StackDirection direction, Window** target) {
+ const Window::Windows& children((*target)->parent()->children());
+ size_t target_i =
+ std::find(children.begin(), children.end(), *target) -
+ children.begin();
+
+ // By convention we don't stack on top of windows with layers with NULL
+ // delegates. Walk backward to find a valid target window. See tests
+ // TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient
+ // for an explanation of this.
+ while (target_i > 0) {
+ const size_t index = direction == Window::STACK_ABOVE ?
+ target_i : target_i - 1;
+ if (!children[index]->layer() ||
+ children[index]->layer()->delegate() != NULL)
+ break;
+ --target_i;
+ }
+ *target = children[target_i];
}
} // namespace
+// static
+TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL;
+
TransientWindowStackingClient::TransientWindowStackingClient() {
+ instance_ = this;
}
TransientWindowStackingClient::~TransientWindowStackingClient() {
+ if (instance_ == this)
+ instance_ = NULL;
}
bool TransientWindowStackingClient::AdjustStacking(
Window** child,
Window** target,
Window::StackDirection* direction) {
+ const TransientWindowManager* transient_manager =
+ TransientWindowManager::Get((*child)->parent());
+ if (transient_manager &&
+ transient_manager->IsStackingTransient(*child, *target))
+ return true;
+
// For windows that have transient children stack the transient ancestors that
// are siblings. This prevents one transient group from being inserted in the
// middle of another.
@@ -89,7 +118,16 @@ bool TransientWindowStackingClient::AdjustStacking(
}
*target = siblings[target_i];
}
- return true;
+
+ SkipNullDelegates(*direction, target);
+
+ // If we couldn't find a valid target position, don't move anything.
+ if (*direction == Window::STACK_ABOVE &&
+ ((*target)->layer() && (*target)->layer()->delegate() == NULL)) {
+ return false;
+ }
+
+ return *child != *target;
}
} // namespace corewm
diff --git a/ui/views/corewm/transient_window_stacking_client.h b/ui/views/corewm/transient_window_stacking_client.h
index f8c1982..04e0eaf 100644
--- a/ui/views/corewm/transient_window_stacking_client.h
+++ b/ui/views/corewm/transient_window_stacking_client.h
@@ -11,6 +11,8 @@
namespace views {
namespace corewm {
+class TransientWindowManager;
+
class VIEWS_EXPORT TransientWindowStackingClient
: public aura::client::WindowStackingClient {
public:
@@ -23,6 +25,11 @@ class VIEWS_EXPORT TransientWindowStackingClient
aura::Window::StackDirection* direction) OVERRIDE;
private:
+ // Purely for DCHECKs.
+ friend class TransientWindowManager;
+
+ static TransientWindowStackingClient* instance_;
+
DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClient);
};
diff --git a/ui/views/corewm/transient_window_stacking_client_unittest.cc b/ui/views/corewm/transient_window_stacking_client_unittest.cc
index 05d7b3e..c4570b7 100644
--- a/ui/views/corewm/transient_window_stacking_client_unittest.cc
+++ b/ui/views/corewm/transient_window_stacking_client_unittest.cc
@@ -7,6 +7,8 @@
#include "base/memory/scoped_ptr.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_windows.h"
+#include "ui/compositor/test/test_layers.h"
+#include "ui/views/corewm/window_util.h"
using aura::test::ChildWindowIDsAsString;
using aura::test::CreateTestWindowWithId;
@@ -49,12 +51,12 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupAbove) {
Window* w22 = CreateTestWindowWithId(22, parent.get());
ASSERT_EQ(8u, parent->children().size());
- w1->AddTransientChild(w11); // w11 is now owned by w1.
- w2->AddTransientChild(w21); // w21 is now owned by w2.
- w2->AddTransientChild(w22); // w22 is now owned by w2.
- w21->AddTransientChild(w211); // w211 is now owned by w21.
- w21->AddTransientChild(w212); // w212 is now owned by w21.
- w21->AddTransientChild(w213); // w213 is now owned by w21.
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w1 at the top (end), this should force w11 to be last (on top of w1).
@@ -119,12 +121,12 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) {
Window* w22 = CreateTestWindowWithId(22, parent.get());
ASSERT_EQ(8u, parent->children().size());
- w1->AddTransientChild(w11); // w11 is now owned by w1.
- w2->AddTransientChild(w21); // w21 is now owned by w2.
- w2->AddTransientChild(w22); // w22 is now owned by w2.
- w21->AddTransientChild(w211); // w211 is now owned by w21.
- w21->AddTransientChild(w212); // w212 is now owned by w21.
- w21->AddTransientChild(w213); // w213 is now owned by w21.
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w2 at the bottom, this should force w11 to be last (on top of w1).
@@ -173,5 +175,43 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) {
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
}
+TEST_F(TransientWindowStackingClientTest,
+ StackWindowsWhoseLayersHaveNoDelegate) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ window1->layer()->set_name("1");
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ window2->layer()->set_name("2");
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
+ window3->layer()->set_name("3");
+
+ // This brings |window1| (and its layer) to the front.
+ root_window()->StackChildAbove(window1.get(), window3.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since |window1| does not have a delegate, |window2| should not move in
+ // front of it, nor should its layer.
+ window1->layer()->set_delegate(NULL);
+ root_window()->StackChildAbove(window2.get(), window1.get());
+ EXPECT_EQ("3 2 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("3 2 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // It should still be possible to stack |window3| immediately below |window1|.
+ root_window()->StackChildBelow(window3.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since neither |window3| nor |window1| have a delegate, |window2| should
+ // not move in front of either.
+ window3->layer()->set_delegate(NULL);
+ root_window()->StackChildBelow(window2.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+}
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/window_modality_controller.cc b/ui/views/corewm/window_modality_controller.cc
index b52a9d6..d87f088 100644
--- a/ui/views/corewm/window_modality_controller.cc
+++ b/ui/views/corewm/window_modality_controller.cc
@@ -63,13 +63,13 @@ bool IsModalTransientChild(aura::Window* transient, aura::Window* original) {
aura::Window* GetModalTransientChild(
aura::Window* activatable,
aura::Window* original) {
- aura::Window::Windows::const_iterator it;
- for (it = activatable->transient_children().begin();
- it != activatable->transient_children().end();
+ for (aura::Window::Windows::const_iterator it =
+ GetTransientChildren(activatable).begin();
+ it != GetTransientChildren(activatable).end();
++it) {
aura::Window* transient = *it;
if (IsModalTransientChild(transient, original)) {
- return transient->transient_children().empty() ?
+ return GetTransientChildren(transient).empty() ?
transient : GetModalTransientChild(transient, original);
}
}
@@ -153,8 +153,8 @@ void WindowModalityController::OnWindowPropertyChanged(aura::Window* window,
window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE &&
window->IsVisible()) {
ActivateWindow(window);
- ui::GestureRecognizer::Get()->TransferEventsTo(
- window->transient_parent(), NULL);
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
}
}
@@ -163,8 +163,8 @@ void WindowModalityController::OnWindowVisibilityChanged(
bool visible) {
if (visible && window->GetProperty(aura::client::kModalKey) !=
ui::MODAL_TYPE_NONE) {
- ui::GestureRecognizer::Get()->TransferEventsTo(
- window->transient_parent(), NULL);
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
// Make sure no other window has capture, otherwise |window| won't get mouse
// events.
aura::Window* capture_window = aura::client::GetCaptureWindow(window);
diff --git a/ui/views/corewm/window_util.cc b/ui/views/corewm/window_util.cc
index eaee174..64a23d1 100644
--- a/ui/views/corewm/window_util.cc
+++ b/ui/views/corewm/window_util.cc
@@ -8,6 +8,7 @@
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
+#include "ui/views/corewm/transient_window_manager.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
@@ -137,5 +138,42 @@ ui::Layer* RecreateWindowLayers(aura::Window* window, bool set_bounds) {
return old_layer;
}
+aura::Window* GetTransientParent(aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* GetTransientParent(const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ return manager ? manager->transient_parent() : NULL;
+}
+
+const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ if (manager)
+ return manager->transient_children();
+
+ static std::vector<aura::Window*>* shared = new std::vector<aura::Window*>;
+ return *shared;
+}
+
+void AddTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void RemoveTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor) {
+ const aura::Window* transient_parent = GetTransientParent(window);
+ if (transient_parent == ancestor)
+ return true;
+ return transient_parent ?
+ HasTransientAncestor(transient_parent, ancestor) : false;
+}
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/window_util.h b/ui/views/corewm/window_util.h
index 01a3ea4..c3b552d 100644
--- a/ui/views/corewm/window_util.h
+++ b/ui/views/corewm/window_util.h
@@ -5,6 +5,8 @@
#ifndef UI_VIEWS_COREWM_WINDOW_UTIL_H_
#define UI_VIEWS_COREWM_WINDOW_UTIL_H_
+#include <vector>
+
#include "base/compiler_specific.h"
#include "ui/views/views_export.h"
@@ -47,6 +49,22 @@ VIEWS_EXPORT void DeepDeleteLayers(ui::Layer* layer);
VIEWS_EXPORT ui::Layer* RecreateWindowLayers(aura::Window* window,
bool set_bounds) WARN_UNUSED_RESULT;
+// Convenience functions that get the TransientWindowManager for the window and
+// redirect appropriately. These are preferable to calling functions on
+// TransientWindowManager as they handle the appropriate NULL checks.
+VIEWS_EXPORT aura::Window* GetTransientParent(aura::Window* window);
+VIEWS_EXPORT const aura::Window* GetTransientParent(const aura::Window* window);
+VIEWS_EXPORT const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window);
+VIEWS_EXPORT void AddTransientChild(aura::Window* parent, aura::Window* child);
+VIEWS_EXPORT void RemoveTransientChild(aura::Window* parent,
+ aura::Window* child);
+
+// Returns true if |window| has |ancestor| as a transient ancestor. A transient
+// ancestor is found by following the transient parent chain of the window.
+VIEWS_EXPORT bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor);
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/wm_state.cc b/ui/views/corewm/wm_state.cc
index 0799247..16d9e7a 100644
--- a/ui/views/corewm/wm_state.cc
+++ b/ui/views/corewm/wm_state.cc
@@ -4,19 +4,27 @@
#include "ui/views/corewm/wm_state.h"
+#include "ui/views/corewm/transient_window_controller.h"
#include "ui/views/corewm/transient_window_stacking_client.h"
namespace views {
namespace corewm {
WMState::WMState()
- : window_stacking_client_(new TransientWindowStackingClient) {
+ : window_stacking_client_(new TransientWindowStackingClient),
+ transient_window_client_(new TransientWindowController) {
aura::client::SetWindowStackingClient(window_stacking_client_.get());
+ aura::client::SetTransientWindowClient(transient_window_client_.get());
}
WMState::~WMState() {
if (aura::client::GetWindowStackingClient() == window_stacking_client_.get())
aura::client::SetWindowStackingClient(NULL);
+
+ if (aura::client::GetTransientWindowClient() ==
+ transient_window_client_.get()) {
+ aura::client::SetTransientWindowClient(NULL);
+ }
}
} // namespace corewm
diff --git a/ui/views/corewm/wm_state.h b/ui/views/corewm/wm_state.h
index 1e0ee4f..e8a09f2 100644
--- a/ui/views/corewm/wm_state.h
+++ b/ui/views/corewm/wm_state.h
@@ -11,6 +11,7 @@
namespace views {
namespace corewm {
+class TransientWindowController;
class TransientWindowStackingClient;
// Installs state needed by the window manager.
@@ -22,6 +23,7 @@ class VIEWS_EXPORT WMState {
// WindowStackingClient:
private:
scoped_ptr<TransientWindowStackingClient> window_stacking_client_;
+ scoped_ptr<TransientWindowController> transient_window_client_;
DISALLOW_COPY_AND_ASSIGN(WMState);
};