| 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 |