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

Side by Side Diff: components/mus/ws/focus_controller.cc

Issue 2119963002: Move mus to //services/ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 5 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
« no previous file with comments | « components/mus/ws/focus_controller.h ('k') | components/mus/ws/focus_controller_delegate.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « components/mus/ws/focus_controller.h ('k') | components/mus/ws/focus_controller_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698