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/display/output_configurator_animation.h"
7 #include "ash/display/display_error_dialog.h"
9 #include "ash/shell_window_ids.h"
10 #include "base/bind.h"
11 #include "base/stl_util.h"
12 #include "base/time.h"
13 #include "ui/aura/root_window.h"
14 #include "ui/aura/window.h"
15 #include "ui/compositor/layer.h"
16 #include "ui/compositor/layer_animation_observer.h"
17 #include "ui/compositor/layer_animation_sequence.h"
18 #include "ui/compositor/layer_animator.h"
19 #include "ui/compositor/scoped_layer_animation_settings.h"
25 const int kFadingAnimationDurationInMS
= 200;
26 const int kFadingTimeoutDurationInSeconds
= 10;
28 // CallbackRunningObserver accepts multiple layer animations and
29 // runs the specified |callback| when all of the animations have finished.
30 class CallbackRunningObserver
{
32 CallbackRunningObserver(base::Closure callback
)
33 : completed_counter_(0),
34 animation_aborted_(false),
35 callback_(callback
) {}
37 void AddNewAnimator(ui::LayerAnimator
* animator
) {
38 Observer
* observer
= new Observer(animator
, this);
39 animator
->AddObserver(observer
);
40 observer_list_
.push_back(observer
);
44 void OnSingleTaskCompleted() {
46 if (completed_counter_
>= observer_list_
.size()) {
47 base::MessageLoopForUI::current()->DeleteSoon(FROM_HERE
, this);
48 if (!animation_aborted_
)
49 base::MessageLoopForUI::current()->PostTask(FROM_HERE
, callback_
);
53 void OnSingleTaskAborted() {
54 animation_aborted_
= true;
55 OnSingleTaskCompleted();
58 // The actual observer to listen each animation completion.
59 class Observer
: public ui::LayerAnimationObserver
{
61 Observer(ui::LayerAnimator
* animator
,
62 CallbackRunningObserver
* observer
)
63 : animator_(animator
),
64 observer_(observer
) {}
67 // ui::LayerAnimationObserver overrides:
68 virtual void OnLayerAnimationEnded(
69 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{
70 animator_
->RemoveObserver(this);
71 observer_
->OnSingleTaskCompleted();
73 virtual void OnLayerAnimationAborted(
74 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{
75 animator_
->RemoveObserver(this);
76 observer_
->OnSingleTaskAborted();
78 virtual void OnLayerAnimationScheduled(
79 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{
81 virtual bool RequiresNotificationWhenAnimatorDestroyed() const OVERRIDE
{
86 ui::LayerAnimator
* animator_
;
87 CallbackRunningObserver
* observer_
;
89 DISALLOW_COPY_AND_ASSIGN(Observer
);
92 size_t completed_counter_
;
93 bool animation_aborted_
;
94 ScopedVector
<Observer
> observer_list_
;
95 base::Closure callback_
;
97 DISALLOW_COPY_AND_ASSIGN(CallbackRunningObserver
);
102 OutputConfiguratorAnimation::OutputConfiguratorAnimation() {
105 OutputConfiguratorAnimation::~OutputConfiguratorAnimation() {
109 void OutputConfiguratorAnimation::StartFadeOutAnimation(
110 base::Closure callback
) {
111 CallbackRunningObserver
* observer
= new CallbackRunningObserver(callback
);
114 // Make the fade-out animation for all root windows. Instead of actually
115 // hiding the root windows, we put a black layer over a root window for
116 // safety. These layers remain to hide root windows and will be deleted
117 // after the animation of OnDisplayModeChanged().
118 Shell::RootWindowList root_windows
=
119 Shell::GetInstance()->GetAllRootWindows();
120 for (Shell::RootWindowList::const_iterator it
= root_windows
.begin();
121 it
!= root_windows
.end(); ++it
) {
122 aura::RootWindow
* root_window
= *it
;
123 ui::Layer
* hiding_layer
= new ui::Layer(ui::LAYER_SOLID_COLOR
);
124 hiding_layer
->SetColor(SK_ColorBLACK
);
125 hiding_layer
->SetBounds(root_window
->bounds());
126 ui::Layer
* parent
= ash::Shell::GetContainer(
128 ash::internal::kShellWindowId_OverlayContainer
)->layer();
129 parent
->Add(hiding_layer
);
131 hiding_layer
->SetOpacity(0.0);
133 ui::ScopedLayerAnimationSettings
settings(hiding_layer
->GetAnimator());
134 settings
.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
135 kFadingAnimationDurationInMS
));
136 observer
->AddNewAnimator(hiding_layer
->GetAnimator());
137 hiding_layer
->SetOpacity(1.0f
);
138 hiding_layer
->SetVisible(true);
139 hiding_layers_
[root_window
] = hiding_layer
;
142 // In case that OnDisplayModeChanged() isn't called or its animator is
143 // canceled due to some unknown errors, we set a timer to clear these
145 timer_
.reset(new base::OneShotTimer
<OutputConfiguratorAnimation
>());
146 timer_
->Start(FROM_HERE
,
147 base::TimeDelta::FromSeconds(kFadingTimeoutDurationInSeconds
),
149 &OutputConfiguratorAnimation::ClearHidingLayers
);
152 void OutputConfiguratorAnimation::StartFadeInAnimation() {
153 // We want to make sure clearing all of hiding layers after the animation
154 // finished. Note that this callback can be canceled, but the cancel only
155 // happens when the next animation is scheduled. Thus the hiding layers
156 // should be deleted eventually.
157 CallbackRunningObserver
* observer
= new CallbackRunningObserver(
158 base::Bind(&OutputConfiguratorAnimation::ClearHidingLayers
,
159 base::Unretained(this)));
161 // Ensure that layers are not animating.
162 for (std::map
<aura::RootWindow
*, ui::Layer
*>::iterator it
=
163 hiding_layers_
.begin(); it
!= hiding_layers_
.end(); ++it
) {
164 ui::LayerAnimator
* animator
= it
->second
->GetAnimator();
165 if (animator
->is_animating())
166 animator
->StopAnimating();
169 // Schedules the fade-in effect for all root windows. Because we put the
170 // black layers for fade-out, here we actually turn those black layers
172 Shell::RootWindowList root_windows
=
173 Shell::GetInstance()->GetAllRootWindows();
174 for (Shell::RootWindowList::const_iterator it
= root_windows
.begin();
175 it
!= root_windows
.end(); ++it
) {
176 aura::RootWindow
* root_window
= *it
;
177 ui::Layer
* hiding_layer
= NULL
;
178 if (hiding_layers_
.find(root_window
) == hiding_layers_
.end()) {
179 // In case of the transition from mirroring->non-mirroring, new root
180 // windows appear and we do not have the black layers for them. Thus
181 // we need to create the layer and make it visible.
182 hiding_layer
= new ui::Layer(ui::LAYER_SOLID_COLOR
);
183 hiding_layer
->SetColor(SK_ColorBLACK
);
184 hiding_layer
->SetBounds(root_window
->bounds());
185 ui::Layer
* parent
= ash::Shell::GetContainer(
187 ash::internal::kShellWindowId_OverlayContainer
)->layer();
188 parent
->Add(hiding_layer
);
189 hiding_layer
->SetOpacity(1.0f
);
190 hiding_layer
->SetVisible(true);
191 hiding_layers_
[root_window
] = hiding_layer
;
193 hiding_layer
= hiding_layers_
[root_window
];
194 if (hiding_layer
->bounds() != root_window
->bounds())
195 hiding_layer
->SetBounds(root_window
->bounds());
198 ui::ScopedLayerAnimationSettings
settings(hiding_layer
->GetAnimator());
199 settings
.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
200 kFadingAnimationDurationInMS
));
201 observer
->AddNewAnimator(hiding_layer
->GetAnimator());
202 hiding_layer
->SetOpacity(0.0f
);
203 hiding_layer
->SetVisible(false);
207 void OutputConfiguratorAnimation::OnDisplayModeChanged() {
208 if (!hiding_layers_
.empty())
209 StartFadeInAnimation();
212 void OutputConfiguratorAnimation::OnDisplayModeChangeFailed(
213 chromeos::OutputState failed_new_state
) {
214 if (!hiding_layers_
.empty())
215 StartFadeInAnimation();
218 void OutputConfiguratorAnimation::ClearHidingLayers() {
223 STLDeleteContainerPairSecondPointers(
224 hiding_layers_
.begin(), hiding_layers_
.end());
225 hiding_layers_
.clear();
228 } // namespace internal