File: window_reorderer.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (222 lines) | stat: -rw-r--r-- 7,168 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/widget/window_reorderer.h"

#include <stddef.h>

#include <algorithm>
#include <map>
#include <set>
#include <vector>

#include "base/containers/adapters.h"
#include "base/debug/crash_logging.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "ui/aura/window.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/compositor/layer.h"
#include "ui/views/view.h"
#include "ui/views/view_constants_aura.h"

namespace views {

namespace {

// Sets |hosted_windows| to a mapping of the views with an associated window to
// the window that they are associated to. Only views associated to a child of
// |parent_window| are returned.
void GetViewsWithAssociatedWindow(
    const aura::Window& parent_window,
    std::map<views::View*, aura::Window*>* hosted_windows) {
  for (aura::Window* child : parent_window.children()) {
    View* host_view = child->GetProperty(kHostViewKey);
    if (host_view) {
      (*hosted_windows)[host_view] = child;
    }
  }
}

// Sets |order| to the list of views whose layer / associated window's layer
// is a child of |parent_layer|. |order| is sorted in ascending z-order of
// the views.
// |hosts| are the views with an associated window whose layer is a child of
// |parent_layer|.
void GetOrderOfViewsWithLayers(
    views::View* view,
    ui::Layer* parent_layer,
    const std::map<views::View*, aura::Window*>& hosts,
    std::vector<views::View*>* order) {
  DCHECK(view);
  SCOPED_CRASH_KEY_STRING64("GetOrderOfViewsWithLayers", "view_name",
                            view->GetObjectName());

  DCHECK(parent_layer);
  DCHECK(order);
  if (view->layer() && view->layer()->parent() == parent_layer) {
    order->push_back(view);
    // |hosts| may contain a child of |view|.
  } else if (hosts.find(view) != hosts.end()) {
    order->push_back(view);
  }

  for (views::View* child : view->GetChildrenInZOrder()) {
    GetOrderOfViewsWithLayers(child, parent_layer, hosts, order);
  }
}

}  // namespace

// Class which reorders windows as a result of the kHostViewKey property being
// set on the window.
class WindowReorderer::AssociationObserver : public aura::WindowObserver {
 public:
  explicit AssociationObserver(WindowReorderer* reorderer);

  AssociationObserver(const AssociationObserver&) = delete;
  AssociationObserver& operator=(const AssociationObserver&) = delete;

  ~AssociationObserver() override;

  // Start/stop observing changes in the kHostViewKey property on |window|.
  void StartObserving(aura::Window* window);
  void StopObserving(aura::Window* window);

 private:
  // aura::WindowObserver overrides:
  void OnWindowPropertyChanged(aura::Window* window,
                               const void* key,
                               intptr_t old) override;
  void OnWindowDestroying(aura::Window* window) override;

  // Not owned.
  raw_ptr<WindowReorderer> reorderer_;

  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
      window_observations_{this};
};

WindowReorderer::AssociationObserver::AssociationObserver(
    WindowReorderer* reorderer)
    : reorderer_(reorderer) {}

WindowReorderer::AssociationObserver::~AssociationObserver() = default;

void WindowReorderer::AssociationObserver::StartObserving(
    aura::Window* window) {
  window_observations_.AddObservation(window);
}

void WindowReorderer::AssociationObserver::StopObserving(aura::Window* window) {
  if (window_observations_.IsObservingSource(window)) {
    window_observations_.RemoveObservation(window);
  }
}

void WindowReorderer::AssociationObserver::OnWindowPropertyChanged(
    aura::Window* window,
    const void* key,
    intptr_t old) {
  if (key == kHostViewKey) {
    reorderer_->ReorderChildWindows();
  }
}

void WindowReorderer::AssociationObserver::OnWindowDestroying(
    aura::Window* window) {
  StopObserving(window);
}

WindowReorderer::WindowReorderer(aura::Window* parent_window, View* root_view)
    : association_observer_(new AssociationObserver(this)) {
  view_observation_.Observe(root_view);
  parent_window_observation_.Observe(parent_window);
  for (aura::Window* window : parent_window->children()) {
    association_observer_->StartObserving(window);
  }
  ReorderChildWindows();
}

WindowReorderer::~WindowReorderer() = default;

void WindowReorderer::ReorderChildWindows() {
  if (!parent_window_observation_.IsObserving() ||
      !view_observation_.IsObserving()) {
    return;
  }

  aura::Window& parent_window = *parent_window_observation_.GetSource();
  std::map<View*, aura::Window*> hosted_windows;
  GetViewsWithAssociatedWindow(parent_window, &hosted_windows);

  if (hosted_windows.empty()) {
    // Exit early if there are no views with associated windows.
    // View::ReorderLayers() should have already reordered the layers owned by
    // views.
    return;
  }

  // Compute the desired z-order of the layers based on the order of the views
  // with layers and views with associated windows in the view tree.
  View* root_view = view_observation_.GetSource();
  std::vector<View*> view_with_layer_order;
  GetOrderOfViewsWithLayers(root_view, parent_window.layer(), hosted_windows,
                            &view_with_layer_order);

  std::vector<ui::Layer*> children_layer_order;

  aura::WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;

  // For the sake of simplicity, reorder both the layers owned by views and the
  // layers of windows associated with a view. Iterate through
  // |view_with_layer_order| backwards and stack windows at the bottom so that
  // windows not associated to a view are stacked above windows with an
  // associated view.
  for (View* view : base::Reversed(view_with_layer_order)) {
    std::vector<ui::Layer*> layers;
    aura::Window* window = nullptr;

    auto hosted_window_it = hosted_windows.find(view);
    if (hosted_window_it != hosted_windows.end()) {
      window = hosted_window_it->second;
      layers.push_back(window->layer());
    } else {
      layers = view->GetLayersInOrder();
      std::reverse(layers.begin(), layers.end());
    }

    DCHECK(!layers.empty());
    if (window) {
      parent_window.StackChildAtBottom(window);
    }

    for (ui::Layer* layer : layers) {
      children_layer_order.emplace_back(layer);
    }
  }
  std::reverse(children_layer_order.begin(), children_layer_order.end());
  parent_window.layer()->StackChildrenAtBottom(children_layer_order);
}

void WindowReorderer::OnWindowAdded(aura::Window* new_window) {
  association_observer_->StartObserving(new_window);
  ReorderChildWindows();
}

void WindowReorderer::OnWillRemoveWindow(aura::Window* window) {
  association_observer_->StopObserving(window);
}

void WindowReorderer::OnWindowDestroying(aura::Window* window) {
  parent_window_observation_.Reset();
  association_observer_.reset();
}

void WindowReorderer::OnViewIsDeleting(View* observed_view) {
  DCHECK(view_observation_.IsObservingSource(observed_view));
  view_observation_.Reset();
}

}  // namespace views