| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "ui/wm/core/transient_window_manager.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <functional> | |
| 9 | |
| 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" | |
| 17 | |
| 18 using aura::Window; | |
| 19 | |
| 20 namespace wm { | |
| 21 namespace { | |
| 22 | |
| 23 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL); | |
| 24 | |
| 25 } // namespace | |
| 26 | |
| 27 TransientWindowManager::~TransientWindowManager() { | |
| 28 } | |
| 29 | |
| 30 // static | |
| 31 TransientWindowManager* TransientWindowManager::Get(Window* window) { | |
| 32 TransientWindowManager* manager = window->GetProperty(kPropertyKey); | |
| 33 if (!manager) { | |
| 34 manager = new TransientWindowManager(window); | |
| 35 window->SetProperty(kPropertyKey, manager); | |
| 36 } | |
| 37 return manager; | |
| 38 } | |
| 39 | |
| 40 // static | |
| 41 const TransientWindowManager* TransientWindowManager::Get( | |
| 42 const Window* window) { | |
| 43 return window->GetProperty(kPropertyKey); | |
| 44 } | |
| 45 | |
| 46 void TransientWindowManager::AddObserver(TransientWindowObserver* observer) { | |
| 47 observers_.AddObserver(observer); | |
| 48 } | |
| 49 | |
| 50 void TransientWindowManager::RemoveObserver(TransientWindowObserver* observer) { | |
| 51 observers_.RemoveObserver(observer); | |
| 52 } | |
| 53 | |
| 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_); | |
| 58 | |
| 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_; | |
| 66 | |
| 67 // Restack |child| properly above its transient parent, if they share the same | |
| 68 // parent. | |
| 69 if (child->parent() == window_->parent()) | |
| 70 RestackTransientDescendants(); | |
| 71 | |
| 72 FOR_EACH_OBSERVER(TransientWindowObserver, observers_, | |
| 73 OnTransientChildAdded(window_, child)); | |
| 74 } | |
| 75 | |
| 76 void TransientWindowManager::RemoveTransientChild(Window* child) { | |
| 77 Windows::iterator i = | |
| 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; | |
| 84 | |
| 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(); | |
| 90 | |
| 91 FOR_EACH_OBSERVER(TransientWindowObserver, observers_, | |
| 92 OnTransientChildRemoved(window_, child)); | |
| 93 } | |
| 94 | |
| 95 bool TransientWindowManager::IsStackingTransient( | |
| 96 const aura::Window* target) const { | |
| 97 return stacking_target_ == target; | |
| 98 } | |
| 99 | |
| 100 TransientWindowManager::TransientWindowManager(Window* window) | |
| 101 : 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); | |
| 108 } | |
| 109 | |
| 110 void TransientWindowManager::RestackTransientDescendants() { | |
| 111 Window* parent = window_->parent(); | |
| 112 if (!parent) | |
| 113 return; | |
| 114 | |
| 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_, | |
| 125 window_); | |
| 126 parent->StackChildAbove((*it), window_); | |
| 127 } | |
| 128 } | |
| 129 } | |
| 130 | |
| 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(); | |
| 140 } | |
| 141 } | |
| 142 | |
| 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(); | |
| 148 window_->Hide(); | |
| 149 } else { | |
| 150 if (show_on_parent_visible_ && parent_controls_visibility_) | |
| 151 window_->Show(); | |
| 152 show_on_parent_visible_ = false; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void TransientWindowManager::OnWindowVisibilityChanging(Window* window, | |
| 157 bool visible) { | |
| 158 DCHECK_EQ(window_, window); | |
| 159 | |
| 160 for (auto* child : transient_children_) | |
| 161 Get(child)->UpdateTransientChildVisibility(visible); | |
| 162 } | |
| 163 | |
| 164 void TransientWindowManager::OnWindowVisibilityChanged(Window* window, | |
| 165 bool visible) { | |
| 166 if (window_ != window || ignore_visibility_changed_event_ || | |
| 167 !transient_parent_ || !parent_controls_visibility_) { | |
| 168 return; | |
| 169 } | |
| 170 if (!transient_parent_->TargetVisibility() && visible) { | |
| 171 base::AutoReset<bool> reset(&ignore_visibility_changed_event_, true); | |
| 172 show_on_parent_visible_ = true; | |
| 173 window_->Hide(); | |
| 174 } else if (!visible) { | |
| 175 DCHECK(!show_on_parent_visible_); | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 void TransientWindowManager::OnWindowStackingChanged(Window* window) { | |
| 180 DCHECK_EQ(window_, window); | |
| 181 // Do nothing if we initiated the stacking change. | |
| 182 const TransientWindowManager* transient_manager = | |
| 183 Get(static_cast<const Window*>(window)); | |
| 184 if (transient_manager && transient_manager->stacking_target_) { | |
| 185 Windows::const_iterator window_i = std::find( | |
| 186 window->parent()->children().begin(), | |
| 187 window->parent()->children().end(), | |
| 188 window); | |
| 189 DCHECK(window_i != window->parent()->children().end()); | |
| 190 if (window_i != window->parent()->children().begin() && | |
| 191 (*(window_i - 1) == transient_manager->stacking_target_)) | |
| 192 return; | |
| 193 } | |
| 194 | |
| 195 RestackTransientDescendants(); | |
| 196 } | |
| 197 | |
| 198 void TransientWindowManager::OnWindowDestroying(Window* window) { | |
| 199 // Removes ourselves from our transient parent (if it hasn't been done by the | |
| 200 // RootWindow). | |
| 201 if (transient_parent_) { | |
| 202 TransientWindowManager::Get(transient_parent_)->RemoveTransientChild( | |
| 203 window_); | |
| 204 } | |
| 205 | |
| 206 // Destroy transient children, only after we've removed ourselves from our | |
| 207 // parent, as destroying an active transient child may otherwise attempt to | |
| 208 // refocus us. | |
| 209 Windows transient_children(transient_children_); | |
| 210 STLDeleteElements(&transient_children); | |
| 211 DCHECK(transient_children_.empty()); | |
| 212 } | |
| 213 | |
| 214 } // namespace wm | |
| OLD | NEW |