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"
23 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager
, kPropertyKey
, NULL
);
27 TransientWindowManager::~TransientWindowManager() {
31 TransientWindowManager
* TransientWindowManager::Get(Window
* window
) {
32 TransientWindowManager
* manager
= window
->GetProperty(kPropertyKey
);
34 manager
= new TransientWindowManager(window
);
35 window
->SetProperty(kPropertyKey
, manager
);
41 const TransientWindowManager
* TransientWindowManager::Get(
42 const Window
* window
) {
43 return window
->GetProperty(kPropertyKey
);
46 void TransientWindowManager::AddObserver(TransientWindowObserver
* observer
) {
47 observers_
.AddObserver(observer
);
50 void TransientWindowManager::RemoveObserver(TransientWindowObserver
* observer
) {
51 observers_
.RemoveObserver(observer
);
54 void TransientWindowManager::AddTransientChild(Window
* child
) {
55 // TransientWindowStackingClient does the stacking of transient windows. If it
56 // isn't installed stacking is going to be wrong.
57 DCHECK(TransientWindowStackingClient::instance_
);
59 TransientWindowManager
* child_manager
= Get(child
);
60 if (child_manager
->transient_parent_
)
61 Get(child_manager
->transient_parent_
)->RemoveTransientChild(child
);
62 DCHECK(std::find(transient_children_
.begin(), transient_children_
.end(),
63 child
) == transient_children_
.end());
64 transient_children_
.push_back(child
);
65 child_manager
->transient_parent_
= window_
;
67 // Restack |child| properly above its transient parent, if they share the same
69 if (child
->parent() == window_
->parent())
70 RestackTransientDescendants();
72 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
73 OnTransientChildAdded(window_
, child
));
76 void TransientWindowManager::RemoveTransientChild(Window
* child
) {
78 std::find(transient_children_
.begin(), transient_children_
.end(), child
);
79 DCHECK(i
!= transient_children_
.end());
80 transient_children_
.erase(i
);
81 TransientWindowManager
* child_manager
= Get(child
);
82 DCHECK_EQ(window_
, child_manager
->transient_parent_
);
83 child_manager
->transient_parent_
= NULL
;
85 // If |child| and its former transient parent share the same parent, |child|
86 // should be restacked properly so it is not among transient children of its
87 // former parent, anymore.
88 if (window_
->parent() == child
->parent())
89 RestackTransientDescendants();
91 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
92 OnTransientChildRemoved(window_
, child
));
95 bool TransientWindowManager::IsStackingTransient(
96 const aura::Window
* target
) const {
97 return stacking_target_
== target
;
100 TransientWindowManager::TransientWindowManager(Window
* window
)
102 transient_parent_(NULL
),
103 stacking_target_(NULL
),
104 parent_controls_visibility_(false),
105 show_on_parent_visible_(false),
106 ignore_visibility_changed_event_(false) {
107 window_
->AddObserver(this);
110 void TransientWindowManager::RestackTransientDescendants() {
111 Window
* parent
= window_
->parent();
115 // Stack any transient children that share the same parent to be in front of
116 // |window_|. The existing stacking order is preserved by iterating backwards
117 // and always stacking on top.
118 Window::Windows
children(parent
->children());
119 for (Window::Windows::reverse_iterator it
= children
.rbegin();
120 it
!= children
.rend(); ++it
) {
121 if ((*it
) != window_
&& HasTransientAncestor(*it
, window_
)) {
122 TransientWindowManager
* descendant_manager
= Get(*it
);
123 base::AutoReset
<Window
*> resetter(
124 &descendant_manager
->stacking_target_
,
126 parent
->StackChildAbove((*it
), window_
);
131 void TransientWindowManager::OnWindowParentChanged(aura::Window
* window
,
132 aura::Window
* parent
) {
133 DCHECK_EQ(window_
, window
);
134 // Stack |window| properly if it is transient child of a sibling.
135 Window
* transient_parent
= wm::GetTransientParent(window
);
136 if (transient_parent
&& transient_parent
->parent() == parent
) {
137 TransientWindowManager
* transient_parent_manager
=
138 Get(transient_parent
);
139 transient_parent_manager
->RestackTransientDescendants();
143 void TransientWindowManager::UpdateTransientChildVisibility(
144 bool parent_visible
) {
145 base::AutoReset
<bool> reset(&ignore_visibility_changed_event_
, true);
146 if (!parent_visible
) {
147 show_on_parent_visible_
= window_
->TargetVisibility();
150 if (show_on_parent_visible_
&& parent_controls_visibility_
)
152 show_on_parent_visible_
= false;
156 void TransientWindowManager::OnWindowVisibilityChanged(Window
* window
,
158 if (window_
!= window
)
161 // If the window has transient children, updates the transient children's
162 // visiblity as well.
163 for (Window
* child
: transient_children_
)
164 Get(child
)->UpdateTransientChildVisibility(visible
);
166 // Remember the show request in |show_on_parent_visible_| and hide it again
167 // if the following conditions are met
168 // - |parent_controls_visibility| is set to true.
169 // - the window is hidden while the transient parent is not visible.
170 // - Show/Hide was NOT called from TransientWindowManager.
171 if (ignore_visibility_changed_event_
||
172 !transient_parent_
|| !parent_controls_visibility_
) {
176 if (!transient_parent_
->TargetVisibility() && visible
) {
177 base::AutoReset
<bool> reset(&ignore_visibility_changed_event_
, true);
178 show_on_parent_visible_
= true;
180 } else if (!visible
) {
181 DCHECK(!show_on_parent_visible_
);
185 void TransientWindowManager::OnWindowStackingChanged(Window
* window
) {
186 DCHECK_EQ(window_
, window
);
187 // Do nothing if we initiated the stacking change.
188 const TransientWindowManager
* transient_manager
=
189 Get(static_cast<const Window
*>(window
));
190 if (transient_manager
&& transient_manager
->stacking_target_
) {
191 Windows::const_iterator window_i
= std::find(
192 window
->parent()->children().begin(),
193 window
->parent()->children().end(),
195 DCHECK(window_i
!= window
->parent()->children().end());
196 if (window_i
!= window
->parent()->children().begin() &&
197 (*(window_i
- 1) == transient_manager
->stacking_target_
))
201 RestackTransientDescendants();
204 void TransientWindowManager::OnWindowDestroying(Window
* window
) {
205 // Removes ourselves from our transient parent (if it hasn't been done by the
207 if (transient_parent_
) {
208 TransientWindowManager::Get(transient_parent_
)->RemoveTransientChild(
212 // Destroy transient children, only after we've removed ourselves from our
213 // parent, as destroying an active transient child may otherwise attempt to
215 Windows
transient_children(transient_children_
);
216 STLDeleteElements(&transient_children
);
217 DCHECK(transient_children_
.empty());