OLD | NEW |
---|---|
(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 | |
OLD | NEW |