OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "components/mus/ws/focus_controller.h" | |
6 | |
7 #include "base/macros.h" | |
8 #include "components/mus/public/interfaces/window_manager.mojom.h" | |
9 #include "components/mus/ws/focus_controller_delegate.h" | |
10 #include "components/mus/ws/focus_controller_observer.h" | |
11 #include "components/mus/ws/server_window.h" | |
12 #include "components/mus/ws/server_window_drawn_tracker.h" | |
13 | |
14 namespace mus { | |
15 namespace ws { | |
16 | |
17 namespace { | |
18 | |
19 ServerWindow* GetDeepestLastDescendant(ServerWindow* window) { | |
20 while (!window->children().empty()) | |
21 window = window->children().back(); | |
22 return window; | |
23 } | |
24 | |
25 // This can be used to iterate over each node in a rooted tree for the purpose | |
26 // of shifting focus/activation. | |
27 class WindowTreeIterator { | |
28 public: | |
29 explicit WindowTreeIterator(ServerWindow* root) : root_(root) {} | |
30 ~WindowTreeIterator() {} | |
31 | |
32 ServerWindow* GetNext(ServerWindow* window) const { | |
33 if (window == root_ || window == nullptr) | |
34 return GetDeepestLastDescendant(root_); | |
35 | |
36 // Return the next sibling. | |
37 ServerWindow* parent = window->parent(); | |
38 if (parent) { | |
39 const ServerWindow::Windows& siblings = parent->children(); | |
40 ServerWindow::Windows::const_reverse_iterator iter = | |
41 std::find(siblings.rbegin(), siblings.rend(), window); | |
42 DCHECK(iter != siblings.rend()); | |
43 ++iter; | |
44 if (iter != siblings.rend()) | |
45 return GetDeepestLastDescendant(*iter); | |
46 } | |
47 | |
48 // All children and siblings have been explored. Next is the parent. | |
49 return parent; | |
50 } | |
51 | |
52 private: | |
53 ServerWindow* root_; | |
54 | |
55 DISALLOW_COPY_AND_ASSIGN(WindowTreeIterator); | |
56 }; | |
57 | |
58 } // namespace | |
59 | |
60 FocusController::FocusController(FocusControllerDelegate* delegate, | |
61 ServerWindow* root) | |
62 : delegate_(delegate), | |
63 root_(root), | |
64 focused_window_(nullptr), | |
65 active_window_(nullptr), | |
66 activation_reason_(ActivationChangeReason::UNKNONW) { | |
67 DCHECK(delegate_); | |
68 DCHECK(root_); | |
69 } | |
70 | |
71 FocusController::~FocusController() { | |
72 } | |
73 | |
74 bool FocusController::SetFocusedWindow(ServerWindow* window) { | |
75 if (GetFocusedWindow() == window) | |
76 return true; | |
77 | |
78 return SetFocusedWindowImpl(FocusControllerChangeSource::EXPLICIT, window); | |
79 } | |
80 | |
81 ServerWindow* FocusController::GetFocusedWindow() { | |
82 return focused_window_; | |
83 } | |
84 | |
85 void FocusController::ActivateNextWindow() { | |
86 WindowTreeIterator iter(root_); | |
87 ServerWindow* activate = active_window_; | |
88 while (true) { | |
89 activate = iter.GetNext(activate); | |
90 if (activation_reason_ == ActivationChangeReason::CYCLE) { | |
91 if (activate == active_window_) { | |
92 // We have cycled over all the activatable windows. Remove the oldest | |
93 // window that was cycled. | |
94 if (!cycle_windows_->windows().empty()) { | |
95 cycle_windows_->Remove(cycle_windows_->windows().front()); | |
96 continue; | |
97 } | |
98 } else if (cycle_windows_->Contains(activate)) { | |
99 // We are cycling between activated windows, and this window has already | |
100 // been through the cycle. So skip over it. | |
101 continue; | |
102 } | |
103 } | |
104 if (activate == active_window_ || CanBeActivated(activate)) | |
105 break; | |
106 } | |
107 SetActiveWindow(activate, ActivationChangeReason::CYCLE); | |
108 | |
109 if (active_window_) { | |
110 // Do not shift focus if the focused window already lives in the active | |
111 // window. | |
112 if (focused_window_ && active_window_->Contains(focused_window_)) | |
113 return; | |
114 // Focus the first focusable window in the tree. | |
115 WindowTreeIterator iter(active_window_); | |
116 ServerWindow* focus = nullptr; | |
117 do { | |
118 focus = iter.GetNext(focus); | |
119 } while (focus != active_window_ && !CanBeFocused(focus)); | |
120 SetFocusedWindow(focus); | |
121 } else { | |
122 SetFocusedWindow(nullptr); | |
123 } | |
124 } | |
125 | |
126 void FocusController::AddObserver(FocusControllerObserver* observer) { | |
127 observers_.AddObserver(observer); | |
128 } | |
129 | |
130 void FocusController::RemoveObserver(FocusControllerObserver* observer) { | |
131 observers_.RemoveObserver(observer); | |
132 } | |
133 | |
134 void FocusController::SetActiveWindow(ServerWindow* window, | |
135 ActivationChangeReason reason) { | |
136 DCHECK(!window || CanBeActivated(window)); | |
137 if (active_window_ == window) | |
138 return; | |
139 if (reason != ActivationChangeReason::CYCLE) { | |
140 cycle_windows_.reset(); | |
141 } else if (activation_reason_ != ActivationChangeReason::CYCLE) { | |
142 DCHECK(!cycle_windows_); | |
143 cycle_windows_.reset(new ServerWindowTracker()); | |
144 if (active_window_) | |
145 cycle_windows_->Add(active_window_); | |
146 } | |
147 | |
148 ServerWindow* old_active = active_window_; | |
149 active_window_ = window; | |
150 activation_reason_ = reason; | |
151 FOR_EACH_OBSERVER(FocusControllerObserver, observers_, | |
152 OnActivationChanged(old_active, active_window_)); | |
153 if (active_window_ && activation_reason_ == ActivationChangeReason::CYCLE) | |
154 cycle_windows_->Add(active_window_); | |
155 } | |
156 | |
157 bool FocusController::CanBeFocused(ServerWindow* window) const { | |
158 // All ancestors of |window| must be drawn, and be focusable. | |
159 for (ServerWindow* w = window; w; w = w->parent()) { | |
160 if (!w->IsDrawn()) | |
161 return false; | |
162 if (!w->can_focus()) | |
163 return false; | |
164 } | |
165 | |
166 // |window| must be a descendent of an activatable window. | |
167 return GetActivatableAncestorOf(window) != nullptr; | |
168 } | |
169 | |
170 bool FocusController::CanBeActivated(ServerWindow* window) const { | |
171 DCHECK(window); | |
172 // A detached window cannot be activated. | |
173 if (!root_->Contains(window)) | |
174 return false; | |
175 | |
176 // The parent window must be allowed to have active children. | |
177 if (!delegate_->CanHaveActiveChildren(window->parent())) | |
178 return false; | |
179 | |
180 if (!window->can_focus()) | |
181 return false; | |
182 | |
183 // The window must be drawn, or if it's not drawn, it must be minimized. | |
184 if (!window->IsDrawn()) { | |
185 bool is_minimized = false; | |
186 const ServerWindow::Properties& props = window->properties(); | |
187 if (props.count(mojom::WindowManager::kShowState_Property)) { | |
188 is_minimized = | |
189 props.find(mojom::WindowManager::kShowState_Property)->second[0] == | |
190 static_cast<int>(mus::mojom::ShowState::MINIMIZED); | |
191 } | |
192 if (!is_minimized) | |
193 return false; | |
194 } | |
195 | |
196 // TODO(sad): If there's a transient modal window, then this cannot be | |
197 // activated. | |
198 return true; | |
199 } | |
200 | |
201 ServerWindow* FocusController::GetActivatableAncestorOf( | |
202 ServerWindow* window) const { | |
203 for (ServerWindow* w = window; w; w = w->parent()) { | |
204 if (CanBeActivated(w)) | |
205 return w; | |
206 } | |
207 return nullptr; | |
208 } | |
209 | |
210 bool FocusController::SetFocusedWindowImpl( | |
211 FocusControllerChangeSource change_source, | |
212 ServerWindow* window) { | |
213 if (window && !CanBeFocused(window)) | |
214 return false; | |
215 | |
216 ServerWindow* old_focused = GetFocusedWindow(); | |
217 | |
218 DCHECK(!window || window->IsDrawn()); | |
219 | |
220 // Activate the closest activatable ancestor window. | |
221 // TODO(sad): The window to activate doesn't necessarily have to be a direct | |
222 // ancestor (e.g. could be a transient parent). | |
223 SetActiveWindow(GetActivatableAncestorOf(window), | |
224 ActivationChangeReason::FOCUS); | |
225 | |
226 FOR_EACH_OBSERVER(FocusControllerObserver, observers_, | |
227 OnFocusChanged(change_source, old_focused, window)); | |
228 | |
229 focused_window_ = window; | |
230 // We can currently use only a single ServerWindowDrawnTracker since focused | |
231 // window is expected to be a direct descendant of the active window. | |
232 if (focused_window_ && active_window_) { | |
233 DCHECK(active_window_->Contains(focused_window_)); | |
234 } | |
235 ServerWindow* track_window = focused_window_; | |
236 if (!track_window) | |
237 track_window = active_window_; | |
238 if (track_window) | |
239 drawn_tracker_.reset(new ServerWindowDrawnTracker(track_window, this)); | |
240 else | |
241 drawn_tracker_.reset(); | |
242 return true; | |
243 } | |
244 | |
245 void FocusController::OnDrawnStateWillChange(ServerWindow* ancestor, | |
246 ServerWindow* window, | |
247 bool is_drawn) { | |
248 DCHECK(!is_drawn); | |
249 DCHECK_NE(ancestor, window); | |
250 DCHECK(root_->Contains(window)); | |
251 drawn_tracker_.reset(); | |
252 | |
253 // Find the window that triggered this state-change notification. | |
254 ServerWindow* trigger = window; | |
255 while (trigger->parent() != ancestor) | |
256 trigger = trigger->parent(); | |
257 DCHECK(trigger); | |
258 auto will_be_hidden = [trigger](ServerWindow* w) { | |
259 return trigger->Contains(w); | |
260 }; | |
261 | |
262 // If |window| is |active_window_|, then activate the next activatable window | |
263 // that does not belong to the subtree which is getting hidden. | |
264 if (window == active_window_) { | |
265 WindowTreeIterator iter(root_); | |
266 ServerWindow* activate = active_window_; | |
267 do { | |
268 activate = iter.GetNext(activate); | |
269 } while (activate != active_window_ && | |
270 (will_be_hidden(activate) || !CanBeActivated(activate))); | |
271 if (activate == window) | |
272 activate = nullptr; | |
273 SetActiveWindow(activate, ActivationChangeReason::DRAWN_STATE_CHANGED); | |
274 | |
275 // Now make sure focus is in the active window. | |
276 ServerWindow* focus = nullptr; | |
277 if (active_window_) { | |
278 WindowTreeIterator iter(active_window_); | |
279 focus = nullptr; | |
280 do { | |
281 focus = iter.GetNext(focus); | |
282 } while (focus != active_window_ && | |
283 (will_be_hidden(focus) || !CanBeFocused(focus))); | |
284 DCHECK(focus && CanBeFocused(focus)); | |
285 } | |
286 SetFocusedWindowImpl(FocusControllerChangeSource::DRAWN_STATE_CHANGED, | |
287 focus); | |
288 return; | |
289 } | |
290 | |
291 // Move focus to the next focusable window in |active_window_|. | |
292 DCHECK_EQ(focused_window_, window); | |
293 WindowTreeIterator iter(active_window_); | |
294 ServerWindow* focus = focused_window_; | |
295 do { | |
296 focus = iter.GetNext(focus); | |
297 } while (focus != focused_window_ && | |
298 (will_be_hidden(focus) || !CanBeFocused(focus))); | |
299 if (focus == window) | |
300 focus = nullptr; | |
301 SetFocusedWindowImpl(FocusControllerChangeSource::DRAWN_STATE_CHANGED, focus); | |
302 } | |
303 | |
304 void FocusController::OnDrawnStateChanged(ServerWindow* ancestor, | |
305 ServerWindow* window, | |
306 bool is_drawn) { | |
307 // DCHECK(false); TODO(sadrul): | |
308 } | |
309 | |
310 } // namespace ws | |
311 } // namespace mus | |
OLD | NEW |