Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: chromecast/graphics/cast_focus_client_aura.cc

Issue 2636303002: [Chromecast] Add support for z-order and window focus. (Closed)
Patch Set: Simplifications and unit tests. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "chromecast/graphics/cast_focus_client_aura.h"
6
7 #include "ui/aura/window.h"
8
9 #define LOG_WINDOW_INFO(top_level, window) \
10 "top-level: " << (top_level)->id() << ": '" << (top_level)->GetName() \
11 << "', window: " << (window)->id() << ": " \
12 << (window)->GetName()
13
14 namespace chromecast {
15
16 CastFocusClientAura::CastFocusClientAura() : focused_window_(nullptr) {}
17
18 CastFocusClientAura::~CastFocusClientAura() {
19 reset();
20 }
21
22 void CastFocusClientAura::reset() {
23 focused_window_ = nullptr;
24 for (auto it = focusable_windows_.begin(); it != focusable_windows_.end();
25 ++it) {
halliwell 2017/01/24 00:54:46 minor: can this be a range-based loop?
Joshua LeVasseur 2017/01/24 03:39:50 Done.
26 (*it)->RemoveObserver(this);
27 }
28 focusable_windows_.clear();
29 }
30
31 aura::Window* CastFocusClientAura::GetFocusedWindow() {
32 return focused_window_;
33 }
34
35 void CastFocusClientAura::AddObserver(
36 aura::client::FocusChangeObserver* observer) {
37 focus_observers_.AddObserver(observer);
38 }
39
40 void CastFocusClientAura::RemoveObserver(
41 aura::client::FocusChangeObserver* observer) {
42 focus_observers_.RemoveObserver(observer);
43 }
44
45 void CastFocusClientAura::OnWindowVisibilityChanged(aura::Window* window,
46 bool visible) {
47 if (!visible && (window == focused_window_)) {
48 // The focused window just lost visibility, so de-focus it.
49 UpdateWindowFocus();
50 } else if (visible) {
51 // The window that just became visible might be the most appropriate window
52 // to have focus.
53 UpdateWindowFocus();
54 }
55 }
56
57 // One of our observed windows is being destroyed.
58 // We observe each window that has the potential for being focused,
59 // so this window needs to be removed from the list of focusable windows.
60 void CastFocusClientAura::OnWindowDestroying(aura::Window* window) {
61 aura::Window* top_level = GetTopLevelWindow(window);
62 DCHECK(top_level);
63 LOG(INFO) << "Removing window, " << LOG_WINDOW_INFO(top_level, window);
64
65 auto iter =
66 std::find(focusable_windows_.begin(), focusable_windows_.end(), window);
67 if (iter != focusable_windows_.end()) {
68 focusable_windows_.erase(iter);
69 window->RemoveObserver(this);
70 }
71 if (window == focused_window_) {
72 // De-focus the window that is being destroyed.
73 UpdateWindowFocus();
74 }
75 }
76
77 // Update focus if a window is entering or leaving our hierarchy.
78 void CastFocusClientAura::OnWindowHierarchyChanging(
79 const HierarchyChangeParams& params) {
80 if (params.new_parent &&
81 (aura::client::GetFocusClient(params.new_parent) == this)) {
82 if (params.old_parent == params.new_parent) {
83 // A window is moving within our hierarchy.
84 return;
85 } else {
86 // A window is entering our hierarchy, so we need to consider
87 // focusing it.
88 FocusWindow(params.target);
89 return;
90 }
91 }
92
93 // The window is leaving our hierarchy, so stop tracking it.
94 // It could contain multiple windows that were focused, so lets stop tracking
95 // them all.
96 auto iter = focusable_windows_.begin();
97 bool was_focused = false;
98 while (iter != focusable_windows_.end()) {
99 aura::Window* window = *iter;
100 if (params.target == window || params.target->Contains(window)) {
101 window->RemoveObserver(this);
102 was_focused |= window == focused_window_;
103 iter = focusable_windows_.erase(iter);
104
105 aura::Window* top_level = GetTopLevelWindow(window);
106 DCHECK(top_level);
107 LOG(INFO) << "Dropping window, " << LOG_WINDOW_INFO(top_level, window);
108 } else {
109 ++iter;
110 }
111 }
112
113 if (was_focused) {
114 // The window that was removed from our hierarchy was the focused window, so
115 // de-focus it.
116 UpdateWindowFocus();
117 }
118 }
119
120 // An explicit request to focus a window.
121 // We lock focus to the top-most high-level window, and so will ignore this
122 // focus request if it isn't for the topmost window. If it is for a lower
123 // window, then we'll track it to focus it later when it rises to the top.
124 void CastFocusClientAura::FocusWindow(aura::Window* window) {
125 if (window) {
126 if (!window->CanFocus()) {
127 return;
128 }
129 aura::Window* top_level = GetTopLevelWindow(window);
130 DCHECK(top_level);
131 LOG(INFO) << "Requesting focus for " << LOG_WINDOW_INFO(top_level, window);
132 auto iter =
133 std::find(focusable_windows_.begin(), focusable_windows_.end(), window);
134 if (iter == focusable_windows_.end()) {
135 // We're not yet tracking this focusable window, so start tracking it as a
136 // potential focus target.
137 window->AddObserver(this);
138 focusable_windows_.push_back(window);
139 }
140 }
141
142 // Check whether this new window is the most appropriate to focus.
143 UpdateWindowFocus();
144 }
145
146 // Finds the top-most window, and if it doesn't have focus, then gives it focus.
147 void CastFocusClientAura::UpdateWindowFocus() {
148 aura::Window* window = GetWindowToFocus();
149 if (window == focused_window_) {
150 return;
151 }
152
153 if (window) {
154 aura::Window* top_level = GetTopLevelWindow(window);
155 DCHECK(top_level);
156 LOG(INFO) << "Switching focus to " << LOG_WINDOW_INFO(top_level, window);
157 }
158
159 aura::Window* unfocus_window = focused_window_;
160 focused_window_ = window;
161
162 for (aura::client::FocusChangeObserver& observer : focus_observers_) {
163 observer.OnWindowFocused(focused_window_, unfocus_window);
164 if (focused_window_ != window) {
165 // The observer changed focused_window_.
166 return;
167 }
168 }
169
170 if (unfocus_window) {
171 aura::client::FocusChangeObserver* focus_observer =
172 aura::client::GetFocusChangeObserver(unfocus_window);
173 if (focus_observer) {
174 focus_observer->OnWindowFocused(focused_window_, unfocus_window);
175 if (focused_window_ != window) {
176 // The observer changed focused_window_.
177 return;
178 }
179 }
180 }
181 if (focused_window_) {
182 aura::client::FocusChangeObserver* focus_observer =
183 aura::client::GetFocusChangeObserver(focused_window_);
184 if (focus_observer) {
185 focus_observer->OnWindowFocused(focused_window_, unfocus_window);
186 if (focused_window_ != window) {
187 // The observer changed focused_window_.
188 return;
189 }
190 }
191 }
192 }
193
194 // Returns the most appropriate window to have focus.
195 // A focusable window could be anywhere within its window hierarchy, and we
196 // choose based on the z-order of the top-level window in its hierarchy.
197 aura::Window* CastFocusClientAura::GetWindowToFocus() {
198 aura::Window* next = nullptr;
199 aura::Window* next_top_level = nullptr;
200 for (auto iter = focusable_windows_.begin(); iter != focusable_windows_.end();
201 ++iter) {
halliwell 2017/01/24 00:54:46 range-based?
Joshua LeVasseur 2017/01/24 03:39:50 Done.
202 aura::Window* window = *iter;
203 if (!window->CanFocus() || !window->IsVisible()) {
204 continue;
205 }
206
207 // Compare z-order of top-level windows using the window IDs.
208 aura::Window* top_level = GetTopLevelWindow(window);
209 DCHECK(top_level);
210 if (!next || top_level->id() >= next_top_level->id()) {
211 next = window;
212 next_top_level = top_level;
213 }
214 }
215 return next;
216 }
217
218 aura::Window* CastFocusClientAura::GetTopLevelWindow(aura::Window* window) {
219 while (window->parent() && !window->parent()->GetHost()) {
220 window = window->parent();
221 }
222 return window;
223 }
224
225 void CastFocusClientAura::ResetFocusWithinActiveWindow(aura::Window* window) {
226 // Sets focus to |window| if it's within the active window (a child of the
227 // focused window).
228 if (focused_window_ && focused_window_->Contains(window)) {
229 FocusWindow(window);
230 }
231 }
232
233 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698