diff options
Diffstat (limited to 'chromium/ui/wm/core/transient_window_manager.cc')
-rw-r--r-- | chromium/ui/wm/core/transient_window_manager.cc | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/chromium/ui/wm/core/transient_window_manager.cc b/chromium/ui/wm/core/transient_window_manager.cc new file mode 100644 index 00000000000..120af84786c --- /dev/null +++ b/chromium/ui/wm/core/transient_window_manager.cc @@ -0,0 +1,182 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/wm/core/transient_window_manager.h" + +#include <algorithm> +#include <functional> + +#include "base/auto_reset.h" +#include "base/stl_util.h" +#include "ui/aura/window.h" +#include "ui/aura/window_property.h" +#include "ui/wm/core/transient_window_observer.h" +#include "ui/wm/core/transient_window_stacking_client.h" +#include "ui/wm/core/window_util.h" + +using aura::Window; + +namespace wm { + +DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL); + +TransientWindowManager::~TransientWindowManager() { +} + +// static +TransientWindowManager* TransientWindowManager::Get(Window* window) { + TransientWindowManager* manager = window->GetProperty(kPropertyKey); + if (!manager) { + manager = new TransientWindowManager(window); + window->SetProperty(kPropertyKey, manager); + } + return manager; +} + +// static +const TransientWindowManager* TransientWindowManager::Get( + const Window* window) { + return window->GetProperty(kPropertyKey); +} + +void TransientWindowManager::AddObserver(TransientWindowObserver* observer) { + observers_.AddObserver(observer); +} + +void TransientWindowManager::RemoveObserver(TransientWindowObserver* observer) { + observers_.RemoveObserver(observer); +} + +void TransientWindowManager::AddTransientChild(Window* child) { + // TransientWindowStackingClient does the stacking of transient windows. If it + // isn't installed stacking is going to be wrong. + DCHECK(TransientWindowStackingClient::instance_); + + TransientWindowManager* child_manager = Get(child); + if (child_manager->transient_parent_) + Get(child_manager->transient_parent_)->RemoveTransientChild(child); + DCHECK(std::find(transient_children_.begin(), transient_children_.end(), + child) == transient_children_.end()); + transient_children_.push_back(child); + child_manager->transient_parent_ = window_; + + // Restack |child| properly above its transient parent, if they share the same + // parent. + if (child->parent() == window_->parent()) + RestackTransientDescendants(); + + FOR_EACH_OBSERVER(TransientWindowObserver, observers_, + OnTransientChildAdded(window_, child)); +} + +void TransientWindowManager::RemoveTransientChild(Window* child) { + Windows::iterator i = + std::find(transient_children_.begin(), transient_children_.end(), child); + DCHECK(i != transient_children_.end()); + transient_children_.erase(i); + TransientWindowManager* child_manager = Get(child); + DCHECK_EQ(window_, child_manager->transient_parent_); + child_manager->transient_parent_ = NULL; + + // If |child| and its former transient parent share the same parent, |child| + // should be restacked properly so it is not among transient children of its + // former parent, anymore. + if (window_->parent() == child->parent()) + RestackTransientDescendants(); + + FOR_EACH_OBSERVER(TransientWindowObserver, observers_, + OnTransientChildRemoved(window_, child)); +} + +bool TransientWindowManager::IsStackingTransient( + const aura::Window* target) const { + return stacking_target_ == target; +} + +TransientWindowManager::TransientWindowManager(Window* window) + : window_(window), + transient_parent_(NULL), + stacking_target_(NULL) { + window_->AddObserver(this); +} + +void TransientWindowManager::RestackTransientDescendants() { + Window* parent = window_->parent(); + if (!parent) + return; + + // Stack any transient children that share the same parent to be in front of + // |window_|. The existing stacking order is preserved by iterating backwards + // and always stacking on top. + Window::Windows children(parent->children()); + for (Window::Windows::reverse_iterator it = children.rbegin(); + it != children.rend(); ++it) { + if ((*it) != window_ && HasTransientAncestor(*it, window_)) { + TransientWindowManager* descendant_manager = Get(*it); + base::AutoReset<Window*> resetter( + &descendant_manager->stacking_target_, + window_); + parent->StackChildAbove((*it), window_); + } + } +} + +void TransientWindowManager::OnWindowParentChanged(aura::Window* window, + aura::Window* parent) { + DCHECK_EQ(window_, window); + // Stack |window| properly if it is transient child of a sibling. + Window* transient_parent = wm::GetTransientParent(window); + if (transient_parent && transient_parent->parent() == parent) { + TransientWindowManager* transient_parent_manager = + Get(transient_parent); + transient_parent_manager->RestackTransientDescendants(); + } +} + +void TransientWindowManager::OnWindowVisibilityChanging(Window* window, + bool visible) { + // TODO(sky): move handling of becoming visible here. + if (!visible) { + std::for_each(transient_children_.begin(), transient_children_.end(), + std::mem_fun(&Window::Hide)); + } +} + +void TransientWindowManager::OnWindowStackingChanged(Window* window) { + DCHECK_EQ(window_, window); + + // Do nothing if we initiated the stacking change. + const TransientWindowManager* transient_manager = + Get(static_cast<const Window*>(window)); + if (transient_manager && transient_manager->stacking_target_) { + Windows::const_iterator window_i = std::find( + window->parent()->children().begin(), + window->parent()->children().end(), + window); + DCHECK(window_i != window->parent()->children().end()); + if (window_i != window->parent()->children().begin() && + (*(window_i - 1) == transient_manager->stacking_target_)) + return; + } + + RestackTransientDescendants(); +} + +void TransientWindowManager::OnWindowDestroying(Window* window) { + // Removes ourselves from our transient parent (if it hasn't been done by the + // RootWindow). + if (transient_parent_) { + TransientWindowManager::Get(transient_parent_)->RemoveTransientChild( + window_); + } + + // Destroy transient children, only after we've removed ourselves from our + // parent, as destroying an active transient child may otherwise attempt to + // refocus us. + Windows transient_children(transient_children_); + STLDeleteElements(&transient_children); + DCHECK(transient_children_.empty()); +} + +} // namespace wm |