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