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