Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / wm / core / transient_window_manager.cc
blob2ca145d2420dbdd9f9a2fc81fd785e8e64d7a495
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"
7 #include <algorithm>
8 #include <functional>
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"
18 using aura::Window;
20 DECLARE_WINDOW_PROPERTY_TYPE(wm::TransientWindowManager*);
22 namespace wm {
23 namespace {
25 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL);
27 } // namespace
29 TransientWindowManager::~TransientWindowManager() {
32 // static
33 TransientWindowManager* TransientWindowManager::Get(Window* window) {
34 TransientWindowManager* manager = window->GetProperty(kPropertyKey);
35 if (!manager) {
36 manager = new TransientWindowManager(window);
37 window->SetProperty(kPropertyKey, manager);
39 return manager;
42 // static
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
70 // parent.
71 if (child->parent() == window_->parent())
72 RestackTransientDescendants();
74 FOR_EACH_OBSERVER(TransientWindowObserver, observers_,
75 OnTransientChildAdded(window_, child));
78 void TransientWindowManager::RemoveTransientChild(Window* child) {
79 Windows::iterator i =
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)
103 : 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();
114 if (!parent)
115 return;
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_,
127 window_);
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();
150 window_->Hide();
151 } else {
152 if (show_on_parent_visible_ && parent_controls_visibility_)
153 window_->Show();
154 show_on_parent_visible_ = false;
158 void TransientWindowManager::OnWindowVisibilityChanged(Window* window,
159 bool visible) {
160 if (window_ != window)
161 return;
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_) {
175 return;
178 if (!transient_parent_->TargetVisibility() && visible) {
179 base::AutoReset<bool> reset(&ignore_visibility_changed_event_, true);
180 show_on_parent_visible_ = true;
181 window_->Hide();
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(),
196 window);
197 DCHECK(window_i != window->parent()->children().end());
198 if (window_i != window->parent()->children().begin() &&
199 (*(window_i - 1) == transient_manager->stacking_target_))
200 return;
203 RestackTransientDescendants();
206 void TransientWindowManager::OnWindowDestroying(Window* window) {
207 // Removes ourselves from our transient parent (if it hasn't been done by the
208 // RootWindow).
209 if (transient_parent_) {
210 TransientWindowManager::Get(transient_parent_)->RemoveTransientChild(
211 window_);
214 // Destroy transient children, only after we've removed ourselves from our
215 // parent, as destroying an active transient child may otherwise attempt to
216 // refocus us.
217 Windows transient_children(transient_children_);
218 STLDeleteElements(&transient_children);
219 DCHECK(transient_children_.empty());
222 } // namespace wm