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"
20 DECLARE_WINDOW_PROPERTY_TYPE(wm::TransientWindowManager
*);
25 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager
, kPropertyKey
, NULL
);
29 TransientWindowManager::~TransientWindowManager() {
33 TransientWindowManager
* TransientWindowManager::Get(Window
* window
) {
34 TransientWindowManager
* manager
= window
->GetProperty(kPropertyKey
);
36 manager
= new TransientWindowManager(window
);
37 window
->SetProperty(kPropertyKey
, manager
);
43 const TransientWindowManager
* TransientWindowManager::Get(
44 const Window
* window
) {
45 return window
->GetProperty(kPropertyKey
);
48 void TransientWindowManager::AddObserver(TransientWindowObserver
* observer
) {
49 observers_
.AddObserver(observer
);
52 void TransientWindowManager::RemoveObserver(TransientWindowObserver
* observer
) {
53 observers_
.RemoveObserver(observer
);
56 void TransientWindowManager::AddTransientChild(Window
* child
) {
57 // TransientWindowStackingClient does the stacking of transient windows. If it
58 // isn't installed stacking is going to be wrong.
59 DCHECK(TransientWindowStackingClient::instance_
);
61 TransientWindowManager
* child_manager
= Get(child
);
62 if (child_manager
->transient_parent_
)
63 Get(child_manager
->transient_parent_
)->RemoveTransientChild(child
);
64 DCHECK(std::find(transient_children_
.begin(), transient_children_
.end(),
65 child
) == transient_children_
.end());
66 transient_children_
.push_back(child
);
67 child_manager
->transient_parent_
= window_
;
69 // Restack |child| properly above its transient parent, if they share the same
71 if (child
->parent() == window_
->parent())
72 RestackTransientDescendants();
74 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
75 OnTransientChildAdded(window_
, child
));
78 void TransientWindowManager::RemoveTransientChild(Window
* child
) {
80 std::find(transient_children_
.begin(), transient_children_
.end(), child
);
81 DCHECK(i
!= transient_children_
.end());
82 transient_children_
.erase(i
);
83 TransientWindowManager
* child_manager
= Get(child
);
84 DCHECK_EQ(window_
, child_manager
->transient_parent_
);
85 child_manager
->transient_parent_
= NULL
;
87 // If |child| and its former transient parent share the same parent, |child|
88 // should be restacked properly so it is not among transient children of its
89 // former parent, anymore.
90 if (window_
->parent() == child
->parent())
91 RestackTransientDescendants();
93 FOR_EACH_OBSERVER(TransientWindowObserver
, observers_
,
94 OnTransientChildRemoved(window_
, child
));
97 bool TransientWindowManager::IsStackingTransient(
98 const aura::Window
* target
) const {
99 return stacking_target_
== target
;
102 TransientWindowManager::TransientWindowManager(Window
* window
)
104 transient_parent_(NULL
),
105 stacking_target_(NULL
),
106 parent_controls_visibility_(false),
107 show_on_parent_visible_(false),
108 ignore_visibility_changed_event_(false) {
109 window_
->AddObserver(this);
112 void TransientWindowManager::RestackTransientDescendants() {
113 Window
* parent
= window_
->parent();
117 // Stack any transient children that share the same parent to be in front of
118 // |window_|. The existing stacking order is preserved by iterating backwards
119 // and always stacking on top.
120 Window::Windows
children(parent
->children());
121 for (Window::Windows::reverse_iterator it
= children
.rbegin();
122 it
!= children
.rend(); ++it
) {
123 if ((*it
) != window_
&& HasTransientAncestor(*it
, window_
)) {
124 TransientWindowManager
* descendant_manager
= Get(*it
);
125 base::AutoReset
<Window
*> resetter(
126 &descendant_manager
->stacking_target_
,
128 parent
->StackChildAbove((*it
), window_
);
133 void TransientWindowManager::OnWindowParentChanged(aura::Window
* window
,
134 aura::Window
* parent
) {
135 DCHECK_EQ(window_
, window
);
136 // Stack |window| properly if it is transient child of a sibling.
137 Window
* transient_parent
= wm::GetTransientParent(window
);
138 if (transient_parent
&& transient_parent
->parent() == parent
) {
139 TransientWindowManager
* transient_parent_manager
=
140 Get(transient_parent
);
141 transient_parent_manager
->RestackTransientDescendants();
145 void TransientWindowManager::UpdateTransientChildVisibility(
146 bool parent_visible
) {
147 base::AutoReset
<bool> reset(&ignore_visibility_changed_event_
, true);
148 if (!parent_visible
) {
149 show_on_parent_visible_
= window_
->TargetVisibility();
152 if (show_on_parent_visible_
&& parent_controls_visibility_
)
154 show_on_parent_visible_
= false;
158 void TransientWindowManager::OnWindowVisibilityChanged(Window
* window
,
160 if (window_
!= window
)
163 // If the window has transient children, updates the transient children's
164 // visiblity as well.
165 for (Window
* child
: transient_children_
)
166 Get(child
)->UpdateTransientChildVisibility(visible
);
168 // Remember the show request in |show_on_parent_visible_| and hide it again
169 // if the following conditions are met
170 // - |parent_controls_visibility| is set to true.
171 // - the window is hidden while the transient parent is not visible.
172 // - Show/Hide was NOT called from TransientWindowManager.
173 if (ignore_visibility_changed_event_
||
174 !transient_parent_
|| !parent_controls_visibility_
) {
178 if (!transient_parent_
->TargetVisibility() && visible
) {
179 base::AutoReset
<bool> reset(&ignore_visibility_changed_event_
, true);
180 show_on_parent_visible_
= true;
182 } else if (!visible
) {
183 DCHECK(!show_on_parent_visible_
);
187 void TransientWindowManager::OnWindowStackingChanged(Window
* window
) {
188 DCHECK_EQ(window_
, window
);
189 // Do nothing if we initiated the stacking change.
190 const TransientWindowManager
* transient_manager
=
191 Get(static_cast<const Window
*>(window
));
192 if (transient_manager
&& transient_manager
->stacking_target_
) {
193 Windows::const_iterator window_i
= std::find(
194 window
->parent()->children().begin(),
195 window
->parent()->children().end(),
197 DCHECK(window_i
!= window
->parent()->children().end());
198 if (window_i
!= window
->parent()->children().begin() &&
199 (*(window_i
- 1) == transient_manager
->stacking_target_
))
203 RestackTransientDescendants();
206 void TransientWindowManager::OnWindowDestroying(Window
* window
) {
207 // Removes ourselves from our transient parent (if it hasn't been done by the
209 if (transient_parent_
) {
210 TransientWindowManager::Get(transient_parent_
)->RemoveTransientChild(
214 // Destroy transient children, only after we've removed ourselves from our
215 // parent, as destroying an active transient child may otherwise attempt to
217 Windows
transient_children(transient_children_
);
218 STLDeleteElements(&transient_children
);
219 DCHECK(transient_children_
.empty());