1// Copyright 2013 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/views/widget/window_reorderer.h"
6
7#include <map>
8#include <vector>
9
10#include "ui/aura/window.h"
11#include "ui/views/view.h"
12#include "ui/views/view_constants_aura.h"
13
14namespace views {
15
16namespace {
17
18// Sets |hosted_windows| to a mapping of the views with an associated window to
19// the window that they are associated to. Only views associated to a child of
20// |parent_window| are returned.
21void GetViewsWithAssociatedWindow(
22    const aura::Window& parent_window,
23    std::map<views::View*, aura::Window*>* hosted_windows) {
24  const std::vector<aura::Window*>& child_windows = parent_window.children();
25  for (size_t i = 0; i < child_windows.size(); ++i) {
26    aura::Window* child = child_windows[i];
27    View* host_view = child->GetProperty(kHostViewKey);
28    if (host_view)
29      (*hosted_windows)[host_view] = child;
30  }
31}
32
33// Sets |order| to the list of views whose layer / associated window's layer
34// is a child of |parent_layer|. |order| is sorted in ascending z-order of
35// the views.
36// |hosts| are the views with an associated window whose layer is a child of
37// |parent_layer|.
38void GetOrderOfViewsWithLayers(
39    views::View* view,
40    ui::Layer* parent_layer,
41    const std::map<views::View*, aura::Window*>& hosts,
42    std::vector<views::View*>* order) {
43  DCHECK(view);
44  DCHECK(parent_layer);
45  DCHECK(order);
46  if (view->layer() && view->layer()->parent() == parent_layer) {
47    order->push_back(view);
48    // |hosts| may contain a child of |view|.
49  } else if (hosts.find(view) != hosts.end()) {
50    order->push_back(view);
51  }
52
53  for (int i = 0; i < view->child_count(); ++i)
54    GetOrderOfViewsWithLayers(view->child_at(i), parent_layer, hosts, order);
55}
56
57}  // namespace
58
59// Class which reorders windows as a result of the kHostViewKey property being
60// set on the window.
61class WindowReorderer::AssociationObserver : public aura::WindowObserver {
62 public:
63  explicit AssociationObserver(WindowReorderer* reorderer);
64  virtual ~AssociationObserver();
65
66  // Start/stop observing changes in the kHostViewKey property on |window|.
67  void StartObserving(aura::Window* window);
68  void StopObserving(aura::Window* window);
69
70 private:
71  // aura::WindowObserver overrides:
72  virtual void OnWindowPropertyChanged(aura::Window* window,
73                                      const void* key,
74                                      intptr_t old) OVERRIDE;
75  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
76
77  // Not owned.
78  WindowReorderer* reorderer_;
79
80  std::set<aura::Window*> windows_;
81
82  DISALLOW_COPY_AND_ASSIGN(AssociationObserver);
83};
84
85WindowReorderer::AssociationObserver::AssociationObserver(
86    WindowReorderer* reorderer)
87    : reorderer_(reorderer) {
88}
89
90WindowReorderer::AssociationObserver::~AssociationObserver() {
91  while (!windows_.empty())
92    StopObserving(*windows_.begin());
93}
94
95void WindowReorderer::AssociationObserver::StartObserving(
96    aura::Window* window) {
97  windows_.insert(window);
98  window->AddObserver(this);
99}
100
101void WindowReorderer::AssociationObserver::StopObserving(
102    aura::Window* window) {
103  windows_.erase(window);
104  window->RemoveObserver(this);
105}
106
107void WindowReorderer::AssociationObserver::OnWindowPropertyChanged(
108    aura::Window* window,
109    const void* key,
110    intptr_t old) {
111  if (key == kHostViewKey)
112    reorderer_->ReorderChildWindows();
113}
114
115void WindowReorderer::AssociationObserver::OnWindowDestroying(
116    aura::Window* window) {
117  windows_.erase(window);
118  window->RemoveObserver(this);
119}
120
121WindowReorderer::WindowReorderer(aura::Window* parent_window,
122                                 View* root_view)
123    : parent_window_(parent_window),
124      root_view_(root_view),
125      association_observer_(new AssociationObserver(this)) {
126  parent_window_->AddObserver(this);
127  const std::vector<aura::Window*>& windows = parent_window_->children();
128  for (size_t i = 0; i < windows.size(); ++i)
129    association_observer_->StartObserving(windows[i]);
130  ReorderChildWindows();
131}
132
133WindowReorderer::~WindowReorderer() {
134  if (parent_window_) {
135    parent_window_->RemoveObserver(this);
136    // |association_observer_| stops observing any windows it is observing upon
137    // destruction.
138  }
139}
140
141void WindowReorderer::ReorderChildWindows() {
142  if (!parent_window_)
143    return;
144
145  std::map<View*, aura::Window*> hosted_windows;
146  GetViewsWithAssociatedWindow(*parent_window_, &hosted_windows);
147
148  if (hosted_windows.empty()) {
149    // Exit early if there are no views with associated windows.
150    // View::ReorderLayers() should have already reordered the layers owned by
151    // views.
152    return;
153  }
154
155  // Compute the desired z-order of the layers based on the order of the views
156  // with layers and views with associated windows in the view tree.
157  std::vector<View*> view_with_layer_order;
158  GetOrderOfViewsWithLayers(root_view_, parent_window_->layer(), hosted_windows,
159      &view_with_layer_order);
160
161  // For the sake of simplicity, reorder both the layers owned by views and the
162  // layers of windows associated with a view. Iterate through
163  // |view_with_layer_order| backwards and stack windows at the bottom so that
164  // windows not associated to a view are stacked above windows with an
165  // associated view.
166  for (std::vector<View*>::reverse_iterator it = view_with_layer_order.rbegin();
167       it != view_with_layer_order.rend(); ++it) {
168    View* view = *it;
169    ui::Layer* layer = view->layer();
170    aura::Window* window = NULL;
171
172    std::map<View*, aura::Window*>::iterator hosted_window_it =
173        hosted_windows.find(view);
174    if (hosted_window_it != hosted_windows.end()) {
175      window = hosted_window_it->second;
176      layer = window->layer();
177    }
178
179    DCHECK(layer);
180    if (window)
181      parent_window_->StackChildAtBottom(window);
182    parent_window_->layer()->StackAtBottom(layer);
183  }
184}
185
186void WindowReorderer::OnWindowAdded(aura::Window* new_window) {
187  association_observer_->StartObserving(new_window);
188  ReorderChildWindows();
189}
190
191void WindowReorderer::OnWillRemoveWindow(aura::Window* window) {
192  association_observer_->StopObserving(window);
193}
194
195void WindowReorderer::OnWindowDestroying(aura::Window* window) {
196  parent_window_->RemoveObserver(this);
197  parent_window_ = NULL;
198  association_observer_.reset();
199}
200
201}  // namespace views
202