OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "ash/wm/workspace/workspace_manager.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ash/screen_ash.h" | |
10 #include "ash/shell.h" | |
11 #include "ash/wm/property_util.h" | |
12 #include "ash/wm/shelf_layout_manager.h" | |
13 #include "ash/wm/window_animations.h" | |
14 #include "ash/wm/window_util.h" | |
15 #include "ash/wm/workspace/managed_workspace.h" | |
16 #include "ash/wm/workspace/maximized_workspace.h" | |
17 #include "base/auto_reset.h" | |
18 #include "base/logging.h" | |
19 #include "base/stl_util.h" | |
20 #include "ui/aura/client/aura_constants.h" | |
21 #include "ui/aura/env.h" | |
22 #include "ui/aura/root_window.h" | |
23 #include "ui/aura/window.h" | |
24 #include "ui/base/ui_base_types.h" | |
25 #include "ui/compositor/layer.h" | |
26 #include "ui/compositor/layer_animator.h" | |
27 #include "ui/compositor/scoped_layer_animation_settings.h" | |
28 #include "ui/gfx/screen.h" | |
29 #include "ui/gfx/transform.h" | |
30 | |
31 namespace { | |
32 | |
33 // Returns a list of all the windows with layers in |result|. Optionally | |
34 // ignores the window |ignore_window|. | |
35 void BuildWindowList(const std::vector<aura::Window*>& windows, | |
36 aura::Window* ignore_window, | |
37 std::vector<aura::Window*>* result) { | |
38 for (size_t i = 0; i < windows.size(); ++i) { | |
39 if (windows[i] == ignore_window) | |
40 continue; | |
41 if (windows[i]->layer()) | |
42 result->push_back(windows[i]); | |
43 BuildWindowList(windows[i]->transient_children(), ignore_window, result); | |
44 } | |
45 } | |
46 | |
47 } | |
48 | |
49 namespace ash { | |
50 namespace internal { | |
51 | |
52 //////////////////////////////////////////////////////////////////////////////// | |
53 // WindowManager, public: | |
54 | |
55 WorkspaceManager::WorkspaceManager(aura::Window* contents_view) | |
56 : contents_view_(contents_view), | |
57 active_workspace_(NULL), | |
58 maximize_restore_window_(NULL), | |
59 grid_size_(0), | |
60 shelf_(NULL) { | |
61 DCHECK(contents_view); | |
62 } | |
63 | |
64 WorkspaceManager::~WorkspaceManager() { | |
65 std::vector<Workspace*> copy_to_delete(workspaces_); | |
66 STLDeleteElements(©_to_delete); | |
67 } | |
68 | |
69 // static | |
70 bool WorkspaceManager::ShouldManageWindow(aura::Window* window) { | |
71 return window->type() == aura::client::WINDOW_TYPE_NORMAL && | |
72 !window->transient_parent() && | |
73 ash::GetTrackedByWorkspace(window) && | |
74 (!ash::GetPersistsAcrossAllWorkspaces(window) || | |
75 wm::IsWindowMaximized(window)); | |
76 } | |
77 | |
78 bool WorkspaceManager::Contains(aura::Window* window) const { | |
79 return FindBy(window) != NULL; | |
80 } | |
81 | |
82 void WorkspaceManager::AddWindow(aura::Window* window) { | |
83 DCHECK(ShouldManageWindow(window)); | |
84 | |
85 Workspace* current_workspace = FindBy(window); | |
86 if (current_workspace) { | |
87 // Already know about this window. Make sure the workspace is active. | |
88 if (active_workspace_ != current_workspace) { | |
89 if (active_workspace_) | |
90 window->layer()->GetAnimator()->StopAnimating(); | |
91 current_workspace->Activate(); | |
92 } | |
93 window->Show(); | |
94 UpdateShelfVisibility(); | |
95 return; | |
96 } | |
97 | |
98 Workspace* workspace = NULL; | |
99 Workspace::Type type_for_window = Workspace::TypeForWindow(window); | |
100 switch (type_for_window) { | |
101 case Workspace::TYPE_MANAGED: | |
102 // All normal windows go in the same workspace. | |
103 workspace = GetManagedWorkspace(); | |
104 break; | |
105 | |
106 case Workspace::TYPE_MAXIMIZED: | |
107 // All maximized windows go in their own workspace. | |
108 break; | |
109 } | |
110 | |
111 if (!workspace) | |
112 workspace = CreateWorkspace(type_for_window); | |
113 workspace->AddWindowAfter(window, NULL); | |
114 workspace->Activate(); | |
115 UpdateShelfVisibility(); | |
116 } | |
117 | |
118 void WorkspaceManager::RemoveWindow(aura::Window* window) { | |
119 Workspace* workspace = FindBy(window); | |
120 if (!workspace) | |
121 return; | |
122 workspace->RemoveWindow(window); | |
123 CleanupWorkspace(workspace); | |
124 } | |
125 | |
126 void WorkspaceManager::UpdateShelfVisibility() { | |
127 if (shelf_) | |
128 shelf_->UpdateVisibilityState(); | |
129 } | |
130 | |
131 void WorkspaceManager::ShowStateChanged(aura::Window* window) { | |
132 Workspace* workspace = FindBy(window); | |
133 if (!workspace) | |
134 return; | |
135 if (!ShouldManageWindow(window)) { | |
136 RemoveWindow(window); | |
137 } else { | |
138 Workspace::Type old_type = workspace->type(); | |
139 Workspace::Type new_type = Workspace::TypeForWindow(window); | |
140 if (new_type != old_type) | |
141 OnTypeOfWorkspacedNeededChanged(window); | |
142 } | |
143 UpdateShelfVisibility(); | |
144 } | |
145 | |
146 bool WorkspaceManager::IsInMaximizedMode() const { | |
147 return active_workspace_ && | |
148 active_workspace_->type() == Workspace::TYPE_MAXIMIZED; | |
149 } | |
150 | |
151 WorkspaceWindowState WorkspaceManager::GetWindowState() const { | |
152 if (!shelf_ || !active_workspace_) | |
153 return WORKSPACE_WINDOW_STATE_DEFAULT; | |
154 | |
155 // TODO: this code needs to be made multi-display aware. | |
156 gfx::Rect shelf_bounds(shelf_->GetIdealBounds()); | |
157 const aura::Window::Windows& windows(contents_view_->children()); | |
158 bool window_overlaps_launcher = false; | |
159 bool has_maximized_window = false; | |
160 for (aura::Window::Windows::const_iterator i = windows.begin(); | |
161 i != windows.end(); ++i) { | |
162 gfx::Rect b = (*i)->bounds(); | |
163 if (GetIgnoredByShelf(*i)) | |
164 continue; | |
165 ui::Layer* layer = (*i)->layer(); | |
166 if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f) | |
167 continue; | |
168 if (wm::IsWindowMaximized(*i)) { | |
169 // An untracked window may still be fullscreen so we keep iterating when | |
170 // we hit a maximized window. | |
171 has_maximized_window = true; | |
172 } else if (wm::IsWindowFullscreen(*i)) { | |
173 return WORKSPACE_WINDOW_STATE_FULL_SCREEN; | |
174 } | |
175 if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds)) | |
176 window_overlaps_launcher = true; | |
177 } | |
178 if (has_maximized_window) | |
179 return WORKSPACE_WINDOW_STATE_MAXIMIZED; | |
180 | |
181 return window_overlaps_launcher ? | |
182 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF : | |
183 WORKSPACE_WINDOW_STATE_DEFAULT; | |
184 } | |
185 | |
186 void WorkspaceManager::SetShelf(ShelfLayoutManager* shelf) { | |
187 shelf_ = shelf; | |
188 } | |
189 | |
190 void WorkspaceManager::SetActiveWorkspaceByWindow(aura::Window* window) { | |
191 Workspace* workspace = FindBy(window); | |
192 if (workspace) | |
193 workspace->Activate(); | |
194 } | |
195 | |
196 aura::Window* WorkspaceManager::GetParentForNewWindow(aura::Window* window) { | |
197 return contents_view_; | |
198 } | |
199 | |
200 //////////////////////////////////////////////////////////////////////////////// | |
201 // WorkspaceManager, private: | |
202 | |
203 void WorkspaceManager::AddWorkspace(Workspace* workspace) { | |
204 DCHECK(std::find(workspaces_.begin(), workspaces_.end(), | |
205 workspace) == workspaces_.end()); | |
206 if (active_workspace_) { | |
207 // New workspaces go right after current workspace. | |
208 Workspaces::iterator i = std::find(workspaces_.begin(), workspaces_.end(), | |
209 active_workspace_); | |
210 workspaces_.insert(++i, workspace); | |
211 } else { | |
212 workspaces_.push_back(workspace); | |
213 } | |
214 } | |
215 | |
216 void WorkspaceManager::RemoveWorkspace(Workspace* workspace) { | |
217 Workspaces::iterator i = std::find(workspaces_.begin(), | |
218 workspaces_.end(), | |
219 workspace); | |
220 DCHECK(i != workspaces_.end()); | |
221 i = workspaces_.erase(i); | |
222 if (active_workspace_ == workspace) { | |
223 // TODO: need mru order. | |
224 if (i != workspaces_.end()) | |
225 SetActiveWorkspace(*i); | |
226 else if (!workspaces_.empty()) | |
227 SetActiveWorkspace(workspaces_.back()); | |
228 else | |
229 active_workspace_ = NULL; | |
230 } | |
231 } | |
232 | |
233 void WorkspaceManager::SetVisibilityOfWorkspaceWindows( | |
234 ash::internal::Workspace* workspace, | |
235 AnimateChangeType change_type, | |
236 bool value) { | |
237 std::vector<aura::Window*> children; | |
238 BuildWindowList(workspace->windows(), maximize_restore_window_, &children); | |
239 SetWindowLayerVisibility(children, change_type, value); | |
240 } | |
241 | |
242 void WorkspaceManager::SetWindowLayerVisibility( | |
243 const std::vector<aura::Window*>& windows, | |
244 AnimateChangeType change_type, | |
245 bool value) { | |
246 for (size_t i = 0; i < windows.size(); ++i) { | |
247 ui::Layer* layer = windows[i]->layer(); | |
248 // Only show the layer for windows that want to be visible. | |
249 if (layer && (!value || windows[i]->TargetVisibility())) { | |
250 bool animation_disabled = | |
251 windows[i]->GetProperty(aura::client::kAnimationsDisabledKey); | |
252 WindowVisibilityAnimationType animation_type = | |
253 GetWindowVisibilityAnimationType(windows[i]); | |
254 windows[i]->SetProperty(aura::client::kAnimationsDisabledKey, | |
255 change_type == DONT_ANIMATE); | |
256 bool update_layer = true; | |
257 if (change_type == ANIMATE) { | |
258 ash::SetWindowVisibilityAnimationType( | |
259 windows[i], | |
260 value ? ash::WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_SHOW : | |
261 ash::WINDOW_VISIBILITY_ANIMATION_TYPE_WORKSPACE_HIDE); | |
262 if (ash::internal::AnimateOnChildWindowVisibilityChanged( | |
263 windows[i], value)) | |
264 update_layer = false; | |
265 } | |
266 if (update_layer) | |
267 layer->SetVisible(value); | |
268 // Reset the animation type so it isn't used in a future hide/show. | |
269 ash::SetWindowVisibilityAnimationType( | |
270 windows[i], animation_type); | |
271 windows[i]->SetProperty(aura::client::kAnimationsDisabledKey, | |
272 animation_disabled); | |
273 } | |
274 } | |
275 } | |
276 | |
277 Workspace* WorkspaceManager::GetActiveWorkspace() const { | |
278 return active_workspace_; | |
279 } | |
280 | |
281 Workspace* WorkspaceManager::FindBy(aura::Window* window) const { | |
282 int index = GetWorkspaceIndexContaining(window); | |
283 return index < 0 ? NULL : workspaces_[index]; | |
284 } | |
285 | |
286 void WorkspaceManager::SetActiveWorkspace(Workspace* workspace) { | |
287 if (active_workspace_ == workspace) | |
288 return; | |
289 DCHECK(std::find(workspaces_.begin(), workspaces_.end(), | |
290 workspace) != workspaces_.end()); | |
291 if (active_workspace_) | |
292 SetVisibilityOfWorkspaceWindows(active_workspace_, ANIMATE, false); | |
293 Workspace* last_active = active_workspace_; | |
294 active_workspace_ = workspace; | |
295 if (active_workspace_) { | |
296 SetVisibilityOfWorkspaceWindows(active_workspace_, | |
297 last_active ? ANIMATE : DONT_ANIMATE, true); | |
298 UpdateShelfVisibility(); | |
299 } | |
300 } | |
301 | |
302 // Returns the index of the workspace that contains the |window|. | |
303 int WorkspaceManager::GetWorkspaceIndexContaining(aura::Window* window) const { | |
304 for (Workspaces::const_iterator i = workspaces_.begin(); | |
305 i != workspaces_.end(); | |
306 ++i) { | |
307 if ((*i)->Contains(window)) | |
308 return i - workspaces_.begin(); | |
309 } | |
310 return -1; | |
311 } | |
312 | |
313 void WorkspaceManager::SetWindowBounds(aura::Window* window, | |
314 const gfx::Rect& bounds) { | |
315 window->SetBounds(bounds); | |
316 } | |
317 | |
318 void WorkspaceManager::OnTypeOfWorkspacedNeededChanged(aura::Window* window) { | |
319 DCHECK(ShouldManageWindow(window)); | |
320 Workspace* current_workspace = FindBy(window); | |
321 DCHECK(current_workspace); | |
322 Workspace* new_workspace = NULL; | |
323 if (Workspace::TypeForWindow(window) == Workspace::TYPE_MAXIMIZED) { | |
324 // Unmaximized -> maximized; create a new workspace. | |
325 current_workspace->RemoveWindow(window); | |
326 new_workspace = CreateWorkspace(Workspace::TYPE_MAXIMIZED); | |
327 new_workspace->AddWindowAfter(window, NULL); | |
328 } else { | |
329 // Maximized -> unmaximized; move window to unmaximized workspace. | |
330 new_workspace = GetManagedWorkspace(); | |
331 current_workspace->RemoveWindow(window); | |
332 if (!new_workspace) | |
333 new_workspace = CreateWorkspace(Workspace::TYPE_MANAGED); | |
334 new_workspace->AddWindowAfter(window, NULL); | |
335 } | |
336 maximize_restore_window_ = window; | |
337 SetActiveWorkspace(new_workspace); | |
338 maximize_restore_window_ = NULL; | |
339 // Delete at the end so that we don't attempt to switch to another | |
340 // workspace in RemoveWorkspace(). | |
341 CleanupWorkspace(current_workspace); | |
342 } | |
343 | |
344 Workspace* WorkspaceManager::GetManagedWorkspace() { | |
345 for (size_t i = 0; i < workspaces_.size(); ++i) { | |
346 if (workspaces_[i]->type() == Workspace::TYPE_MANAGED) | |
347 return workspaces_[i]; | |
348 } | |
349 return NULL; | |
350 } | |
351 | |
352 Workspace* WorkspaceManager::CreateWorkspace(Workspace::Type type) { | |
353 Workspace* workspace = NULL; | |
354 if (type == Workspace::TYPE_MAXIMIZED) | |
355 workspace = new MaximizedWorkspace(this); | |
356 else | |
357 workspace = new ManagedWorkspace(this); | |
358 AddWorkspace(workspace); | |
359 return workspace; | |
360 } | |
361 | |
362 void WorkspaceManager::CleanupWorkspace(Workspace* workspace) { | |
363 if (workspace->type() != Workspace::TYPE_MANAGED && workspace->is_empty()) | |
364 delete workspace; | |
365 } | |
366 | |
367 } // namespace internal | |
368 } // namespace ash | |
OLD | NEW |