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_manager2.h" | |
6 | |
7 #include <algorithm> | |
8 #include <functional> | |
9 | |
10 #include "ash/ash_switches.h" | |
11 #include "ash/root_window_controller.h" | |
12 #include "ash/shell.h" | |
13 #include "ash/shell_window_ids.h" | |
14 #include "ash/wm/base_layout_manager.h" | |
15 #include "ash/wm/property_util.h" | |
16 #include "ash/wm/shelf_layout_manager.h" | |
17 #include "ash/wm/window_animations.h" | |
18 #include "ash/wm/window_properties.h" | |
19 #include "ash/wm/window_util.h" | |
20 #include "ash/wm/workspace/auto_window_management.h" | |
21 #include "ash/wm/workspace/desktop_background_fade_controller.h" | |
22 #include "ash/wm/workspace/workspace_animations.h" | |
23 #include "ash/wm/workspace/workspace_layout_manager2.h" | |
24 #include "ash/wm/workspace/workspace2.h" | |
25 #include "base/auto_reset.h" | |
26 #include "base/command_line.h" | |
27 #include "base/logging.h" | |
28 #include "base/stl_util.h" | |
29 #include "ui/aura/client/aura_constants.h" | |
30 #include "ui/aura/root_window.h" | |
31 #include "ui/aura/window.h" | |
32 #include "ui/aura/window_property.h" | |
33 #include "ui/base/ui_base_types.h" | |
34 #include "ui/compositor/layer.h" | |
35 #include "ui/compositor/layer_animator.h" | |
36 #include "ui/compositor/scoped_layer_animation_settings.h" | |
37 #include "ui/views/widget/widget.h" | |
38 | |
39 DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace2*); | |
40 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ASH_EXPORT, ui::WindowShowState); | |
41 | |
42 using aura::Window; | |
43 | |
44 namespace ash { | |
45 namespace internal { | |
46 | |
47 DEFINE_WINDOW_PROPERTY_KEY(Workspace2*, kWorkspaceKey, NULL); | |
48 | |
49 namespace { | |
50 | |
51 // Duration for fading out the desktop background when maximizing. | |
52 const int kCrossFadeSwitchTimeMS = 700; | |
53 | |
54 // Amount of time to pause before animating anything. Only used during initial | |
55 // animation (when logging in). | |
56 const int kInitialPauseTimeMS = 750; | |
57 | |
58 // Changes the parent of |window| and all its transient children to | |
59 // |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked | |
60 // beneath it. | |
61 void ReparentWindow(Window* window, | |
62 Window* new_parent, | |
63 Window* stack_beneath) { | |
64 window->SetParent(new_parent); | |
65 if (stack_beneath) | |
66 new_parent->StackChildBelow(window, stack_beneath); | |
67 for (size_t i = 0; i < window->transient_children().size(); ++i) | |
68 ReparentWindow(window->transient_children()[i], new_parent, stack_beneath); | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 // Workspace ------------------------------------------------------------------- | |
74 | |
75 // LayoutManager installed on the parent window of all the Workspace window (eg | |
76 // |WorkspaceManager2::contents_view_|). | |
77 class WorkspaceManager2::LayoutManagerImpl : public BaseLayoutManager { | |
78 public: | |
79 explicit LayoutManagerImpl(WorkspaceManager2* workspace_manager) | |
80 : BaseLayoutManager(workspace_manager->contents_view_->GetRootWindow()), | |
81 workspace_manager_(workspace_manager) { | |
82 } | |
83 virtual ~LayoutManagerImpl() {} | |
84 | |
85 // Overridden from BaseWorkspaceLayoutManager: | |
86 virtual void OnWindowResized() OVERRIDE { | |
87 for (size_t i = 0; i < window()->children().size(); ++i) | |
88 window()->children()[i]->SetBounds(gfx::Rect(window()->bounds().size())); | |
89 } | |
90 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE { | |
91 // Only workspaces should be added as children. | |
92 DCHECK((child->id() == kShellWindowId_WorkspaceContainer) || | |
93 workspace_manager_->creating_fade_); | |
94 child->SetBounds(gfx::Rect(window()->bounds().size())); | |
95 } | |
96 | |
97 private: | |
98 aura::Window* window() { return workspace_manager_->contents_view_; } | |
99 | |
100 WorkspaceManager2* workspace_manager_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(LayoutManagerImpl); | |
103 }; | |
104 | |
105 // WorkspaceManager2 ----------------------------------------------------------- | |
106 | |
107 WorkspaceManager2::WorkspaceManager2(Window* contents_view) | |
108 : contents_view_(contents_view), | |
109 active_workspace_(NULL), | |
110 shelf_(NULL), | |
111 in_move_(false), | |
112 ALLOW_THIS_IN_INITIALIZER_LIST( | |
113 clear_unminimizing_workspace_factory_(this)), | |
114 unminimizing_workspace_(NULL), | |
115 app_terminating_(false), | |
116 creating_fade_(false) { | |
117 // Clobber any existing event filter. | |
118 contents_view->SetEventFilter(NULL); | |
119 // |contents_view| takes ownership of LayoutManagerImpl. | |
120 contents_view->SetLayoutManager(new LayoutManagerImpl(this)); | |
121 active_workspace_ = CreateWorkspace(false); | |
122 workspaces_.push_back(active_workspace_); | |
123 active_workspace_->window()->Show(); | |
124 Shell::GetInstance()->AddShellObserver(this); | |
125 } | |
126 | |
127 WorkspaceManager2::~WorkspaceManager2() { | |
128 Shell::GetInstance()->RemoveShellObserver(this); | |
129 // Release the windows, they'll be destroyed when |contents_view_| is | |
130 // destroyed. | |
131 std::for_each(workspaces_.begin(), workspaces_.end(), | |
132 std::mem_fun(&Workspace2::ReleaseWindow)); | |
133 std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(), | |
134 std::mem_fun(&Workspace2::ReleaseWindow)); | |
135 std::for_each(to_delete_.begin(), to_delete_.end(), | |
136 std::mem_fun(&Workspace2::ReleaseWindow)); | |
137 STLDeleteElements(&workspaces_); | |
138 STLDeleteElements(&pending_workspaces_); | |
139 STLDeleteElements(&to_delete_); | |
140 } | |
141 | |
142 // static | |
143 bool WorkspaceManager2::IsMaximized(Window* window) { | |
144 return IsMaximizedState(window->GetProperty(aura::client::kShowStateKey)); | |
145 } | |
146 | |
147 // static | |
148 bool WorkspaceManager2::IsMaximizedState(ui::WindowShowState state) { | |
149 return state == ui::SHOW_STATE_MAXIMIZED || | |
150 state == ui::SHOW_STATE_FULLSCREEN; | |
151 } | |
152 | |
153 // static | |
154 bool WorkspaceManager2::WillRestoreMaximized(Window* window) { | |
155 return wm::IsWindowMinimized(window) && | |
156 IsMaximizedState(window->GetProperty(internal::kRestoreShowStateKey)); | |
157 } | |
158 | |
159 WorkspaceWindowState WorkspaceManager2::GetWindowState() const { | |
160 if (!shelf_) | |
161 return WORKSPACE_WINDOW_STATE_DEFAULT; | |
162 | |
163 const bool is_active_maximized = active_workspace_->is_maximized(); | |
164 const gfx::Rect shelf_bounds(shelf_->GetIdealBounds()); | |
165 const Window::Windows& windows(active_workspace_->window()->children()); | |
166 bool window_overlaps_launcher = false; | |
167 bool has_maximized_window = false; | |
168 for (Window::Windows::const_iterator i = windows.begin(); | |
169 i != windows.end(); ++i) { | |
170 if (GetIgnoredByShelf(*i)) | |
171 continue; | |
172 ui::Layer* layer = (*i)->layer(); | |
173 if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f) | |
174 continue; | |
175 // Ignore maximized/fullscreen windows if we're in the desktop. Such a state | |
176 // is transitory and means we haven't yet switched. If we did consider such | |
177 // windows we'll return the wrong thing, which can lead to prematurely | |
178 // changing the launcher state and clobbering restore bounds. | |
179 if (is_active_maximized) { | |
180 if (wm::IsWindowMaximized(*i)) { | |
181 // An untracked window may still be fullscreen so we keep iterating when | |
182 // we hit a maximized window. | |
183 has_maximized_window = true; | |
184 } else if (wm::IsWindowFullscreen(*i)) { | |
185 return WORKSPACE_WINDOW_STATE_FULL_SCREEN; | |
186 } | |
187 } | |
188 if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds)) | |
189 window_overlaps_launcher = true; | |
190 } | |
191 if (has_maximized_window) | |
192 return WORKSPACE_WINDOW_STATE_MAXIMIZED; | |
193 | |
194 return window_overlaps_launcher ? | |
195 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF : | |
196 WORKSPACE_WINDOW_STATE_DEFAULT; | |
197 } | |
198 | |
199 void WorkspaceManager2::SetShelf(ShelfLayoutManager* shelf) { | |
200 shelf_ = shelf; | |
201 } | |
202 | |
203 void WorkspaceManager2::SetActiveWorkspaceByWindow(Window* window) { | |
204 Workspace2* workspace = FindBy(window); | |
205 if (!workspace) | |
206 return; | |
207 | |
208 if (workspace != active_workspace_) { | |
209 // A window is being made active. In the following cases we reparent to | |
210 // the active desktop: | |
211 // . The window is not tracked by workspace code. This is used for tab | |
212 // dragging. Since tab dragging needs to happen in the active workspace we | |
213 // have to reparent the window (otherwise the window you dragged the tab | |
214 // out of would disappear since the workspace changed). Since this case is | |
215 // only transiently used (property reset on input release) we don't worry | |
216 // about window state. In fact we can't consider window state here as we | |
217 // have to allow dragging of a maximized window to work in this case. | |
218 // . The window persists across all workspaces. For example, the task | |
219 // manager is in the desktop worskpace and the current workspace is | |
220 // maximized. If we swapped to the desktop you would lose context. Instead | |
221 // we reparent. The exception to this is if the window is maximized (it | |
222 // needs its own workspace then) or we're in the process of maximizing. If | |
223 // we're in the process of maximizing the window needs its own workspace. | |
224 if (!GetTrackedByWorkspace(window) || | |
225 (GetPersistsAcrossAllWorkspaces(window) && !IsMaximized(window) && | |
226 !(wm::IsWindowMinimized(window) && WillRestoreMaximized(window)))) { | |
227 ReparentWindow(window, active_workspace_->window(), NULL); | |
228 } else { | |
229 SetActiveWorkspace(workspace, SWITCH_WINDOW_MADE_ACTIVE, | |
230 base::TimeDelta()); | |
231 } | |
232 } | |
233 if (workspace->is_maximized() && IsMaximized(window)) { | |
234 // Clicking on the maximized window in a maximized workspace. Force all | |
235 // other windows to drop to the desktop. | |
236 MoveChildrenToDesktop(workspace->window(), NULL); | |
237 } | |
238 } | |
239 | |
240 Window* WorkspaceManager2::GetParentForNewWindow(Window* window) { | |
241 // Try to put windows with transient parents in the same workspace as their | |
242 // transient parent. | |
243 if (window->transient_parent() && !IsMaximized(window)) { | |
244 Workspace2* workspace = FindBy(window->transient_parent()); | |
245 if (workspace) | |
246 return workspace->window(); | |
247 // Fall through to normal logic. | |
248 } | |
249 | |
250 if (!GetTrackedByWorkspace(window)) | |
251 return active_workspace_->window(); | |
252 | |
253 if (IsMaximized(window)) { | |
254 // Wait for the window to be made active before showing the workspace. | |
255 Workspace2* workspace = CreateWorkspace(true); | |
256 pending_workspaces_.insert(workspace); | |
257 return workspace->window(); | |
258 } | |
259 | |
260 if (!GetTrackedByWorkspace(window) || GetPersistsAcrossAllWorkspaces(window)) | |
261 return active_workspace_->window(); | |
262 | |
263 return desktop_workspace()->window(); | |
264 } | |
265 | |
266 void WorkspaceManager2::DoInitialAnimation() { | |
267 if (active_workspace_->is_maximized()) { | |
268 RootWindowController* root_controller = GetRootWindowController( | |
269 contents_view_->GetRootWindow()); | |
270 if (root_controller) { | |
271 aura::Window* background = root_controller->GetContainer( | |
272 kShellWindowId_DesktopBackgroundContainer); | |
273 background->Show(); | |
274 ShowOrHideDesktopBackground(background, SWITCH_INITIAL, | |
275 base::TimeDelta(), false); | |
276 } | |
277 } | |
278 ShowWorkspace(active_workspace_, active_workspace_, SWITCH_INITIAL); | |
279 } | |
280 | |
281 void WorkspaceManager2::OnAppTerminating() { | |
282 app_terminating_ = true; | |
283 } | |
284 | |
285 void WorkspaceManager2::UpdateShelfVisibility() { | |
286 if (shelf_) | |
287 shelf_->UpdateVisibilityState(); | |
288 } | |
289 | |
290 Workspace2* WorkspaceManager2::FindBy(Window* window) const { | |
291 while (window) { | |
292 Workspace2* workspace = window->GetProperty(kWorkspaceKey); | |
293 if (workspace) | |
294 return workspace; | |
295 window = window->parent(); | |
296 } | |
297 return NULL; | |
298 } | |
299 | |
300 void WorkspaceManager2::SetActiveWorkspace(Workspace2* workspace, | |
301 SwitchReason reason, | |
302 base::TimeDelta duration) { | |
303 DCHECK(workspace); | |
304 if (active_workspace_ == workspace) | |
305 return; | |
306 | |
307 pending_workspaces_.erase(workspace); | |
308 | |
309 // Adjust the z-order. No need to adjust the z-order for the desktop since | |
310 // it always stays at the bottom. | |
311 if (workspace != desktop_workspace() && | |
312 FindWorkspace(workspace) == workspaces_.end()) { | |
313 contents_view_->StackChildAbove(workspace->window(), | |
314 workspaces_.back()->window()); | |
315 workspaces_.push_back(workspace); | |
316 } | |
317 | |
318 Workspace2* last_active = active_workspace_; | |
319 active_workspace_ = workspace; | |
320 | |
321 // The display work-area may have changed while |workspace| was not the active | |
322 // workspace. Give it a chance to adjust its state for the new work-area. | |
323 active_workspace_->workspace_layout_manager()-> | |
324 OnDisplayWorkAreaInsetsChanged(); | |
325 | |
326 const bool is_unminimizing_maximized_window = | |
327 unminimizing_workspace_ && unminimizing_workspace_ == active_workspace_ && | |
328 active_workspace_->is_maximized(); | |
329 if (is_unminimizing_maximized_window) { | |
330 // If we're unminimizing a window it needs to be on the top, otherwise you | |
331 // won't see the animation. | |
332 contents_view_->StackChildAtTop(active_workspace_->window()); | |
333 } else if (active_workspace_->is_maximized() && last_active->is_maximized()) { | |
334 // When switching between maximized windows we need the last active | |
335 // workspace on top of the new, otherwise the animations won't look | |
336 // right. Since only one workspace is visible at a time stacking order of | |
337 // the workspace windows ultimately doesn't matter. | |
338 contents_view_->StackChildAtTop(last_active->window()); | |
339 } | |
340 | |
341 UpdateShelfVisibility(); | |
342 | |
343 // NOTE: duration supplied to this method is only used for desktop background. | |
344 HideWorkspace(last_active, reason, is_unminimizing_maximized_window); | |
345 ShowWorkspace(workspace, last_active, reason); | |
346 | |
347 RootWindowController* root_controller = GetRootWindowController( | |
348 contents_view_->GetRootWindow()); | |
349 if (root_controller) { | |
350 aura::Window* background = root_controller->GetContainer( | |
351 kShellWindowId_DesktopBackgroundContainer); | |
352 if (last_active == desktop_workspace()) { | |
353 ShowOrHideDesktopBackground(background, reason, duration, false); | |
354 } else if (active_workspace_ == desktop_workspace() && !app_terminating_) { | |
355 ShowOrHideDesktopBackground(background, reason, duration, true); | |
356 } | |
357 } | |
358 } | |
359 | |
360 WorkspaceManager2::Workspaces::iterator | |
361 WorkspaceManager2::FindWorkspace(Workspace2* workspace) { | |
362 return std::find(workspaces_.begin(), workspaces_.end(), workspace); | |
363 } | |
364 | |
365 Workspace2* WorkspaceManager2::CreateWorkspace(bool maximized) { | |
366 return new Workspace2(this, contents_view_, maximized); | |
367 } | |
368 | |
369 void WorkspaceManager2::MoveWorkspaceToPendingOrDelete( | |
370 Workspace2* workspace, | |
371 Window* stack_beneath, | |
372 SwitchReason reason) { | |
373 // We're all ready moving windows. | |
374 if (in_move_) | |
375 return; | |
376 | |
377 DCHECK_NE(desktop_workspace(), workspace); | |
378 | |
379 if (workspace == active_workspace_) | |
380 SelectNextWorkspace(reason); | |
381 | |
382 AutoReset<bool> setter(&in_move_, true); | |
383 | |
384 MoveChildrenToDesktop(workspace->window(), stack_beneath); | |
385 | |
386 { | |
387 Workspaces::iterator workspace_i(FindWorkspace(workspace)); | |
388 if (workspace_i != workspaces_.end()) | |
389 workspaces_.erase(workspace_i); | |
390 } | |
391 | |
392 if (workspace->window()->children().empty()) { | |
393 if (workspace == unminimizing_workspace_) | |
394 unminimizing_workspace_ = NULL; | |
395 pending_workspaces_.erase(workspace); | |
396 ScheduleDelete(workspace); | |
397 } else { | |
398 pending_workspaces_.insert(workspace); | |
399 } | |
400 } | |
401 | |
402 void WorkspaceManager2::MoveChildrenToDesktop(aura::Window* window, | |
403 aura::Window* stack_beneath) { | |
404 // Build the list of windows to move. Exclude maximized/fullscreen and windows | |
405 // with transient parents. | |
406 Window::Windows to_move; | |
407 for (size_t i = 0; i < window->children().size(); ++i) { | |
408 Window* child = window->children()[i]; | |
409 if (!child->transient_parent() && !IsMaximized(child) && | |
410 !WillRestoreMaximized(child)) { | |
411 to_move.push_back(child); | |
412 } | |
413 } | |
414 // Move the windows, but make sure the window is still a child of |window| | |
415 // (moving may cascade and cause other windows to move). | |
416 for (size_t i = 0; i < to_move.size(); ++i) { | |
417 if (std::find(window->children().begin(), window->children().end(), | |
418 to_move[i]) != window->children().end()) { | |
419 ReparentWindow(to_move[i], desktop_workspace()->window(), | |
420 stack_beneath); | |
421 } | |
422 } | |
423 } | |
424 | |
425 void WorkspaceManager2::SelectNextWorkspace(SwitchReason reason) { | |
426 DCHECK_NE(active_workspace_, desktop_workspace()); | |
427 | |
428 Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_)); | |
429 Workspaces::const_iterator next_workspace_i(workspace_i + 1); | |
430 if (next_workspace_i != workspaces_.end()) | |
431 SetActiveWorkspace(*next_workspace_i, reason, base::TimeDelta()); | |
432 else | |
433 SetActiveWorkspace(*(workspace_i - 1), reason, base::TimeDelta()); | |
434 } | |
435 | |
436 void WorkspaceManager2::ScheduleDelete(Workspace2* workspace) { | |
437 to_delete_.insert(workspace); | |
438 delete_timer_.Stop(); | |
439 delete_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, | |
440 &WorkspaceManager2::ProcessDeletion); | |
441 } | |
442 | |
443 void WorkspaceManager2::SetUnminimizingWorkspace(Workspace2* workspace) { | |
444 // The normal sequence of unminimizing a window is: Show() the window, which | |
445 // triggers changing the kShowStateKey to NORMAL and lastly the window is made | |
446 // active. This means at the time the window is unminimized we don't know if | |
447 // the workspace it is in is going to become active. To track this | |
448 // |unminimizing_workspace_| is set at the time we unminimize and a task is | |
449 // schedule to reset it. This way when we get the activate we know we're in | |
450 // the process unminimizing and can do the right animation. | |
451 unminimizing_workspace_ = workspace; | |
452 if (unminimizing_workspace_) { | |
453 MessageLoop::current()->PostTask( | |
454 FROM_HERE, | |
455 base::Bind(&WorkspaceManager2::SetUnminimizingWorkspace, | |
456 clear_unminimizing_workspace_factory_.GetWeakPtr(), | |
457 static_cast<Workspace2*>(NULL))); | |
458 } | |
459 } | |
460 | |
461 void WorkspaceManager2::FadeDesktop(aura::Window* window, | |
462 base::TimeDelta duration) { | |
463 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
464 ash::switches::kAshWindowAnimationsDisabled) || | |
465 ui::LayerAnimator::disable_animations_for_test()) | |
466 return; | |
467 | |
468 AutoReset<bool> reseter(&creating_fade_, true); | |
469 DesktopBackgroundFadeController::Direction direction; | |
470 aura::Window* parent = NULL; | |
471 aura::Window* stack_above = NULL; | |
472 if (active_workspace_ == desktop_workspace()) { | |
473 direction = DesktopBackgroundFadeController::FADE_IN; | |
474 parent = desktop_workspace()->window(); | |
475 stack_above = window; | |
476 } else { | |
477 direction = DesktopBackgroundFadeController::FADE_OUT; | |
478 parent = contents_view_; | |
479 stack_above = desktop_workspace()->window(); | |
480 DCHECK_EQ(kCrossFadeSwitchTimeMS, (int)duration.InMilliseconds()); | |
481 duration = base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS); | |
482 } | |
483 desktop_fade_controller_.reset( | |
484 new DesktopBackgroundFadeController( | |
485 parent, stack_above, duration, direction)); | |
486 } | |
487 | |
488 void WorkspaceManager2::ShowOrHideDesktopBackground( | |
489 aura::Window* window, | |
490 SwitchReason reason, | |
491 base::TimeDelta duration, | |
492 bool show) const { | |
493 WorkspaceAnimationDetails details; | |
494 details.animate = true; | |
495 details.direction = show ? WORKSPACE_ANIMATE_UP : WORKSPACE_ANIMATE_DOWN; | |
496 details.animate_scale = reason != SWITCH_MAXIMIZED_OR_RESTORED; | |
497 details.duration = duration; | |
498 if (reason == SWITCH_INITIAL) | |
499 details.pause_time_ms = kInitialPauseTimeMS; | |
500 if (show) | |
501 ash::internal::ShowWorkspace(window, details); | |
502 else | |
503 ash::internal::HideWorkspace(window, details); | |
504 } | |
505 | |
506 void WorkspaceManager2::ShowWorkspace( | |
507 Workspace2* workspace, | |
508 Workspace2* last_active, | |
509 SwitchReason reason) const { | |
510 WorkspaceAnimationDetails details; | |
511 details.direction = | |
512 (last_active == desktop_workspace() || reason == SWITCH_INITIAL) ? | |
513 WORKSPACE_ANIMATE_DOWN : WORKSPACE_ANIMATE_UP; | |
514 | |
515 switch (reason) { | |
516 case SWITCH_WINDOW_MADE_ACTIVE: | |
517 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED: | |
518 case SWITCH_WINDOW_REMOVED: | |
519 case SWITCH_VISIBILITY_CHANGED: | |
520 case SWITCH_MINIMIZED: | |
521 details.animate = details.animate_scale = true; | |
522 details.animate_opacity = last_active == desktop_workspace(); | |
523 break; | |
524 | |
525 case SWITCH_INITIAL: | |
526 details.animate = details.animate_opacity = details.animate_scale = true; | |
527 details.pause_time_ms = kInitialPauseTimeMS; | |
528 break; | |
529 | |
530 // Remaining cases require no animation. | |
531 default: | |
532 break; | |
533 } | |
534 ash::internal::ShowWorkspace(workspace->window(), details); | |
535 } | |
536 | |
537 void WorkspaceManager2::HideWorkspace( | |
538 Workspace2* workspace, | |
539 SwitchReason reason, | |
540 bool is_unminimizing_maximized_window) const { | |
541 WorkspaceAnimationDetails details; | |
542 details.direction = active_workspace_ == desktop_workspace() ? | |
543 WORKSPACE_ANIMATE_UP : WORKSPACE_ANIMATE_DOWN; | |
544 switch (reason) { | |
545 case SWITCH_WINDOW_MADE_ACTIVE: | |
546 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED: | |
547 details.animate_opacity = | |
548 ((active_workspace_ == desktop_workspace() || | |
549 workspace != desktop_workspace()) && | |
550 !is_unminimizing_maximized_window); | |
551 details.animate_scale = true; | |
552 details.animate = true; | |
553 break; | |
554 | |
555 case SWITCH_MAXIMIZED_OR_RESTORED: | |
556 if (active_workspace_->is_maximized()) { | |
557 // Delay the hide until the animation is done. | |
558 details.duration = | |
559 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS); | |
560 details.animate = true; | |
561 } | |
562 break; | |
563 | |
564 // Remaining cases require no animation. | |
565 default: | |
566 break; | |
567 } | |
568 ash::internal::HideWorkspace(workspace->window(), details); | |
569 } | |
570 | |
571 void WorkspaceManager2::ProcessDeletion() { | |
572 std::set<Workspace2*> to_delete; | |
573 to_delete.swap(to_delete_); | |
574 for (std::set<Workspace2*>::iterator i = to_delete.begin(); | |
575 i != to_delete.end(); ++i) { | |
576 Workspace2* workspace = *i; | |
577 if (workspace->window()->layer()->children().empty()) { | |
578 delete workspace->ReleaseWindow(); | |
579 delete workspace; | |
580 } else { | |
581 to_delete_.insert(workspace); | |
582 } | |
583 } | |
584 if (!to_delete_.empty()) { | |
585 delete_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, | |
586 &WorkspaceManager2::ProcessDeletion); | |
587 } | |
588 } | |
589 | |
590 void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace, | |
591 Window* child) { | |
592 child->SetProperty(kWorkspaceKey, workspace); | |
593 // Do nothing (other than updating shelf visibility) as the right parent was | |
594 // chosen by way of GetParentForNewWindow() or we explicitly moved the window | |
595 // to the workspace. | |
596 if (workspace == active_workspace_) | |
597 UpdateShelfVisibility(); | |
598 | |
599 RearrangeVisibleWindowOnShow(child); | |
600 } | |
601 | |
602 void WorkspaceManager2::OnWillRemoveWindowFromWorkspace(Workspace2* workspace, | |
603 Window* child) { | |
604 if (child->TargetVisibility()) | |
605 RearrangeVisibleWindowOnHideOrRemove(child); | |
606 child->ClearProperty(kWorkspaceKey); | |
607 } | |
608 | |
609 void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace, | |
610 Window* child) { | |
611 if (workspace->ShouldMoveToPending()) | |
612 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_WINDOW_REMOVED); | |
613 } | |
614 | |
615 void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged( | |
616 Workspace2* workspace, | |
617 Window* child) { | |
618 if (workspace->ShouldMoveToPending()) { | |
619 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_VISIBILITY_CHANGED); | |
620 } else { | |
621 if (child->TargetVisibility()) | |
622 RearrangeVisibleWindowOnShow(child); | |
623 else | |
624 RearrangeVisibleWindowOnHideOrRemove(child); | |
625 if (workspace == active_workspace_) | |
626 UpdateShelfVisibility(); | |
627 } | |
628 } | |
629 | |
630 void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged( | |
631 Workspace2* workspace, | |
632 Window* child) { | |
633 if (workspace == active_workspace_) | |
634 UpdateShelfVisibility(); | |
635 } | |
636 | |
637 void WorkspaceManager2::OnWorkspaceWindowShowStateChanged( | |
638 Workspace2* workspace, | |
639 Window* child, | |
640 ui::WindowShowState last_show_state, | |
641 ui::Layer* old_layer) { | |
642 // |child| better still be in |workspace| else things have gone wrong. | |
643 DCHECK_EQ(workspace, child->GetProperty(kWorkspaceKey)); | |
644 if (wm::IsWindowMinimized(child)) { | |
645 if (workspace->ShouldMoveToPending()) | |
646 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_MINIMIZED); | |
647 DCHECK(!old_layer); | |
648 } else { | |
649 // Set of cases to deal with: | |
650 // . More than one maximized window: move newly maximized window into | |
651 // own workspace. | |
652 // . One maximized window and not in a maximized workspace: move window | |
653 // into own workspace. | |
654 // . No maximized window and not in desktop: move to desktop and further | |
655 // any existing windows are stacked beneath |child|. | |
656 const bool is_active = wm::IsActiveWindow(child); | |
657 Workspace2* new_workspace = NULL; | |
658 const int max_count = workspace->GetNumMaximizedWindows(); | |
659 base::TimeDelta duration = old_layer && !IsMaximized(child) ? | |
660 GetCrossFadeDuration(old_layer->bounds(), child->bounds()) : | |
661 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS); | |
662 if (max_count == 0) { | |
663 if (workspace != desktop_workspace()) { | |
664 { | |
665 AutoReset<bool> setter(&in_move_, true); | |
666 ReparentWindow(child, desktop_workspace()->window(), NULL); | |
667 } | |
668 DCHECK(!is_active || old_layer); | |
669 new_workspace = desktop_workspace(); | |
670 SetActiveWorkspace(new_workspace, SWITCH_MAXIMIZED_OR_RESTORED, | |
671 duration); | |
672 MoveWorkspaceToPendingOrDelete(workspace, child, | |
673 SWITCH_MAXIMIZED_OR_RESTORED); | |
674 if (FindWorkspace(workspace) == workspaces_.end()) | |
675 workspace = NULL; | |
676 } | |
677 } else if ((max_count == 1 && workspace == desktop_workspace()) || | |
678 max_count > 1) { | |
679 new_workspace = CreateWorkspace(true); | |
680 pending_workspaces_.insert(new_workspace); | |
681 ReparentWindow(child, new_workspace->window(), NULL); | |
682 } | |
683 if (is_active && new_workspace) { | |
684 // |old_layer| may be NULL if as part of processing | |
685 // WorkspaceLayoutManager2::OnWindowPropertyChanged() the window is made | |
686 // active. | |
687 if (old_layer) { | |
688 SetActiveWorkspace(new_workspace, SWITCH_MAXIMIZED_OR_RESTORED, | |
689 duration); | |
690 CrossFadeWindowBetweenWorkspaces(new_workspace->window(), child, | |
691 old_layer); | |
692 if (workspace == desktop_workspace() || | |
693 new_workspace == desktop_workspace()) { | |
694 FadeDesktop(child, duration); | |
695 } | |
696 } else { | |
697 SetActiveWorkspace(new_workspace, SWITCH_OTHER, base::TimeDelta()); | |
698 } | |
699 } else { | |
700 if (last_show_state == ui::SHOW_STATE_MINIMIZED) | |
701 SetUnminimizingWorkspace(new_workspace ? new_workspace : workspace); | |
702 DCHECK(!old_layer); | |
703 } | |
704 } | |
705 UpdateShelfVisibility(); | |
706 } | |
707 | |
708 void WorkspaceManager2::OnTrackedByWorkspaceChanged(Workspace2* workspace, | |
709 aura::Window* window) { | |
710 Workspace2* new_workspace = NULL; | |
711 if (IsMaximized(window)) { | |
712 new_workspace = CreateWorkspace(true); | |
713 pending_workspaces_.insert(new_workspace); | |
714 } else if (workspace->is_maximized()) { | |
715 new_workspace = desktop_workspace(); | |
716 } else { | |
717 return; | |
718 } | |
719 // If the window is active we need to make sure the destination Workspace | |
720 // window is showing. Otherwise the window will be parented to a hidden window | |
721 // and lose activation. | |
722 const bool is_active = wm::IsActiveWindow(window); | |
723 if (is_active) | |
724 new_workspace->window()->Show(); | |
725 ReparentWindow(window, new_workspace->window(), NULL); | |
726 if (is_active) { | |
727 SetActiveWorkspace(new_workspace, SWITCH_TRACKED_BY_WORKSPACE_CHANGED, | |
728 base::TimeDelta()); | |
729 } | |
730 } | |
731 | |
732 } // namespace internal | |
733 } // namespace ash | |
OLD | NEW |