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 22 DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL); 23 24 TransientWindowManager::~TransientWindowManager() { 25 } 26 27 // static 28 TransientWindowManager* TransientWindowManager::Get(Window* window) { 29 TransientWindowManager* manager = window->GetProperty(kPropertyKey); 30 if (!manager) { 31 manager = new TransientWindowManager(window); 32 window->SetProperty(kPropertyKey, manager); 33 } 34 return manager; 35 } 36 37 // static 38 const TransientWindowManager* TransientWindowManager::Get( 39 const Window* window) { 40 return window->GetProperty(kPropertyKey); 41 } 42 43 void TransientWindowManager::AddObserver(TransientWindowObserver* observer) { 44 observers_.AddObserver(observer); 45 } 46 47 void TransientWindowManager::RemoveObserver(TransientWindowObserver* observer) { 48 observers_.RemoveObserver(observer); 49 } 50 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_); 55 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_; 63 64 // Restack |child| properly above its transient parent, if they share the same 65 // parent. 66 if (child->parent() == window_->parent()) 67 RestackTransientDescendants(); 68 69 FOR_EACH_OBSERVER(TransientWindowObserver, observers_, 70 OnTransientChildAdded(window_, child)); 71 } 72 73 void TransientWindowManager::RemoveTransientChild(Window* child) { 74 Windows::iterator i = 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; 81 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(); 87 88 FOR_EACH_OBSERVER(TransientWindowObserver, observers_, 89 OnTransientChildRemoved(window_, child)); 90 } 91 92 bool TransientWindowManager::IsStackingTransient( 93 const aura::Window* target) const { 94 return stacking_target_ == target; 95 } 96 97 TransientWindowManager::TransientWindowManager(Window* window) 98 : window_(window), 99 transient_parent_(NULL), 100 stacking_target_(NULL) { 101 window_->AddObserver(this); 102 } 103 104 void TransientWindowManager::RestackTransientDescendants() { 105 Window* parent = window_->parent(); 106 if (!parent) 107 return; 108 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_, 119 window_); 120 parent->StackChildAbove((*it), window_); 121 } 122 } 123 } 124 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(); 134 } 135 } 136 137 void TransientWindowManager::OnWindowVisibilityChanging(Window* window, 138 bool visible) { 139 // TODO(sky): move handling of becoming visible here. 140 if (!visible) { 141 std::for_each(transient_children_.begin(), transient_children_.end(), 142 std::mem_fun(&Window::Hide)); 143 } 144 } 145 146 void TransientWindowManager::OnWindowStackingChanged(Window* window) { 147 DCHECK_EQ(window_, window); 148 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(), 156 window); 157 DCHECK(window_i != window->parent()->children().end()); 158 if (window_i != window->parent()->children().begin() && 159 (*(window_i - 1) == transient_manager->stacking_target_)) 160 return; 161 } 162 163 RestackTransientDescendants(); 164 } 165 166 void TransientWindowManager::OnWindowDestroying(Window* window) { 167 // Removes ourselves from our transient parent (if it hasn't been done by the 168 // RootWindow). 169 if (transient_parent_) { 170 TransientWindowManager::Get(transient_parent_)->RemoveTransientChild( 171 window_); 172 } 173 174 // Destroy transient children, only after we've removed ourselves from our 175 // parent, as destroying an active transient child may otherwise attempt to 176 // refocus us. 177 Windows transient_children(transient_children_); 178 STLDeleteElements(&transient_children); 179 DCHECK(transient_children_.empty()); 180 } 181 182 } // namespace wm 183