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.
5 #include "ash/wm/workspace/workspace_manager.h"
10 #include "ash/root_window_controller.h"
11 #include "ash/shelf/shelf_layout_manager.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/frame_painter.h"
16 #include "ash/wm/property_util.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_cycler.h"
24 #include "ash/wm/workspace/workspace_cycler_animator.h"
25 #include "ash/wm/workspace/workspace_cycler_configuration.h"
26 #include "ash/wm/workspace/workspace_layout_manager.h"
27 #include "ash/wm/workspace/workspace.h"
28 #include "base/auto_reset.h"
29 #include "base/logging.h"
30 #include "base/stl_util.h"
31 #include "ui/aura/client/aura_constants.h"
32 #include "ui/aura/root_window.h"
33 #include "ui/aura/window.h"
34 #include "ui/aura/window_property.h"
35 #include "ui/base/ui_base_types.h"
36 #include "ui/compositor/layer.h"
37 #include "ui/compositor/layer_animator.h"
38 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
39 #include "ui/compositor/scoped_layer_animation_settings.h"
40 #include "ui/views/widget/widget.h"
42 DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace
*);
43 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ASH_EXPORT
, ui::WindowShowState
);
50 DEFINE_WINDOW_PROPERTY_KEY(Workspace
*, kWorkspaceKey
, NULL
);
54 // Duration for fading out the desktop background when maximizing.
55 const int kCrossFadeSwitchTimeMS
= 700;
57 // Amount of time to pause before animating anything. Only used during initial
58 // animation (when logging in).
59 const int kInitialPauseTimeMS
= 750;
61 // Changes the parent of |window| and all its transient children to
62 // |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked
64 void ReparentWindow(Window
* window
,
66 Window
* stack_beneath
) {
67 new_parent
->AddChild(window
);
69 new_parent
->StackChildBelow(window
, stack_beneath
);
70 for (size_t i
= 0; i
< window
->transient_children().size(); ++i
)
71 ReparentWindow(window
->transient_children()[i
], new_parent
, stack_beneath
);
76 // Workspace -------------------------------------------------------------------
78 // LayoutManager installed on the parent window of all the Workspace window (eg
79 // |WorkspaceManager::contents_view_|).
80 class WorkspaceManager::LayoutManagerImpl
: public BaseLayoutManager
{
82 explicit LayoutManagerImpl(WorkspaceManager
* workspace_manager
)
83 : BaseLayoutManager(workspace_manager
->contents_view_
->GetRootWindow()),
84 workspace_manager_(workspace_manager
) {
86 virtual ~LayoutManagerImpl() {}
88 // Overridden from BaseWorkspaceLayoutManager:
89 virtual void OnWindowResized() OVERRIDE
{
90 for (size_t i
= 0; i
< window()->children().size(); ++i
)
91 window()->children()[i
]->SetBounds(gfx::Rect(window()->bounds().size()));
93 virtual void OnWindowAddedToLayout(Window
* child
) OVERRIDE
{
94 // Only workspaces should be added as children.
95 DCHECK((child
->id() == kShellWindowId_WorkspaceContainer
) ||
96 workspace_manager_
->creating_fade_
);
97 child
->SetBounds(gfx::Rect(window()->bounds().size()));
101 aura::Window
* window() { return workspace_manager_
->contents_view_
; }
103 WorkspaceManager
* workspace_manager_
;
105 DISALLOW_COPY_AND_ASSIGN(LayoutManagerImpl
);
108 // WorkspaceManager -----------------------------------------------------------
110 WorkspaceManager::WorkspaceManager(Window
* contents_view
)
111 : contents_view_(contents_view
),
112 active_workspace_(NULL
),
115 ALLOW_THIS_IN_INITIALIZER_LIST(
116 clear_unminimizing_workspace_factory_(this)),
117 unminimizing_workspace_(NULL
),
118 app_terminating_(false),
119 creating_fade_(false),
120 workspace_cycler_(NULL
) {
121 // Clobber any existing event filter.
122 contents_view
->SetEventFilter(NULL
);
123 // |contents_view| takes ownership of LayoutManagerImpl.
124 contents_view
->SetLayoutManager(new LayoutManagerImpl(this));
125 active_workspace_
= CreateWorkspace(false);
126 workspaces_
.push_back(active_workspace_
);
127 active_workspace_
->window()->Show();
128 Shell::GetInstance()->AddShellObserver(this);
130 if (ash::WorkspaceCyclerConfiguration::IsCyclerEnabled())
131 workspace_cycler_
.reset(new WorkspaceCycler(this));
134 WorkspaceManager::~WorkspaceManager() {
135 Shell::GetInstance()->RemoveShellObserver(this);
136 // Release the windows, they'll be destroyed when |contents_view_| is
138 std::for_each(workspaces_
.begin(), workspaces_
.end(),
139 std::mem_fun(&Workspace::ReleaseWindow
));
140 std::for_each(pending_workspaces_
.begin(), pending_workspaces_
.end(),
141 std::mem_fun(&Workspace::ReleaseWindow
));
142 std::for_each(to_delete_
.begin(), to_delete_
.end(),
143 std::mem_fun(&Workspace::ReleaseWindow
));
144 STLDeleteElements(&workspaces_
);
145 STLDeleteElements(&pending_workspaces_
);
146 STLDeleteElements(&to_delete_
);
150 bool WorkspaceManager::IsMaximized(Window
* window
) {
151 return IsMaximizedState(window
->GetProperty(aura::client::kShowStateKey
));
155 bool WorkspaceManager::IsMaximizedState(ui::WindowShowState state
) {
156 return state
== ui::SHOW_STATE_MAXIMIZED
||
157 state
== ui::SHOW_STATE_FULLSCREEN
;
161 bool WorkspaceManager::WillRestoreMaximized(Window
* window
) {
162 return wm::IsWindowMinimized(window
) &&
163 IsMaximizedState(window
->GetProperty(internal::kRestoreShowStateKey
));
166 WorkspaceWindowState
WorkspaceManager::GetWindowState() const {
168 return WORKSPACE_WINDOW_STATE_DEFAULT
;
170 const bool is_active_maximized
= active_workspace_
->is_maximized();
171 const gfx::Rect
shelf_bounds(shelf_
->GetIdealBounds());
172 const Window::Windows
& windows(active_workspace_
->window()->children());
173 bool window_overlaps_launcher
= false;
174 bool has_maximized_window
= false;
175 for (Window::Windows::const_iterator i
= windows
.begin();
176 i
!= windows
.end(); ++i
) {
177 if (GetIgnoredByShelf(*i
))
179 ui::Layer
* layer
= (*i
)->layer();
180 if (!layer
->GetTargetVisibility() || layer
->GetTargetOpacity() == 0.0f
)
182 // Ignore maximized/fullscreen windows if we're in the desktop. Such a state
183 // is transitory and means we haven't yet switched. If we did consider such
184 // windows we'll return the wrong thing, which can lead to prematurely
185 // changing the launcher state and clobbering restore bounds.
186 if (is_active_maximized
) {
187 if (wm::IsWindowMaximized(*i
)) {
188 // An untracked window may still be fullscreen so we keep iterating when
189 // we hit a maximized window.
190 has_maximized_window
= true;
191 } else if (wm::IsWindowFullscreen(*i
)) {
192 return WORKSPACE_WINDOW_STATE_FULL_SCREEN
;
195 if (!window_overlaps_launcher
&& (*i
)->bounds().Intersects(shelf_bounds
))
196 window_overlaps_launcher
= true;
198 if (has_maximized_window
)
199 return WORKSPACE_WINDOW_STATE_MAXIMIZED
;
201 return window_overlaps_launcher
?
202 WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF
:
203 WORKSPACE_WINDOW_STATE_DEFAULT
;
206 void WorkspaceManager::SetShelf(ShelfLayoutManager
* shelf
) {
210 void WorkspaceManager::SetActiveWorkspaceByWindow(Window
* window
) {
211 Workspace
* workspace
= FindBy(window
);
215 if (workspace
!= active_workspace_
) {
216 // A window is being made active. In the following cases we reparent to
217 // the active desktop:
218 // . The window is not tracked by workspace code. This is used for tab
219 // dragging. Since tab dragging needs to happen in the active workspace we
220 // have to reparent the window (otherwise the window you dragged the tab
221 // out of would disappear since the workspace changed). Since this case is
222 // only transiently used (property reset on input release) we don't worry
223 // about window state. In fact we can't consider window state here as we
224 // have to allow dragging of a maximized window to work in this case.
225 // . The window persists across all workspaces. For example, the task
226 // manager is in the desktop worskpace and the current workspace is
227 // maximized. If we swapped to the desktop you would lose context. Instead
228 // we reparent. The exception to this is if the window is maximized (it
229 // needs its own workspace then) or we're in the process of maximizing. If
230 // we're in the process of maximizing the window needs its own workspace.
231 if (!GetTrackedByWorkspace(window
) ||
232 (GetPersistsAcrossAllWorkspaces(window
) && !IsMaximized(window
) &&
233 !(wm::IsWindowMinimized(window
) && WillRestoreMaximized(window
)))) {
234 ReparentWindow(window
, active_workspace_
->window(), NULL
);
236 SetActiveWorkspace(workspace
, SWITCH_WINDOW_MADE_ACTIVE
,
240 if (workspace
->is_maximized() && IsMaximized(window
)) {
241 // Clicking on the maximized window in a maximized workspace. Force all
242 // other windows to drop to the desktop.
243 MoveChildrenToDesktop(workspace
->window(), NULL
);
247 Window
* WorkspaceManager::GetActiveWorkspaceWindow() {
248 return active_workspace_
->window();
251 Window
* WorkspaceManager::GetParentForNewWindow(Window
* window
) {
252 // Try to put windows with transient parents in the same workspace as their
254 if (window
->transient_parent() && !IsMaximized(window
)) {
255 Workspace
* workspace
= FindBy(window
->transient_parent());
257 return workspace
->window();
258 // Fall through to normal logic.
261 if (!GetTrackedByWorkspace(window
))
262 return active_workspace_
->window();
264 if (IsMaximized(window
)) {
265 // Wait for the window to be made active before showing the workspace.
266 Workspace
* workspace
= CreateWorkspace(true);
267 pending_workspaces_
.insert(workspace
);
268 return workspace
->window();
271 if (!GetTrackedByWorkspace(window
) || GetPersistsAcrossAllWorkspaces(window
))
272 return active_workspace_
->window();
274 return desktop_workspace()->window();
277 bool WorkspaceManager::CanStartCyclingThroughWorkspaces() const {
278 return workspace_cycler_
.get() && workspaces_
.size() > 1u;
281 void WorkspaceManager::InitWorkspaceCyclerAnimatorWithCurrentState(
282 WorkspaceCyclerAnimator
* animator
) {
284 animator
->Init(workspaces_
, active_workspace_
);
287 void WorkspaceManager::SetActiveWorkspaceFromCycler(Workspace
* workspace
) {
288 if (!workspace
|| workspace
== active_workspace_
)
291 SetActiveWorkspace(workspace
, SWITCH_WORKSPACE_CYCLER
, base::TimeDelta());
293 // Activate the topmost window in the newly activated workspace as
294 // SetActiveWorkspace() does not do so.
295 aura::Window
* topmost_activatable_window
=
296 workspace
->GetTopmostActivatableWindow();
297 if (topmost_activatable_window
)
298 wm::ActivateWindow(topmost_activatable_window
);
301 void WorkspaceManager::DoInitialAnimation() {
302 if (active_workspace_
->is_maximized()) {
303 RootWindowController
* root_controller
= GetRootWindowController(
304 contents_view_
->GetRootWindow());
305 if (root_controller
) {
306 aura::Window
* background
= root_controller
->GetContainer(
307 kShellWindowId_DesktopBackgroundContainer
);
309 ShowOrHideDesktopBackground(background
, SWITCH_INITIAL
,
310 base::TimeDelta(), false);
313 ShowWorkspace(active_workspace_
, active_workspace_
, SWITCH_INITIAL
);
316 void WorkspaceManager::OnAppTerminating() {
317 app_terminating_
= true;
320 void WorkspaceManager::UpdateShelfVisibility() {
322 shelf_
->UpdateVisibilityState();
325 Workspace
* WorkspaceManager::FindBy(Window
* window
) const {
327 Workspace
* workspace
= window
->GetProperty(kWorkspaceKey
);
330 window
= window
->parent();
335 void WorkspaceManager::SetActiveWorkspace(Workspace
* workspace
,
337 base::TimeDelta duration
) {
339 if (active_workspace_
== workspace
)
342 // It is possible for a user to use accelerator keys to restore windows etc
343 // while the user is cycling through workspaces.
344 if (workspace_cycler_
.get())
345 workspace_cycler_
->AbortCycling();
347 pending_workspaces_
.erase(workspace
);
349 // Adjust the z-order. No need to adjust the z-order for the desktop since
350 // it always stays at the bottom.
351 if (workspace
!= desktop_workspace() &&
352 FindWorkspace(workspace
) == workspaces_
.end()) {
353 contents_view_
->StackChildAbove(workspace
->window(),
354 workspaces_
.back()->window());
355 workspaces_
.push_back(workspace
);
358 Workspace
* last_active
= active_workspace_
;
359 active_workspace_
= workspace
;
361 // The display work-area may have changed while |workspace| was not the active
362 // workspace. Give it a chance to adjust its state for the new work-area.
363 active_workspace_
->workspace_layout_manager()->
364 OnDisplayWorkAreaInsetsChanged();
366 const bool is_unminimizing_maximized_window
=
367 unminimizing_workspace_
&& unminimizing_workspace_
== active_workspace_
&&
368 active_workspace_
->is_maximized();
369 if (is_unminimizing_maximized_window
) {
370 // If we're unminimizing a window it needs to be on the top, otherwise you
371 // won't see the animation.
372 contents_view_
->StackChildAtTop(active_workspace_
->window());
373 } else if (active_workspace_
->is_maximized() && last_active
->is_maximized()) {
374 // When switching between maximized windows we need the last active
375 // workspace on top of the new, otherwise the animations won't look
376 // right. Since only one workspace is visible at a time stacking order of
377 // the workspace windows ultimately doesn't matter.
378 contents_view_
->StackChildAtTop(last_active
->window());
381 UpdateShelfVisibility();
383 // NOTE: duration supplied to this method is only used for desktop background.
384 HideWorkspace(last_active
, reason
, is_unminimizing_maximized_window
);
385 ShowWorkspace(workspace
, last_active
, reason
);
387 RootWindowController
* root_controller
= GetRootWindowController(
388 contents_view_
->GetRootWindow());
389 if (root_controller
) {
390 aura::Window
* background
= root_controller
->GetContainer(
391 kShellWindowId_DesktopBackgroundContainer
);
392 if (last_active
== desktop_workspace()) {
393 ShowOrHideDesktopBackground(background
, reason
, duration
, false);
394 } else if (active_workspace_
== desktop_workspace() && !app_terminating_
) {
395 ShowOrHideDesktopBackground(background
, reason
, duration
, true);
399 // Showing or hiding a workspace may change the "solo window" status of
400 // a window, requiring the header to be updated.
401 FramePainter::UpdateSoloWindowHeader(contents_view_
->GetRootWindow());
404 WorkspaceManager::Workspaces::iterator
405 WorkspaceManager::FindWorkspace(Workspace
* workspace
) {
406 return std::find(workspaces_
.begin(), workspaces_
.end(), workspace
);
409 Workspace
* WorkspaceManager::CreateWorkspace(bool maximized
) {
410 return new Workspace(this, contents_view_
, maximized
);
413 void WorkspaceManager::MoveWorkspaceToPendingOrDelete(
414 Workspace
* workspace
,
415 Window
* stack_beneath
,
416 SwitchReason reason
) {
417 // We're all ready moving windows.
421 DCHECK_NE(desktop_workspace(), workspace
);
423 // The user may have closed or minimized a window via accelerator keys while
424 // cycling through workspaces.
425 if (workspace_cycler_
.get())
426 workspace_cycler_
->AbortCycling();
428 if (workspace
== active_workspace_
)
429 SelectNextWorkspace(reason
);
431 base::AutoReset
<bool> setter(&in_move_
, true);
433 MoveChildrenToDesktop(workspace
->window(), stack_beneath
);
436 Workspaces::iterator
workspace_i(FindWorkspace(workspace
));
437 if (workspace_i
!= workspaces_
.end())
438 workspaces_
.erase(workspace_i
);
441 if (workspace
->window()->children().empty()) {
442 if (workspace
== unminimizing_workspace_
)
443 unminimizing_workspace_
= NULL
;
444 pending_workspaces_
.erase(workspace
);
445 ScheduleDelete(workspace
);
447 pending_workspaces_
.insert(workspace
);
451 void WorkspaceManager::MoveChildrenToDesktop(aura::Window
* window
,
452 aura::Window
* stack_beneath
) {
453 // Build the list of windows to move. Exclude maximized/fullscreen and windows
454 // with transient parents.
455 Window::Windows to_move
;
456 for (size_t i
= 0; i
< window
->children().size(); ++i
) {
457 Window
* child
= window
->children()[i
];
458 if (!child
->transient_parent() && !IsMaximized(child
) &&
459 !WillRestoreMaximized(child
)) {
460 to_move
.push_back(child
);
463 // Move the windows, but make sure the window is still a child of |window|
464 // (moving may cascade and cause other windows to move).
465 for (size_t i
= 0; i
< to_move
.size(); ++i
) {
466 if (std::find(window
->children().begin(), window
->children().end(),
467 to_move
[i
]) != window
->children().end()) {
468 ReparentWindow(to_move
[i
], desktop_workspace()->window(),
474 void WorkspaceManager::SelectNextWorkspace(SwitchReason reason
) {
475 DCHECK_NE(active_workspace_
, desktop_workspace());
477 Workspaces::const_iterator
workspace_i(FindWorkspace(active_workspace_
));
478 Workspaces::const_iterator
next_workspace_i(workspace_i
+ 1);
479 if (next_workspace_i
!= workspaces_
.end())
480 SetActiveWorkspace(*next_workspace_i
, reason
, base::TimeDelta());
482 SetActiveWorkspace(*(workspace_i
- 1), reason
, base::TimeDelta());
485 void WorkspaceManager::ScheduleDelete(Workspace
* workspace
) {
486 to_delete_
.insert(workspace
);
487 delete_timer_
.Stop();
488 delete_timer_
.Start(FROM_HERE
, base::TimeDelta::FromSeconds(1), this,
489 &WorkspaceManager::ProcessDeletion
);
492 void WorkspaceManager::SetUnminimizingWorkspace(Workspace
* workspace
) {
493 // The normal sequence of unminimizing a window is: Show() the window, which
494 // triggers changing the kShowStateKey to NORMAL and lastly the window is made
495 // active. This means at the time the window is unminimized we don't know if
496 // the workspace it is in is going to become active. To track this
497 // |unminimizing_workspace_| is set at the time we unminimize and a task is
498 // schedule to reset it. This way when we get the activate we know we're in
499 // the process unminimizing and can do the right animation.
500 unminimizing_workspace_
= workspace
;
501 if (unminimizing_workspace_
) {
502 MessageLoop::current()->PostTask(
504 base::Bind(&WorkspaceManager::SetUnminimizingWorkspace
,
505 clear_unminimizing_workspace_factory_
.GetWeakPtr(),
506 static_cast<Workspace
*>(NULL
)));
510 void WorkspaceManager::FadeDesktop(aura::Window
* window
,
511 base::TimeDelta duration
) {
512 if (views::corewm::WindowAnimationsDisabled(NULL
) ||
513 ui::ScopedAnimationDurationScaleMode::duration_scale_mode() ==
514 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION
)
517 base::AutoReset
<bool> reseter(&creating_fade_
, true);
518 DesktopBackgroundFadeController::Direction direction
;
519 aura::Window
* parent
= NULL
;
520 aura::Window
* stack_above
= NULL
;
521 if (active_workspace_
== desktop_workspace()) {
522 direction
= DesktopBackgroundFadeController::FADE_IN
;
523 parent
= desktop_workspace()->window();
524 stack_above
= window
;
526 direction
= DesktopBackgroundFadeController::FADE_OUT
;
527 parent
= contents_view_
;
528 stack_above
= desktop_workspace()->window();
529 DCHECK_EQ(kCrossFadeSwitchTimeMS
,
530 static_cast<int>(duration
.InMilliseconds()));
531 duration
= base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS
);
533 desktop_fade_controller_
.reset(
534 new DesktopBackgroundFadeController(
535 parent
, stack_above
, duration
, direction
));
538 void WorkspaceManager::ShowOrHideDesktopBackground(
539 aura::Window
* window
,
541 base::TimeDelta duration
,
543 WorkspaceAnimationDetails details
;
544 details
.direction
= show
? WORKSPACE_ANIMATE_UP
: WORKSPACE_ANIMATE_DOWN
;
545 details
.duration
= duration
;
548 case SWITCH_WORKSPACE_CYCLER
:
549 // The workspace cycler has already animated the desktop background's
550 // opacity. Do not do any further animation.
552 case SWITCH_MAXIMIZED_OR_RESTORED
:
553 // FadeDesktop() fades the desktop background by animating the opacity of
554 // a black window immediately above the desktop background. Set the
555 // workspace as animated to delay hiding the desktop background by
557 details
.animate
= true;
560 details
.animate
= true;
561 details
.animate_scale
= true;
562 details
.pause_time_ms
= kInitialPauseTimeMS
;
565 details
.animate
= true;
566 details
.animate_scale
= true;
570 ash::internal::ShowWorkspace(window
, details
);
572 ash::internal::HideWorkspace(window
, details
);
575 void WorkspaceManager::ShowWorkspace(
576 Workspace
* workspace
,
577 Workspace
* last_active
,
578 SwitchReason reason
) const {
579 WorkspaceAnimationDetails details
;
581 (last_active
== desktop_workspace() || reason
== SWITCH_INITIAL
) ?
582 WORKSPACE_ANIMATE_DOWN
: WORKSPACE_ANIMATE_UP
;
585 case SWITCH_WINDOW_MADE_ACTIVE
:
586 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED
:
587 case SWITCH_WINDOW_REMOVED
:
588 case SWITCH_VISIBILITY_CHANGED
:
589 case SWITCH_MINIMIZED
:
590 details
.animate
= details
.animate_scale
= true;
591 details
.animate_opacity
= last_active
== desktop_workspace();
595 details
.animate
= details
.animate_opacity
= details
.animate_scale
= true;
596 details
.pause_time_ms
= kInitialPauseTimeMS
;
599 // Remaining cases require no animation.
603 ash::internal::ShowWorkspace(workspace
->window(), details
);
606 void WorkspaceManager::HideWorkspace(
607 Workspace
* workspace
,
609 bool is_unminimizing_maximized_window
) const {
610 WorkspaceAnimationDetails details
;
611 details
.direction
= active_workspace_
== desktop_workspace() ?
612 WORKSPACE_ANIMATE_UP
: WORKSPACE_ANIMATE_DOWN
;
614 case SWITCH_WINDOW_MADE_ACTIVE
:
615 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED
:
616 details
.animate_opacity
=
617 ((active_workspace_
== desktop_workspace() ||
618 workspace
!= desktop_workspace()) &&
619 !is_unminimizing_maximized_window
);
620 details
.animate_scale
= true;
621 details
.animate
= true;
624 case SWITCH_VISIBILITY_CHANGED
:
625 // The window is most likely closing. Make the workspace visible for the
626 // duration of the switch so that the close animation is visible.
627 details
.animate
= true;
628 details
.animate_scale
= true;
631 case SWITCH_MAXIMIZED_OR_RESTORED
:
632 if (active_workspace_
->is_maximized()) {
633 // Delay the hide until the animation is done.
635 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS
);
636 details
.animate
= true;
640 // Remaining cases require no animation.
644 ash::internal::HideWorkspace(workspace
->window(), details
);
647 void WorkspaceManager::ProcessDeletion() {
648 std::set
<Workspace
*> to_delete
;
649 to_delete
.swap(to_delete_
);
650 for (std::set
<Workspace
*>::iterator i
= to_delete
.begin();
651 i
!= to_delete
.end(); ++i
) {
652 Workspace
* workspace
= *i
;
653 if (workspace
->window()->layer()->children().empty()) {
654 delete workspace
->ReleaseWindow();
657 to_delete_
.insert(workspace
);
660 if (!to_delete_
.empty()) {
661 delete_timer_
.Start(FROM_HERE
, base::TimeDelta::FromSeconds(1), this,
662 &WorkspaceManager::ProcessDeletion
);
666 void WorkspaceManager::OnWindowAddedToWorkspace(Workspace
* workspace
,
668 child
->SetProperty(kWorkspaceKey
, workspace
);
669 // Don't make changes to window parenting as the right parent was chosen
670 // by way of GetParentForNewWindow() or we explicitly moved the window
672 if (workspace
== active_workspace_
) {
673 UpdateShelfVisibility();
674 FramePainter::UpdateSoloWindowHeader(child
->GetRootWindow());
677 RearrangeVisibleWindowOnShow(child
);
680 void WorkspaceManager::OnWillRemoveWindowFromWorkspace(Workspace
* workspace
,
682 if (child
->TargetVisibility())
683 RearrangeVisibleWindowOnHideOrRemove(child
);
684 child
->ClearProperty(kWorkspaceKey
);
687 void WorkspaceManager::OnWindowRemovedFromWorkspace(Workspace
* workspace
,
689 if (workspace
->ShouldMoveToPending())
690 MoveWorkspaceToPendingOrDelete(workspace
, NULL
, SWITCH_WINDOW_REMOVED
);
693 void WorkspaceManager::OnWorkspaceChildWindowVisibilityChanged(
694 Workspace
* workspace
,
696 if (workspace
->ShouldMoveToPending()) {
697 MoveWorkspaceToPendingOrDelete(workspace
, NULL
, SWITCH_VISIBILITY_CHANGED
);
699 if (child
->TargetVisibility())
700 RearrangeVisibleWindowOnShow(child
);
702 RearrangeVisibleWindowOnHideOrRemove(child
);
703 if (workspace
== active_workspace_
) {
704 UpdateShelfVisibility();
705 FramePainter::UpdateSoloWindowHeader(child
->GetRootWindow());
710 void WorkspaceManager::OnWorkspaceWindowChildBoundsChanged(
711 Workspace
* workspace
,
713 if (workspace
== active_workspace_
)
714 UpdateShelfVisibility();
717 void WorkspaceManager::OnWorkspaceWindowShowStateChanged(
718 Workspace
* workspace
,
720 ui::WindowShowState last_show_state
,
721 ui::Layer
* old_layer
) {
722 // |child| better still be in |workspace| else things have gone wrong.
723 DCHECK_EQ(workspace
, child
->GetProperty(kWorkspaceKey
));
724 if (wm::IsWindowMinimized(child
)) {
725 if (workspace
->ShouldMoveToPending())
726 MoveWorkspaceToPendingOrDelete(workspace
, NULL
, SWITCH_MINIMIZED
);
729 // Set of cases to deal with:
730 // . More than one maximized window: move newly maximized window into
732 // . One maximized window and not in a maximized workspace: move window
733 // into own workspace.
734 // . No maximized window and not in desktop: move to desktop and further
735 // any existing windows are stacked beneath |child|.
736 const bool is_active
= wm::IsActiveWindow(child
);
737 Workspace
* new_workspace
= NULL
;
738 const int max_count
= workspace
->GetNumMaximizedWindows();
739 base::TimeDelta duration
= old_layer
&& !IsMaximized(child
) ?
740 GetCrossFadeDuration(old_layer
->bounds(), child
->bounds()) :
741 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS
);
742 if (max_count
== 0) {
743 if (workspace
!= desktop_workspace()) {
745 base::AutoReset
<bool> setter(&in_move_
, true);
746 ReparentWindow(child
, desktop_workspace()->window(), NULL
);
748 DCHECK(!is_active
|| old_layer
);
749 new_workspace
= desktop_workspace();
750 SetActiveWorkspace(new_workspace
, SWITCH_MAXIMIZED_OR_RESTORED
,
752 MoveWorkspaceToPendingOrDelete(workspace
, child
,
753 SWITCH_MAXIMIZED_OR_RESTORED
);
754 if (FindWorkspace(workspace
) == workspaces_
.end())
757 } else if ((max_count
== 1 && workspace
== desktop_workspace()) ||
759 new_workspace
= CreateWorkspace(true);
760 pending_workspaces_
.insert(new_workspace
);
761 ReparentWindow(child
, new_workspace
->window(), NULL
);
763 if (is_active
&& new_workspace
) {
764 // |old_layer| may be NULL if as part of processing
765 // WorkspaceLayoutManager::OnWindowPropertyChanged() the window is made
768 SetActiveWorkspace(new_workspace
, SWITCH_MAXIMIZED_OR_RESTORED
,
770 CrossFadeWindowBetweenWorkspaces(new_workspace
->window(), child
,
772 if (workspace
== desktop_workspace() ||
773 new_workspace
== desktop_workspace()) {
774 FadeDesktop(child
, duration
);
777 SetActiveWorkspace(new_workspace
, SWITCH_OTHER
, base::TimeDelta());
780 if (last_show_state
== ui::SHOW_STATE_MINIMIZED
)
781 SetUnminimizingWorkspace(new_workspace
? new_workspace
: workspace
);
785 UpdateShelfVisibility();
788 void WorkspaceManager::OnTrackedByWorkspaceChanged(Workspace
* workspace
,
789 aura::Window
* window
) {
790 Workspace
* new_workspace
= NULL
;
791 if (IsMaximized(window
)) {
792 if (workspace
->is_maximized() && workspace
->GetNumMaximizedWindows() == 1) {
793 // If |window| is the only window in a maximized workspace then leave
794 // it there. Additionally animate it back to the origin.
795 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
796 // All bounds changes get routed through WorkspaceLayoutManager and since
797 // the window is maximized WorkspaceLayoutManager is going to force a
798 // value. In other words, it doesn't matter what we supply to SetBounds()
800 window
->SetBounds(gfx::Rect());
803 new_workspace
= CreateWorkspace(true);
804 pending_workspaces_
.insert(new_workspace
);
805 } else if (workspace
->is_maximized()) {
806 new_workspace
= desktop_workspace();
810 // If the window is active we need to make sure the destination Workspace
811 // window is showing. Otherwise the window will be parented to a hidden window
812 // and lose activation.
813 const bool is_active
= wm::IsActiveWindow(window
);
815 new_workspace
->window()->Show();
816 ReparentWindow(window
, new_workspace
->window(), NULL
);
818 SetActiveWorkspace(new_workspace
, SWITCH_TRACKED_BY_WORKSPACE_CHANGED
,
823 } // namespace internal