Index: ash/display/output_configurator_animation.cc |
diff --git a/ash/display/output_configurator_animation.cc b/ash/display/output_configurator_animation.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..270da62a16f9a40924eb083a9a5368df0e2b5695 |
--- /dev/null |
+++ b/ash/display/output_configurator_animation.cc |
@@ -0,0 +1,212 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ash/display/output_configurator_animation.h" |
+ |
+#include "ash/shell.h" |
+#include "ash/shell_window_ids.h" |
+#include "base/bind.h" |
+#include "base/stl_util.h" |
+#include "base/time.h" |
+#include "ui/aura/root_window.h" |
+#include "ui/aura/window.h" |
+#include "ui/compositor/layer.h" |
+#include "ui/compositor/layer_animation_observer.h" |
+#include "ui/compositor/layer_animation_sequence.h" |
+#include "ui/compositor/layer_animator.h" |
+#include "ui/compositor/scoped_layer_animation_settings.h" |
+ |
+namespace ash { |
+namespace internal { |
+namespace { |
+ |
+const int kFadingAnimationDurationInMS = 200; |
+const int kFadingTimeoutDurationInSeconds = 10; |
+ |
+// CallbackRunningObserver accepts multiple layer animations and |
+// runs the specified |callback| when all of the animations have finished. |
+class CallbackRunningObserver { |
+ public: |
+ CallbackRunningObserver(base::Closure callback) |
+ : completed_counter_(0), |
+ animation_aborted_(false), |
+ callback_(callback) {} |
+ |
+ void AddNewAnimator(ui::LayerAnimator* animator) { |
+ Observer* observer = new Observer(this); |
+ animator->AddObserver(observer); |
+ observer_list_.push_back(observer); |
+ } |
+ |
+ private: |
+ void OnSingleTaskCompleted() { |
+ completed_counter_++; |
+ if (completed_counter_ >= observer_list_.size()) { |
+ if (!animation_aborted_) |
+ callback_.Run(); |
+ MessageLoopForUI::current()->DeleteSoon(FROM_HERE, this); |
+ } |
+ } |
+ |
+ void OnSingleTaskAborted() { |
+ animation_aborted_ = true; |
+ OnSingleTaskCompleted(); |
+ } |
+ |
+ // The actual observer to listen each animation completion. |
+ class Observer : public ui::LayerAnimationObserver { |
+ public: |
+ Observer(CallbackRunningObserver* observer) |
+ : observer_(observer) {} |
+ |
+ protected: |
+ // ui::LayerAnimationObserver overrides: |
+ virtual void OnLayerAnimationEnded( |
+ ui::LayerAnimationSequence* sequence) OVERRIDE { |
+ StopObserving(); |
+ observer_->OnSingleTaskCompleted(); |
+ } |
+ virtual void OnLayerAnimationAborted( |
+ ui::LayerAnimationSequence* sequence) OVERRIDE { |
+ StopObserving(); |
+ observer_->OnSingleTaskAborted(); |
+ } |
+ virtual void OnLayerAnimationScheduled( |
+ ui::LayerAnimationSequence* sequence) OVERRIDE { |
+ } |
+ virtual bool RequiresNotificationWhenAnimatorDestroyed() const OVERRIDE { |
+ return true; |
+ } |
+ |
+ private: |
+ CallbackRunningObserver* observer_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Observer); |
+ }; |
+ |
+ size_t completed_counter_; |
+ bool animation_aborted_; |
+ ScopedVector<Observer> observer_list_; |
+ base::Closure callback_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CallbackRunningObserver); |
+}; |
+ |
+} // namespace |
+ |
+OutputConfiguratorAnimation::OutputConfiguratorAnimation() { |
+} |
+ |
+OutputConfiguratorAnimation::~OutputConfiguratorAnimation() { |
+ ClearHidingLayers(); |
+} |
+ |
+void OutputConfiguratorAnimation::WillDisplayModeChange( |
+ base::Closure callback) { |
+ CallbackRunningObserver* observer = new CallbackRunningObserver(callback); |
+ ClearHidingLayers(); |
+ |
+ // Make the fade-out animation for all root windows. Instead of actually |
+ // hiding the root windows, we put a black layer over a root window for |
+ // safety. These layers remain to hide root windows and will be deleted |
+ // after the animation of OnDisplayModeChanged(). |
+ Shell::RootWindowList root_windows = |
+ Shell::GetInstance()->GetAllRootWindows(); |
+ for (Shell::RootWindowList::const_iterator it = root_windows.begin(); |
+ it != root_windows.end(); ++it) { |
+ aura::RootWindow* root_window = *it; |
+ ui::Layer* hiding_layer = new ui::Layer(ui::LAYER_SOLID_COLOR); |
+ hiding_layer->SetColor(SK_ColorBLACK); |
+ hiding_layer->SetBounds(root_window->bounds()); |
+ ui::Layer* parent = ash::Shell::GetContainer( |
+ root_window, |
+ ash::internal::kShellWindowId_OverlayContainer)->layer(); |
+ parent->Add(hiding_layer); |
+ |
+ hiding_layer->SetOpacity(0.0); |
+ |
+ ui::ScopedLayerAnimationSettings settings(hiding_layer->GetAnimator()); |
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
+ kFadingAnimationDurationInMS)); |
+ observer->AddNewAnimator(hiding_layer->GetAnimator()); |
+ hiding_layer->SetOpacity(1.0f); |
+ hiding_layer->SetVisible(true); |
+ hiding_layers_[root_window] = hiding_layer; |
+ } |
+ |
+ // In case that OnDisplayModeChanged() isn't called or its animator is |
+ // canceled due to some unknown errors, we set a timer to clear these |
+ // hiding layers. |
+ timer_.reset(new base::OneShotTimer<OutputConfiguratorAnimation>()); |
+ timer_->Start(FROM_HERE, |
+ base::TimeDelta::FromSeconds(kFadingTimeoutDurationInSeconds), |
+ this, |
+ &OutputConfiguratorAnimation::ClearHidingLayers); |
+} |
+ |
+void OutputConfiguratorAnimation::OnDisplayModeChanged() { |
+ // We want to make sure clearing all of hiding layers after the animation |
+ // finished. Note that this callback can be canceled, but the cancel only |
+ // happens when the next animation is scheduled. Thus the hiding layers |
+ // should be deleted eventually. |
+ CallbackRunningObserver* observer = new CallbackRunningObserver( |
+ base::Bind(&OutputConfiguratorAnimation::ClearHidingLayers, |
+ base::Unretained(this))); |
+ |
+ // Ensure that layers are not animating. |
+ for (std::map<aura::RootWindow*, ui::Layer*>::iterator it = |
+ hiding_layers_.begin(); it != hiding_layers_.end(); ++it) { |
+ ui::LayerAnimator* animator = it->second->GetAnimator(); |
+ if (animator->is_animating()) |
+ animator->StopAnimating(); |
+ } |
+ |
+ // Schedules the fade-in effect for all root windows. Because we put the |
+ // black layers for fade-out, here we actually turn those black layers |
+ // invisible. |
+ Shell::RootWindowList root_windows = |
+ Shell::GetInstance()->GetAllRootWindows(); |
+ for (Shell::RootWindowList::const_iterator it = root_windows.begin(); |
+ it != root_windows.end(); ++it) { |
+ aura::RootWindow* root_window = *it; |
+ ui::Layer* hiding_layer = NULL; |
+ if (hiding_layers_.find(root_window) == hiding_layers_.end()) { |
+ // In case of the transition from mirroring->non-mirroring, new root |
+ // windows appear and we do not have the black layers for them. Thus |
+ // we need to create the layer and make it visible. |
+ hiding_layer = new ui::Layer(ui::LAYER_SOLID_COLOR); |
+ hiding_layer->SetColor(SK_ColorBLACK); |
+ hiding_layer->SetBounds(root_window->bounds()); |
+ ui::Layer* parent = ash::Shell::GetContainer( |
+ root_window, |
+ ash::internal::kShellWindowId_OverlayContainer)->layer(); |
+ parent->Add(hiding_layer); |
+ hiding_layer->SetOpacity(1.0f); |
+ hiding_layer->SetVisible(true); |
+ hiding_layers_[root_window] = hiding_layer; |
+ } else { |
+ hiding_layer = hiding_layers_[root_window]; |
+ } |
+ |
+ ui::ScopedLayerAnimationSettings settings(hiding_layer->GetAnimator()); |
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
+ kFadingAnimationDurationInMS)); |
+ observer->AddNewAnimator(hiding_layer->GetAnimator()); |
+ hiding_layer->SetOpacity(0.0f); |
+ hiding_layer->SetVisible(false); |
+ } |
+} |
+ |
+void OutputConfiguratorAnimation::ClearHidingLayers() { |
+ if (timer_.get()) { |
+ timer_->Stop(); |
+ timer_.reset(); |
+ } |
+ STLDeleteContainerPairSecondPointers( |
+ hiding_layers_.begin(), hiding_layers_.end()); |
+ hiding_layers_.clear(); |
+} |
+ |
+} // namespace internal |
+} // namespace ash |