OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/wm/window_cycle_controller.h" | 5 #include "ash/wm/mru_window_tracker.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "ash/session_state_delegate.h" | 9 #include "ash/session_state_delegate.h" |
10 #include "ash/shell.h" | 10 #include "ash/shell.h" |
11 #include "ash/shell_window_ids.h" | 11 #include "ash/shell_window_ids.h" |
12 #include "ash/wm/activation_controller.h" | 12 #include "ash/wm/activation_controller.h" |
13 #include "ash/wm/window_cycle_list.h" | 13 #include "ash/wm/window_cycle_list.h" |
14 #include "ash/wm/window_util.h" | 14 #include "ash/wm/window_util.h" |
15 #include "ash/wm/workspace_controller.h" | 15 #include "ash/wm/workspace_controller.h" |
16 #include "ui/aura/root_window.h" | 16 #include "ui/aura/root_window.h" |
17 #include "ui/base/events/event.h" | 17 #include "ui/base/events/event.h" |
18 #include "ui/base/events/event_handler.h" | 18 #include "ui/base/events/event_handler.h" |
19 | 19 |
20 namespace ash { | 20 namespace ash { |
21 | 21 |
22 namespace { | 22 namespace { |
23 | 23 |
24 // List of containers whose children we will cycle through. | 24 // List of containers whose children we will allow switching to. |
25 const int kContainerIds[] = { | 25 const int kContainerIds[] = { |
26 internal::kShellWindowId_DefaultContainer, | 26 internal::kShellWindowId_DefaultContainer, |
27 internal::kShellWindowId_AlwaysOnTopContainer | 27 internal::kShellWindowId_AlwaysOnTopContainer |
28 }; | 28 }; |
29 | 29 |
30 // Filter to watch for the termination of a keyboard gesture to cycle through | |
31 // multiple windows. | |
32 class WindowCycleEventFilter : public ui::EventHandler { | |
33 public: | |
34 WindowCycleEventFilter(); | |
35 virtual ~WindowCycleEventFilter(); | |
36 | |
37 // Overridden from ui::EventHandler: | |
38 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; | |
39 private: | |
40 DISALLOW_COPY_AND_ASSIGN(WindowCycleEventFilter); | |
41 }; | |
42 | |
43 // Watch for all keyboard events by filtering the root window. | |
44 WindowCycleEventFilter::WindowCycleEventFilter() { | |
45 } | |
46 | |
47 WindowCycleEventFilter::~WindowCycleEventFilter() { | |
48 } | |
49 | |
50 void WindowCycleEventFilter::OnKeyEvent(ui::KeyEvent* event) { | |
51 // Views uses VKEY_MENU for both left and right Alt keys. | |
52 if (event->key_code() == ui::VKEY_MENU && | |
53 event->type() == ui::ET_KEY_RELEASED) { | |
54 Shell::GetInstance()->window_cycle_controller()->AltKeyReleased(); | |
55 // Warning: |this| will be deleted from here on. | |
56 } | |
57 } | |
58 | |
59 // Adds all the children of |window| to |windows|. | 30 // Adds all the children of |window| to |windows|. |
60 void AddAllChildren(aura::Window* window, | 31 void AddAllChildren(aura::Window* window, |
61 WindowCycleList::WindowList* windows) { | 32 MruWindowTracker::WindowList* windows) { |
62 const WindowCycleList::WindowList& children(window->children()); | 33 const MruWindowTracker::WindowList& children(window->children()); |
63 windows->insert(windows->end(), children.begin(), children.end()); | 34 windows->insert(windows->end(), children.begin(), children.end()); |
64 } | 35 } |
65 | 36 |
66 // Adds all the children of all of |window|s children to |windows|. | 37 // Adds all the children of all of |window|s children to |windows|. |
67 void AddWorkspaceChildren(aura::Window* window, | 38 void AddWorkspaceChildren(aura::Window* window, |
68 WindowCycleList::WindowList* windows) { | 39 MruWindowTracker::WindowList* windows) { |
69 for (size_t i = 0; i < window->children().size(); ++i) | 40 for (size_t i = 0; i < window->children().size(); ++i) |
70 AddAllChildren(window->children()[i], windows); | 41 AddAllChildren(window->children()[i], windows); |
71 } | 42 } |
72 | 43 |
73 // Adds the windows that can be cycled through for the specified window id to | 44 // Adds the windows that can be cycled through for the specified window id to |
74 // |windows|. | 45 // |windows|. |
75 void AddCycleWindows(aura::RootWindow* root, | 46 void AddTrackedWindows(aura::RootWindow* root, |
76 int container_id, | 47 int container_id, |
77 WindowCycleList::WindowList* windows) { | 48 MruWindowTracker::WindowList* windows) { |
78 aura::Window* container = Shell::GetContainer(root, container_id); | 49 aura::Window* container = Shell::GetContainer(root, container_id); |
79 if (container_id == internal::kShellWindowId_DefaultContainer) | 50 if (container_id == internal::kShellWindowId_DefaultContainer) |
80 AddWorkspaceChildren(container, windows); | 51 AddWorkspaceChildren(container, windows); |
81 else | 52 else |
82 AddAllChildren(container, windows); | 53 AddAllChildren(container, windows); |
83 } | 54 } |
84 | 55 |
85 } // namespace | 56 // Returns a list of windows ordered by their stacking order. |
86 | 57 // If |mru_windows| is passed, these windows are moved to the front of the list. |
87 ////////////////////////////////////////////////////////////////////////////// | 58 // If |top_most_at_end|, the list is returned in descending (bottom-most / least |
88 // WindowCycleController, public: | 59 // recently used) order. |
89 | 60 MruWindowTracker::WindowList BuildWindowListInternal( |
90 WindowCycleController::WindowCycleController( | |
91 aura::client::ActivationClient* activation_client) | |
92 : activation_client_(activation_client) { | |
93 activation_client_->AddObserver(this); | |
94 } | |
95 | |
96 WindowCycleController::~WindowCycleController() { | |
97 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); | |
98 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); | |
99 iter != root_windows.end(); ++iter) { | |
100 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { | |
101 aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]); | |
102 if (container) | |
103 container->RemoveObserver(this); | |
104 } | |
105 aura::Window* default_container = | |
106 Shell::GetContainer(*iter, internal::kShellWindowId_DefaultContainer); | |
107 if (default_container) { | |
108 for (size_t i = 0; i < default_container->children().size(); ++i) { | |
109 aura::Window* workspace_window = default_container->children()[i]; | |
110 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, | |
111 workspace_window->id()); | |
112 workspace_window->RemoveObserver(this); | |
113 } | |
114 } | |
115 } | |
116 | |
117 activation_client_->RemoveObserver(this); | |
118 StopCycling(); | |
119 } | |
120 | |
121 // static | |
122 bool WindowCycleController::CanCycle() { | |
123 // Don't allow window cycling if the screen is locked or a modal dialog is | |
124 // open. | |
125 return !Shell::GetInstance()->session_state_delegate()->IsScreenLocked() && | |
126 !Shell::GetInstance()->IsSystemModalWindowOpen(); | |
127 } | |
128 | |
129 void WindowCycleController::HandleCycleWindow(Direction direction, | |
130 bool is_alt_down) { | |
131 if (!CanCycle()) | |
132 return; | |
133 | |
134 if (is_alt_down) { | |
135 if (!IsCycling()) { | |
136 // This is the start of an alt-tab cycle through multiple windows, so | |
137 // listen for the alt key being released to stop cycling. | |
138 StartCycling(); | |
139 Step(direction); | |
140 InstallEventFilter(); | |
141 } else { | |
142 // We're in the middle of an alt-tab cycle, just step forward. | |
143 Step(direction); | |
144 } | |
145 } else { | |
146 // This is a simple, single-step window cycle. | |
147 StartCycling(); | |
148 Step(direction); | |
149 StopCycling(); | |
150 } | |
151 } | |
152 | |
153 void WindowCycleController::HandleLinearCycleWindow() { | |
154 if (!CanCycle() || IsCycling()) | |
155 return; | |
156 | |
157 // Use the reversed list of windows to prevent a 2-cycle of the most recent | |
158 // windows occurring. | |
159 WindowCycleList cycle_list(BuildWindowList(NULL,true)); | |
160 cycle_list.Step(WindowCycleList::FORWARD); | |
161 } | |
162 | |
163 void WindowCycleController::AltKeyReleased() { | |
164 StopCycling(); | |
165 } | |
166 | |
167 // static | |
168 std::vector<aura::Window*> WindowCycleController::BuildWindowList( | |
169 const std::list<aura::Window*>* mru_windows, | 61 const std::list<aura::Window*>* mru_windows, |
170 bool top_most_at_end) { | 62 bool top_most_at_end) { |
171 WindowCycleList::WindowList windows; | 63 MruWindowTracker::WindowList windows; |
172 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); | 64 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); |
173 | 65 |
174 aura::RootWindow* active_root = Shell::GetActiveRootWindow(); | 66 aura::RootWindow* active_root = Shell::GetActiveRootWindow(); |
175 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); | 67 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); |
176 iter != root_windows.end(); ++iter) { | 68 iter != root_windows.end(); ++iter) { |
177 if (*iter == active_root) | 69 if (*iter == active_root) |
178 continue; | 70 continue; |
179 for (size_t i = 0; i < arraysize(kContainerIds); ++i) | 71 for (size_t i = 0; i < arraysize(kContainerIds); ++i) |
180 AddCycleWindows(*iter, kContainerIds[i], &windows); | 72 AddTrackedWindows(*iter, kContainerIds[i], &windows); |
181 } | 73 } |
182 | 74 |
183 // Add windows in the active root windows last so that the topmost window | 75 // Add windows in the active root windows last so that the topmost window |
184 // in the active root window becomes the front of the list. | 76 // in the active root window becomes the front of the list. |
185 for (size_t i = 0; i < arraysize(kContainerIds); ++i) | 77 for (size_t i = 0; i < arraysize(kContainerIds); ++i) |
186 AddCycleWindows(active_root, kContainerIds[i], &windows); | 78 AddTrackedWindows(active_root, kContainerIds[i], &windows); |
187 | 79 |
188 // Removes unfocusable windows. | 80 // Removes unfocusable windows. |
189 WindowCycleList::WindowList::iterator last = | 81 MruWindowTracker::WindowList::iterator last = |
190 std::remove_if( | 82 std::remove_if( |
191 windows.begin(), | 83 windows.begin(), |
192 windows.end(), | 84 windows.end(), |
193 std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); | 85 std::not1(std::ptr_fun(ash::wm::CanActivateWindow))); |
194 windows.erase(last, windows.end()); | 86 windows.erase(last, windows.end()); |
195 | 87 |
196 // Put the windows in the mru_windows list at the head, if it's available. | 88 // Put the windows in the mru_windows list at the head, if it's available. |
197 if (mru_windows) { | 89 if (mru_windows) { |
198 // Iterate through the list backwards, so that we can move each window to | 90 // Iterate through the list backwards, so that we can move each window to |
199 // the front of the windows list as we find them. | 91 // the front of the windows list as we find them. |
200 for (std::list<aura::Window*>::const_reverse_iterator ix = | 92 for (std::list<aura::Window*>::const_reverse_iterator ix = |
201 mru_windows->rbegin(); | 93 mru_windows->rbegin(); |
202 ix != mru_windows->rend(); ++ix) { | 94 ix != mru_windows->rend(); ++ix) { |
203 WindowCycleList::WindowList::iterator window = | 95 MruWindowTracker::WindowList::iterator window = |
204 std::find(windows.begin(), windows.end(), *ix); | 96 std::find(windows.begin(), windows.end(), *ix); |
205 if (window != windows.end()) { | 97 if (window != windows.end()) { |
206 windows.erase(window); | 98 windows.erase(window); |
207 windows.push_back(*ix); | 99 windows.push_back(*ix); |
208 } | 100 } |
209 } | 101 } |
210 } | 102 } |
211 | 103 |
212 // Window cycling expects the topmost window at the front of the list. | 104 // Window cycling expects the topmost window at the front of the list. |
213 if (!top_most_at_end) | 105 if (!top_most_at_end) |
214 std::reverse(windows.begin(), windows.end()); | 106 std::reverse(windows.begin(), windows.end()); |
215 | 107 |
216 return windows; | 108 return windows; |
217 } | 109 } |
218 | 110 |
219 void WindowCycleController::OnRootWindowAdded(aura::RootWindow* root_window) { | 111 } // namespace |
| 112 |
| 113 ////////////////////////////////////////////////////////////////////////////// |
| 114 // MruWindowTracker, public: |
| 115 |
| 116 MruWindowTracker::MruWindowTracker( |
| 117 aura::client::ActivationClient* activation_client) |
| 118 : activation_client_(activation_client), |
| 119 ignore_window_activations_(false) { |
| 120 activation_client_->AddObserver(this); |
| 121 } |
| 122 |
| 123 MruWindowTracker::~MruWindowTracker() { |
| 124 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); |
| 125 for (Shell::RootWindowList::const_iterator iter = root_windows.begin(); |
| 126 iter != root_windows.end(); ++iter) { |
| 127 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { |
| 128 aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]); |
| 129 if (container) |
| 130 container->RemoveObserver(this); |
| 131 } |
| 132 aura::Window* default_container = |
| 133 Shell::GetContainer(*iter, internal::kShellWindowId_DefaultContainer); |
| 134 if (default_container) { |
| 135 for (size_t i = 0; i < default_container->children().size(); ++i) { |
| 136 aura::Window* workspace_window = default_container->children()[i]; |
| 137 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, |
| 138 workspace_window->id()); |
| 139 workspace_window->RemoveObserver(this); |
| 140 } |
| 141 } |
| 142 } |
| 143 |
| 144 activation_client_->RemoveObserver(this); |
| 145 } |
| 146 |
| 147 // static |
| 148 MruWindowTracker::WindowList MruWindowTracker::BuildWindowList( |
| 149 bool top_most_at_end) { |
| 150 return BuildWindowListInternal(NULL, top_most_at_end); |
| 151 } |
| 152 |
| 153 MruWindowTracker::WindowList MruWindowTracker::BuildMruWindowList() { |
| 154 return BuildWindowListInternal(&mru_windows_, false); |
| 155 } |
| 156 |
| 157 void MruWindowTracker::OnRootWindowAdded(aura::RootWindow* root_window) { |
220 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { | 158 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { |
221 aura::Window* container = | 159 aura::Window* container = |
222 Shell::GetContainer(root_window, kContainerIds[i]); | 160 Shell::GetContainer(root_window, kContainerIds[i]); |
223 container->AddObserver(this); | 161 container->AddObserver(this); |
224 } | 162 } |
225 | 163 |
226 aura::Window* default_container = | 164 aura::Window* default_container = |
227 Shell::GetContainer(root_window, | 165 Shell::GetContainer(root_window, |
228 internal::kShellWindowId_DefaultContainer); | 166 internal::kShellWindowId_DefaultContainer); |
229 for (size_t i = 0; i < default_container->children().size(); ++i) { | 167 for (size_t i = 0; i < default_container->children().size(); ++i) { |
230 aura::Window* workspace_window = default_container->children()[i]; | 168 aura::Window* workspace_window = default_container->children()[i]; |
231 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, | 169 DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer, |
232 workspace_window->id()); | 170 workspace_window->id()); |
233 workspace_window->AddObserver(this); | 171 workspace_window->AddObserver(this); |
234 } | 172 } |
235 } | 173 } |
236 | 174 |
| 175 void MruWindowTracker::SetIgnoreActivations(bool ignore) { |
| 176 ignore_window_activations_ = ignore; |
| 177 |
| 178 // If no longer ignoring window activations, move currently active window |
| 179 // to front. |
| 180 if (!ignore) { |
| 181 aura::Window* active_window = wm::GetActiveWindow(); |
| 182 mru_windows_.remove(active_window); |
| 183 mru_windows_.push_front(active_window); |
| 184 } |
| 185 } |
| 186 |
237 ////////////////////////////////////////////////////////////////////////////// | 187 ////////////////////////////////////////////////////////////////////////////// |
238 // WindowCycleController, private: | 188 // MruWindowTracker, private: |
239 | |
240 void WindowCycleController::StartCycling() { | |
241 windows_.reset(new WindowCycleList(BuildWindowList(&mru_windows_, false))); | |
242 } | |
243 | |
244 void WindowCycleController::Step(Direction direction) { | |
245 DCHECK(windows_.get()); | |
246 windows_->Step(direction == FORWARD ? WindowCycleList::FORWARD : | |
247 WindowCycleList::BACKWARD); | |
248 } | |
249 | |
250 void WindowCycleController::StopCycling() { | |
251 windows_.reset(); | |
252 // Remove our key event filter. | |
253 if (event_handler_) { | |
254 Shell::GetInstance()->RemovePreTargetHandler(event_handler_.get()); | |
255 event_handler_.reset(); | |
256 } | |
257 | |
258 // Add the currently focused window to the MRU list | |
259 aura::Window* active_window = wm::GetActiveWindow(); | |
260 mru_windows_.remove(active_window); | |
261 mru_windows_.push_front(active_window); | |
262 } | |
263 | 189 |
264 // static | 190 // static |
265 bool WindowCycleController::IsTrackedContainer(aura::Window* window) { | 191 bool MruWindowTracker::IsTrackedContainer(aura::Window* window) { |
266 if (!window) | 192 if (!window) |
267 return false; | 193 return false; |
268 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { | 194 for (size_t i = 0; i < arraysize(kContainerIds); ++i) { |
269 if (window->id() == kContainerIds[i]) { | 195 if (window->id() == kContainerIds[i]) { |
270 return true; | 196 return true; |
271 } | 197 } |
272 } | 198 } |
273 return window->id() == internal::kShellWindowId_WorkspaceContainer; | 199 return window->id() == internal::kShellWindowId_WorkspaceContainer; |
274 } | 200 } |
275 | 201 |
276 void WindowCycleController::InstallEventFilter() { | 202 void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, |
277 event_handler_.reset(new WindowCycleEventFilter()); | 203 aura::Window* lost_active) { |
278 Shell::GetInstance()->AddPreTargetHandler(event_handler_.get()); | 204 if (gained_active && !ignore_window_activations_ && |
279 } | |
280 | |
281 void WindowCycleController::OnWindowActivated(aura::Window* gained_active, | |
282 aura::Window* lost_active) { | |
283 if (gained_active && !IsCycling() && | |
284 IsTrackedContainer(gained_active->parent())) { | 205 IsTrackedContainer(gained_active->parent())) { |
285 mru_windows_.remove(gained_active); | 206 mru_windows_.remove(gained_active); |
286 mru_windows_.push_front(gained_active); | 207 mru_windows_.push_front(gained_active); |
287 } | 208 } |
288 } | 209 } |
289 | 210 |
290 void WindowCycleController::OnWindowAdded(aura::Window* window) { | 211 void MruWindowTracker::OnWindowAdded(aura::Window* window) { |
291 if (window->id() == internal::kShellWindowId_WorkspaceContainer) | 212 if (window->id() == internal::kShellWindowId_WorkspaceContainer) |
292 window->AddObserver(this); | 213 window->AddObserver(this); |
293 } | 214 } |
294 | 215 |
295 void WindowCycleController::OnWillRemoveWindow(aura::Window* window) { | 216 void MruWindowTracker::OnWillRemoveWindow(aura::Window* window) { |
296 mru_windows_.remove(window); | 217 mru_windows_.remove(window); |
297 if (window->id() == internal::kShellWindowId_WorkspaceContainer) | 218 if (window->id() == internal::kShellWindowId_WorkspaceContainer) |
298 window->RemoveObserver(this); | 219 window->RemoveObserver(this); |
299 } | 220 } |
300 | 221 |
301 void WindowCycleController::OnWindowDestroying(aura::Window* window) { | 222 void MruWindowTracker::OnWindowDestroying(aura::Window* window) { |
302 window->RemoveObserver(this); | 223 window->RemoveObserver(this); |
303 } | 224 } |
304 | 225 |
305 } // namespace ash | 226 } // namespace ash |
OLD | NEW |