1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/wm/core/transient_window_manager.h"
10 #include "base/auto_reset.h"
11 #include "base/stl_util.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_property.h"
14 #include "ui/wm/core/transient_window_observer.h"
15 #include "ui/wm/core/transient_window_stacking_client.h"
16 #include "ui/wm/core/window_util.h"
22 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager
, kPropertyKey
, NULL
);
24 TransientWindowManager::~TransientWindowManager() {
28 TransientWindowManager
* TransientWindowManager::Get(Window
* window
) {
29 TransientWindowManager
* manager
= window
->GetProperty(kPropertyKey
);
31 manager
= new TransientWindowManager(window
);
32 window
->SetProperty(kPropertyKey
, manager
);
38 const TransientWindowManager
* TransientWindowManager::Get(
39 const Window
* window
) {
40 return window
->GetProperty(kPropertyKey
);
43 void TransientWindowManager::AddObserver(TransientWindowObserver
* observer
) {
44 observers_
.AddObserver(observer
);
47 void TransientWindowManager::RemoveObserver(TransientWindowObserver
* observer
) {
48 observers_
.RemoveObserver(observer
);
51 void TransientWindowManager::AddTransientChild(Window
* child
) {
52 // TransientWindowStackingClient does the stacking of transient windows. If it
53 // isn't installed stacking is going to be wrong.
54 DCHECK(TransientWindowStackingClient::instance_
);
56 TransientWindowManager
* child_manager
= Get(child
);
57 if (child_manager
->transient_parent_
)
58 Get(child_manager
->transient_parent_
)->RemoveTransientChild(child
);
59 DCHECK(std::find(transient_children_
.begin(), transient_children_
.end(),
60 child
) == transient_children_
.end());
61 transient_children_
.push_back(child
);
62 child_manager
->transient_parent_
= window_
;
64 // Restack |child| properly above its transient parent, if they share the same
66 if (child
->parent() == window_
->parent())
67 RestackTransientDescendants();
69 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
70 OnTransientChildAdded(window_
, child
));
73 void TransientWindowManager::RemoveTransientChild(Window
* child
) {
75 std::find(transient_children_
.begin(), transient_children_
.end(), child
);
76 DCHECK(i
!= transient_children_
.end());
77 transient_children_
.erase(i
);
78 TransientWindowManager
* child_manager
= Get(child
);
79 DCHECK_EQ(window_
, child_manager
->transient_parent_
);
80 child_manager
->transient_parent_
= NULL
;
82 // If |child| and its former transient parent share the same parent, |child|
83 // should be restacked properly so it is not among transient children of its
84 // former parent, anymore.
85 if (window_
->parent() == child
->parent())
86 RestackTransientDescendants();
88 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
89 OnTransientChildRemoved(window_
, child
));
92 bool TransientWindowManager::IsStackingTransient(
93 const aura::Window
* target
) const {
94 return stacking_target_
== target
;
97 TransientWindowManager::TransientWindowManager(Window
* window
)
99 transient_parent_(NULL
),
100 stacking_target_(NULL
) {
101 window_
->AddObserver(this);
104 void TransientWindowManager::RestackTransientDescendants() {
105 Window
* parent
= window_
->parent();
109 // Stack any transient children that share the same parent to be in front of
110 // |window_|. The existing stacking order is preserved by iterating backwards
111 // and always stacking on top.
112 Window::Windows
children(parent
->children());
113 for (Window::Windows::reverse_iterator it
= children
.rbegin();
114 it
!= children
.rend(); ++it
) {
115 if ((*it
) != window_
&& HasTransientAncestor(*it
, window_
)) {
116 TransientWindowManager
* descendant_manager
= Get(*it
);
117 base::AutoReset
<Window
*> resetter(
118 &descendant_manager
->stacking_target_
,
120 parent
->StackChildAbove((*it
), window_
);
125 void TransientWindowManager::OnWindowParentChanged(aura::Window
* window
,
126 aura::Window
* parent
) {
127 DCHECK_EQ(window_
, window
);
128 // Stack |window| properly if it is transient child of a sibling.
129 Window
* transient_parent
= wm::GetTransientParent(window
);
130 if (transient_parent
&& transient_parent
->parent() == parent
) {
131 TransientWindowManager
* transient_parent_manager
=
132 Get(transient_parent
);
133 transient_parent_manager
->RestackTransientDescendants();
137 void TransientWindowManager::OnWindowVisibilityChanging(Window
* window
,
139 // TODO(sky): move handling of becoming visible here.
141 std::for_each(transient_children_
.begin(), transient_children_
.end(),
142 std::mem_fun(&Window::Hide
));
146 void TransientWindowManager::OnWindowStackingChanged(Window
* window
) {
147 DCHECK_EQ(window_
, window
);
149 // Do nothing if we initiated the stacking change.
150 const TransientWindowManager
* transient_manager
=
151 Get(static_cast<const Window
*>(window
));
152 if (transient_manager
&& transient_manager
->stacking_target_
) {
153 Windows::const_iterator window_i
= std::find(
154 window
->parent()->children().begin(),
155 window
->parent()->children().end(),
157 DCHECK(window_i
!= window
->parent()->children().end());
158 if (window_i
!= window
->parent()->children().begin() &&
159 (*(window_i
- 1) == transient_manager
->stacking_target_
))
163 RestackTransientDescendants();
166 void TransientWindowManager::OnWindowDestroying(Window
* window
) {
167 // Removes ourselves from our transient parent (if it hasn't been done by the
169 if (transient_parent_
) {
170 TransientWindowManager::Get(transient_parent_
)->RemoveTransientChild(
174 // Destroy transient children, only after we've removed ourselves from our
175 // parent, as destroying an active transient child may otherwise attempt to
177 Windows
transient_children(transient_children_
);
178 STLDeleteElements(&transient_children
);
179 DCHECK(transient_children_
.empty());