Only fsync leveldb's directory when the manifest is being updated.
[chromium-blink-merge.git] / ash / wm / workspace / workspace_manager.cc
blobb4d42260eee8b8db795d587cfe34b62f1f9044a6
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"
7 #include <algorithm>
8 #include <functional>
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);
45 using aura::Window;
47 namespace ash {
48 namespace internal {
50 DEFINE_WINDOW_PROPERTY_KEY(Workspace*, kWorkspaceKey, NULL);
52 namespace {
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
63 // beneath it.
64 void ReparentWindow(Window* window,
65 Window* new_parent,
66 Window* stack_beneath) {
67 new_parent->AddChild(window);
68 if (stack_beneath)
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);
74 } // namespace
76 // Workspace -------------------------------------------------------------------
78 // LayoutManager installed on the parent window of all the Workspace window (eg
79 // |WorkspaceManager::contents_window_|).
80 class WorkspaceManager::LayoutManagerImpl : public BaseLayoutManager {
81 public:
82 explicit LayoutManagerImpl(WorkspaceManager* workspace_manager)
83 : BaseLayoutManager(
84 workspace_manager->contents_window_->GetRootWindow()),
85 workspace_manager_(workspace_manager) {
87 virtual ~LayoutManagerImpl() {}
89 // Overridden from BaseWorkspaceLayoutManager:
90 virtual void OnWindowResized() OVERRIDE {
91 for (size_t i = 0; i < window()->children().size(); ++i)
92 window()->children()[i]->SetBounds(gfx::Rect(window()->bounds().size()));
94 virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
95 // Only workspaces should be added as children.
96 DCHECK((child->id() == kShellWindowId_WorkspaceContainer) ||
97 workspace_manager_->creating_fade_);
98 child->SetBounds(gfx::Rect(window()->bounds().size()));
101 private:
102 aura::Window* window() { return workspace_manager_->contents_window_; }
104 WorkspaceManager* workspace_manager_;
106 DISALLOW_COPY_AND_ASSIGN(LayoutManagerImpl);
109 // WorkspaceManager -----------------------------------------------------------
111 WorkspaceManager::WorkspaceManager(Window* contents_window)
112 : contents_window_(contents_window),
113 active_workspace_(NULL),
114 shelf_(NULL),
115 in_move_(false),
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_window->SetEventFilter(NULL);
123 // |contents_window| takes ownership of LayoutManagerImpl.
124 contents_window->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_window_| is
137 // destroyed.
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_);
149 // static
150 bool WorkspaceManager::IsMaximized(Window* window) {
151 return IsMaximizedState(window->GetProperty(aura::client::kShowStateKey));
154 // static
155 bool WorkspaceManager::IsMaximizedState(ui::WindowShowState state) {
156 return state == ui::SHOW_STATE_MAXIMIZED ||
157 state == ui::SHOW_STATE_FULLSCREEN;
160 // static
161 bool WorkspaceManager::WillRestoreMaximized(Window* window) {
162 return wm::IsWindowMinimized(window) &&
163 IsMaximizedState(window->GetProperty(aura::client::kRestoreShowStateKey));
166 WorkspaceWindowState WorkspaceManager::GetWindowState() const {
167 if (!shelf_)
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))
178 continue;
179 ui::Layer* layer = (*i)->layer();
180 if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
181 continue;
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) {
207 shelf_ = shelf;
210 void WorkspaceManager::SetActiveWorkspaceByWindow(Window* window) {
211 Workspace* workspace = FindBy(window);
212 if (!workspace)
213 return;
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);
235 } else {
236 SetActiveWorkspace(workspace, SWITCH_WINDOW_MADE_ACTIVE,
237 base::TimeDelta());
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
253 // transient parent.
254 if (window->transient_parent() && !IsMaximized(window)) {
255 Workspace* workspace = FindBy(window->transient_parent());
256 if (workspace)
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) {
283 if (animator)
284 animator->Init(workspaces_, active_workspace_);
287 void WorkspaceManager::SetActiveWorkspaceFromCycler(Workspace* workspace) {
288 if (!workspace || workspace == active_workspace_)
289 return;
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_window_->GetRootWindow());
305 if (root_controller) {
306 aura::Window* background = root_controller->GetContainer(
307 kShellWindowId_DesktopBackgroundContainer);
308 background->Show();
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() {
321 if (shelf_)
322 shelf_->UpdateVisibilityState();
325 Workspace* WorkspaceManager::FindBy(Window* window) const {
326 while (window) {
327 Workspace* workspace = window->GetProperty(kWorkspaceKey);
328 if (workspace)
329 return workspace;
330 window = window->parent();
332 return NULL;
335 void WorkspaceManager::SetActiveWorkspace(Workspace* workspace,
336 SwitchReason reason,
337 base::TimeDelta duration) {
338 DCHECK(workspace);
339 if (active_workspace_ == workspace)
340 return;
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_)
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_window_->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_window_->StackChildAtTop(active_workspace_->window());
373 } else if (active_workspace_->is_maximized() &&
374 last_active->is_maximized() &&
375 reason != SWITCH_MAXIMIZED_FROM_MAXIMIZED_WORKSPACE) {
376 // When switching between maximized windows we need the last active
377 // workspace on top of the new, otherwise the animations won't look
378 // right. Since only one workspace is visible at a time stacking order of
379 // the workspace windows ultimately doesn't matter.
380 contents_window_->StackChildAtTop(last_active->window());
383 UpdateShelfVisibility();
385 // NOTE: duration supplied to this method is only used for desktop background.
386 HideWorkspace(last_active, reason, is_unminimizing_maximized_window);
387 ShowWorkspace(workspace, last_active, reason);
389 RootWindowController* root_controller = GetRootWindowController(
390 contents_window_->GetRootWindow());
391 if (root_controller) {
392 aura::Window* background = root_controller->GetContainer(
393 kShellWindowId_DesktopBackgroundContainer);
394 if (last_active == desktop_workspace()) {
395 ShowOrHideDesktopBackground(background, reason, duration, false);
396 } else if (active_workspace_ == desktop_workspace() && !app_terminating_) {
397 ShowOrHideDesktopBackground(background, reason, duration, true);
401 // Showing or hiding a workspace may change the "solo window" status of
402 // a window, requiring the header to be updated.
403 FramePainter::UpdateSoloWindowHeader(contents_window_->GetRootWindow());
406 WorkspaceManager::Workspaces::iterator
407 WorkspaceManager::FindWorkspace(Workspace* workspace) {
408 return std::find(workspaces_.begin(), workspaces_.end(), workspace);
411 Workspace* WorkspaceManager::CreateWorkspace(bool maximized) {
412 return new Workspace(this, contents_window_, maximized);
415 void WorkspaceManager::MoveWorkspaceToPendingOrDelete(
416 Workspace* workspace,
417 Window* stack_beneath,
418 SwitchReason reason) {
419 // We're all ready moving windows.
420 if (in_move_)
421 return;
423 DCHECK_NE(desktop_workspace(), workspace);
425 // The user may have closed or minimized a window via accelerator keys while
426 // cycling through workspaces.
427 if (workspace_cycler_)
428 workspace_cycler_->AbortCycling();
430 if (workspace == active_workspace_)
431 SelectNextWorkspace(reason);
433 base::AutoReset<bool> setter(&in_move_, true);
435 MoveChildrenToDesktop(workspace->window(), stack_beneath);
438 Workspaces::iterator workspace_i(FindWorkspace(workspace));
439 if (workspace_i != workspaces_.end())
440 workspaces_.erase(workspace_i);
443 if (workspace->window()->children().empty()) {
444 if (workspace == unminimizing_workspace_)
445 unminimizing_workspace_ = NULL;
446 pending_workspaces_.erase(workspace);
447 ScheduleDelete(workspace);
448 } else {
449 pending_workspaces_.insert(workspace);
453 void WorkspaceManager::MoveChildrenToDesktop(aura::Window* window,
454 aura::Window* stack_beneath) {
455 // Build the list of windows to move. Exclude maximized/fullscreen and windows
456 // with transient parents.
457 Window::Windows to_move;
458 for (size_t i = 0; i < window->children().size(); ++i) {
459 Window* child = window->children()[i];
460 if (!child->transient_parent() && !IsMaximized(child) &&
461 !WillRestoreMaximized(child)) {
462 to_move.push_back(child);
465 // Move the windows, but make sure the window is still a child of |window|
466 // (moving may cascade and cause other windows to move).
467 for (size_t i = 0; i < to_move.size(); ++i) {
468 if (std::find(window->children().begin(), window->children().end(),
469 to_move[i]) != window->children().end()) {
470 ReparentWindow(to_move[i], desktop_workspace()->window(),
471 stack_beneath);
476 void WorkspaceManager::SelectNextWorkspace(SwitchReason reason) {
477 DCHECK_NE(active_workspace_, desktop_workspace());
479 Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_));
480 Workspaces::const_iterator next_workspace_i(workspace_i + 1);
481 if (next_workspace_i != workspaces_.end())
482 SetActiveWorkspace(*next_workspace_i, reason, base::TimeDelta());
483 else
484 SetActiveWorkspace(*(workspace_i - 1), reason, base::TimeDelta());
487 void WorkspaceManager::ScheduleDelete(Workspace* workspace) {
488 to_delete_.insert(workspace);
489 delete_timer_.Stop();
490 delete_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
491 &WorkspaceManager::ProcessDeletion);
494 void WorkspaceManager::SetUnminimizingWorkspace(Workspace* workspace) {
495 // The normal sequence of unminimizing a window is: Show() the window, which
496 // triggers changing the kShowStateKey to NORMAL and lastly the window is made
497 // active. This means at the time the window is unminimized we don't know if
498 // the workspace it is in is going to become active. To track this
499 // |unminimizing_workspace_| is set at the time we unminimize and a task is
500 // schedule to reset it. This way when we get the activate we know we're in
501 // the process unminimizing and can do the right animation.
502 unminimizing_workspace_ = workspace;
503 if (unminimizing_workspace_) {
504 base::MessageLoop::current()->PostTask(
505 FROM_HERE,
506 base::Bind(&WorkspaceManager::SetUnminimizingWorkspace,
507 clear_unminimizing_workspace_factory_.GetWeakPtr(),
508 static_cast<Workspace*>(NULL)));
512 void WorkspaceManager::FadeDesktop(aura::Window* window,
513 base::TimeDelta duration) {
514 if (views::corewm::WindowAnimationsDisabled(NULL) ||
515 ui::ScopedAnimationDurationScaleMode::duration_scale_mode() ==
516 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)
517 return;
519 base::AutoReset<bool> reseter(&creating_fade_, true);
520 DesktopBackgroundFadeController::Direction direction;
521 aura::Window* parent = NULL;
522 aura::Window* stack_above = NULL;
523 if (active_workspace_ == desktop_workspace()) {
524 direction = DesktopBackgroundFadeController::FADE_IN;
525 parent = desktop_workspace()->window();
526 stack_above = window;
527 } else {
528 direction = DesktopBackgroundFadeController::FADE_OUT;
529 parent = contents_window_;
530 stack_above = desktop_workspace()->window();
531 DCHECK_EQ(kCrossFadeSwitchTimeMS,
532 static_cast<int>(duration.InMilliseconds()));
533 duration = base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS);
535 desktop_fade_controller_.reset(
536 new DesktopBackgroundFadeController(
537 parent, stack_above, duration, direction));
540 void WorkspaceManager::ShowOrHideDesktopBackground(
541 aura::Window* window,
542 SwitchReason reason,
543 base::TimeDelta duration,
544 bool show) const {
545 WorkspaceAnimationDetails details;
546 details.direction = show ? WORKSPACE_ANIMATE_UP : WORKSPACE_ANIMATE_DOWN;
547 details.duration = duration;
549 switch (reason) {
550 case SWITCH_WORKSPACE_CYCLER:
551 // The workspace cycler has already animated the desktop background's
552 // opacity. Do not do any further animation.
553 break;
554 case SWITCH_MAXIMIZED_FROM_MAXIMIZED_WORKSPACE:
555 case SWITCH_MAXIMIZED_OR_RESTORED:
556 // FadeDesktop() fades the desktop background by animating the opacity of
557 // a black window immediately above the desktop background. Set the
558 // workspace as animated to delay hiding the desktop background by
559 // |duration|.
560 details.animate = true;
561 break;
562 case SWITCH_INITIAL:
563 details.animate = true;
564 details.animate_scale = true;
565 details.pause_time_ms = kInitialPauseTimeMS;
566 break;
567 default:
568 details.animate = true;
569 details.animate_scale = true;
570 break;
572 if (show)
573 ash::internal::ShowWorkspace(window, details);
574 else
575 ash::internal::HideWorkspace(window, details);
578 void WorkspaceManager::ShowWorkspace(
579 Workspace* workspace,
580 Workspace* last_active,
581 SwitchReason reason) const {
582 WorkspaceAnimationDetails details;
583 details.direction =
584 (last_active == desktop_workspace() || reason == SWITCH_INITIAL) ?
585 WORKSPACE_ANIMATE_DOWN : WORKSPACE_ANIMATE_UP;
587 switch (reason) {
588 case SWITCH_WINDOW_MADE_ACTIVE:
589 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED:
590 case SWITCH_WINDOW_REMOVED:
591 case SWITCH_VISIBILITY_CHANGED:
592 case SWITCH_MINIMIZED:
593 details.animate = details.animate_scale = true;
594 details.animate_opacity = last_active == desktop_workspace();
595 break;
597 case SWITCH_INITIAL:
598 details.animate = details.animate_opacity = details.animate_scale = true;
599 details.pause_time_ms = kInitialPauseTimeMS;
600 break;
602 // Remaining cases require no animation.
603 default:
604 break;
606 ash::internal::ShowWorkspace(workspace->window(), details);
609 void WorkspaceManager::HideWorkspace(
610 Workspace* workspace,
611 SwitchReason reason,
612 bool is_unminimizing_maximized_window) const {
613 WorkspaceAnimationDetails details;
614 details.direction = active_workspace_ == desktop_workspace() ?
615 WORKSPACE_ANIMATE_UP : WORKSPACE_ANIMATE_DOWN;
616 switch (reason) {
617 case SWITCH_WINDOW_MADE_ACTIVE:
618 case SWITCH_TRACKED_BY_WORKSPACE_CHANGED:
619 details.animate_opacity =
620 ((active_workspace_ == desktop_workspace() ||
621 workspace != desktop_workspace()) &&
622 !is_unminimizing_maximized_window);
623 details.animate_scale = true;
624 details.animate = true;
625 break;
627 case SWITCH_VISIBILITY_CHANGED:
628 // The window is most likely closing. Make the workspace visible for the
629 // duration of the switch so that the close animation is visible.
630 details.animate = true;
631 details.animate_scale = true;
632 break;
634 case SWITCH_MAXIMIZED_FROM_MAXIMIZED_WORKSPACE:
635 case SWITCH_MAXIMIZED_OR_RESTORED:
636 if (active_workspace_->is_maximized()) {
637 // Delay the hide until the animation is done.
638 details.duration =
639 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS);
640 details.animate = true;
642 break;
644 // Remaining cases require no animation.
645 default:
646 break;
648 ash::internal::HideWorkspace(workspace->window(), details);
651 void WorkspaceManager::ProcessDeletion() {
652 std::set<Workspace*> to_delete;
653 to_delete.swap(to_delete_);
654 for (std::set<Workspace*>::iterator i = to_delete.begin();
655 i != to_delete.end(); ++i) {
656 Workspace* workspace = *i;
657 if (workspace->window()->layer()->children().empty()) {
658 delete workspace->ReleaseWindow();
659 delete workspace;
660 } else {
661 to_delete_.insert(workspace);
664 if (!to_delete_.empty()) {
665 delete_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
666 &WorkspaceManager::ProcessDeletion);
670 void WorkspaceManager::OnWindowAddedToWorkspace(Workspace* workspace,
671 Window* child) {
672 child->SetProperty(kWorkspaceKey, workspace);
673 // Don't make changes to window parenting as the right parent was chosen
674 // by way of GetParentForNewWindow() or we explicitly moved the window
675 // to the workspace.
676 if (workspace == active_workspace_) {
677 UpdateShelfVisibility();
678 FramePainter::UpdateSoloWindowHeader(child->GetRootWindow());
681 RearrangeVisibleWindowOnShow(child);
684 void WorkspaceManager::OnWillRemoveWindowFromWorkspace(Workspace* workspace,
685 Window* child) {
686 if (child->TargetVisibility())
687 RearrangeVisibleWindowOnHideOrRemove(child);
688 child->ClearProperty(kWorkspaceKey);
691 void WorkspaceManager::OnWindowRemovedFromWorkspace(Workspace* workspace,
692 Window* child) {
693 if (workspace->ShouldMoveToPending())
694 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_WINDOW_REMOVED);
695 UpdateShelfVisibility();
698 void WorkspaceManager::OnWorkspaceChildWindowVisibilityChanged(
699 Workspace* workspace,
700 Window* child) {
701 if (workspace->ShouldMoveToPending()) {
702 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_VISIBILITY_CHANGED);
703 } else {
704 if (child->TargetVisibility())
705 RearrangeVisibleWindowOnShow(child);
706 else
707 RearrangeVisibleWindowOnHideOrRemove(child);
708 if (workspace == active_workspace_) {
709 UpdateShelfVisibility();
710 FramePainter::UpdateSoloWindowHeader(child->GetRootWindow());
715 void WorkspaceManager::OnWorkspaceWindowChildBoundsChanged(
716 Workspace* workspace,
717 Window* child) {
718 if (workspace == active_workspace_)
719 UpdateShelfVisibility();
722 void WorkspaceManager::OnWorkspaceWindowShowStateChanged(
723 Workspace* workspace,
724 Window* child,
725 ui::WindowShowState last_show_state,
726 ui::Layer* old_layer) {
727 // |child| better still be in |workspace| else things have gone wrong.
728 DCHECK_EQ(workspace, child->GetProperty(kWorkspaceKey));
729 if (wm::IsWindowMinimized(child)) {
730 if (workspace->ShouldMoveToPending())
731 MoveWorkspaceToPendingOrDelete(workspace, NULL, SWITCH_MINIMIZED);
732 DCHECK(!old_layer);
733 } else {
734 // Set of cases to deal with:
735 // . More than one maximized window: move newly maximized window into
736 // own workspace.
737 // . One maximized window and not in a maximized workspace: move window
738 // into own workspace.
739 // . No maximized window and not in desktop: move to desktop and further
740 // any existing windows are stacked beneath |child|.
741 const bool is_active = wm::IsActiveWindow(child);
742 Workspace* new_workspace = NULL;
743 const int max_count = workspace->GetNumMaximizedWindows();
744 base::TimeDelta duration = old_layer && !IsMaximized(child) ?
745 GetCrossFadeDuration(old_layer->bounds(), child->bounds()) :
746 base::TimeDelta::FromMilliseconds(kCrossFadeSwitchTimeMS);
747 if (max_count == 0) {
748 if (workspace != desktop_workspace()) {
750 base::AutoReset<bool> setter(&in_move_, true);
751 ReparentWindow(child, desktop_workspace()->window(), NULL);
753 DCHECK(!is_active || old_layer);
754 new_workspace = desktop_workspace();
755 SetActiveWorkspace(new_workspace, SWITCH_MAXIMIZED_OR_RESTORED,
756 duration);
757 MoveWorkspaceToPendingOrDelete(workspace, child,
758 SWITCH_MAXIMIZED_OR_RESTORED);
759 if (FindWorkspace(workspace) == workspaces_.end())
760 workspace = NULL;
762 } else if ((max_count == 1 && workspace == desktop_workspace()) ||
763 max_count > 1) {
764 new_workspace = CreateWorkspace(true);
765 pending_workspaces_.insert(new_workspace);
766 ReparentWindow(child, new_workspace->window(), NULL);
768 if (is_active && new_workspace) {
769 // |old_layer| may be NULL if as part of processing
770 // WorkspaceLayoutManager::OnWindowPropertyChanged() the window is made
771 // active.
772 if (old_layer) {
773 SetActiveWorkspace(new_workspace,
774 max_count >= 2 ?
775 SWITCH_MAXIMIZED_FROM_MAXIMIZED_WORKSPACE :
776 SWITCH_MAXIMIZED_OR_RESTORED,
777 duration);
778 CrossFadeWindowBetweenWorkspaces(new_workspace->window(), child,
779 old_layer);
780 if (workspace == desktop_workspace() ||
781 new_workspace == desktop_workspace()) {
782 FadeDesktop(child, duration);
784 } else {
785 SetActiveWorkspace(new_workspace, SWITCH_OTHER, base::TimeDelta());
787 } else {
788 if (last_show_state == ui::SHOW_STATE_MINIMIZED)
789 SetUnminimizingWorkspace(new_workspace ? new_workspace : workspace);
790 DCHECK(!old_layer);
793 UpdateShelfVisibility();
796 void WorkspaceManager::OnTrackedByWorkspaceChanged(Workspace* workspace,
797 aura::Window* window) {
798 Workspace* new_workspace = NULL;
799 if (IsMaximized(window)) {
800 if (workspace->is_maximized() && workspace->GetNumMaximizedWindows() == 1) {
801 // If |window| is the only window in a maximized workspace then leave
802 // it there. Additionally animate it back to the origin.
803 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
804 // All bounds changes get routed through WorkspaceLayoutManager and since
805 // the window is maximized WorkspaceLayoutManager is going to force a
806 // value. In other words, it doesn't matter what we supply to SetBounds()
807 // here.
808 window->SetBounds(gfx::Rect());
809 return;
811 new_workspace = CreateWorkspace(true);
812 pending_workspaces_.insert(new_workspace);
813 } else if (workspace->is_maximized()) {
814 new_workspace = desktop_workspace();
815 } else {
816 return;
818 // If the window is active we need to make sure the destination Workspace
819 // window is showing. Otherwise the window will be parented to a hidden window
820 // and lose activation.
821 const bool is_active = wm::IsActiveWindow(window);
822 if (is_active)
823 new_workspace->window()->Show();
824 ReparentWindow(window, new_workspace->window(), NULL);
825 if (is_active) {
826 SetActiveWorkspace(new_workspace, SWITCH_TRACKED_BY_WORKSPACE_CHANGED,
827 base::TimeDelta());
831 } // namespace internal
832 } // namespace ash