Home | History | Annotate | Download | only in core
      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