diff options
Diffstat (limited to 'ui/views/corewm')
-rw-r--r-- | ui/views/corewm/base_focus_rules.cc | 7 | ||||
-rw-r--r-- | ui/views/corewm/focus_controller.cc | 7 | ||||
-rw-r--r-- | ui/views/corewm/shadow_controller.cc | 7 | ||||
-rw-r--r-- | ui/views/corewm/shadow_controller_unittest.cc | 3 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_controller.cc | 42 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_controller.h | 38 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_manager.cc | 147 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_manager.h | 96 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_manager_unittest.cc | 577 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client.cc | 56 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client.h | 7 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client_unittest.cc | 64 | ||||
-rw-r--r-- | ui/views/corewm/window_modality_controller.cc | 16 | ||||
-rw-r--r-- | ui/views/corewm/window_util.cc | 38 | ||||
-rw-r--r-- | ui/views/corewm/window_util.h | 18 | ||||
-rw-r--r-- | ui/views/corewm/wm_state.cc | 10 | ||||
-rw-r--r-- | ui/views/corewm/wm_state.h | 2 |
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); }; |