1 // Copyright 2014 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 "athena/wm/window_overview_mode.h"
11 #include "base/macros.h"
12 #include "ui/aura/scoped_window_targeter.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_delegate.h"
15 #include "ui/aura/window_property.h"
16 #include "ui/aura/window_targeter.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/compositor/compositor.h"
19 #include "ui/compositor/compositor_animation_observer.h"
20 #include "ui/compositor/scoped_layer_animation_settings.h"
21 #include "ui/events/event_handler.h"
22 #include "ui/events/gestures/fling_curve.h"
23 #include "ui/gfx/frame_time.h"
24 #include "ui/gfx/transform.h"
25 #include "ui/wm/core/shadow.h"
29 struct WindowOverviewState
{
30 // The transform for when the window is at the topmost position.
33 // The transform for when the window is at the bottom-most position.
34 gfx::Transform bottom
;
36 // The current overview state of the window. 0.f means the window is at the
37 // topmost position. 1.f means the window is at the bottom-most position.
40 scoped_ptr
<wm::Shadow
> shadow
;
45 DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState
*)
46 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState
,
53 bool ShouldShowWindowInOverviewMode(aura::Window
* window
) {
54 return window
->type() == ui::wm::WINDOW_TYPE_NORMAL
;
57 // Sets the progress-state for the window in the overview mode.
58 void SetWindowProgress(aura::Window
* window
, float progress
) {
59 WindowOverviewState
* state
= window
->GetProperty(kWindowOverviewState
);
60 gfx::Transform transform
=
61 gfx::Tween::TransformValueBetween(progress
, state
->top
, state
->bottom
);
62 window
->SetTransform(transform
);
63 state
->progress
= progress
;
66 // Resets the overview-related state for |window|.
67 void RestoreWindowState(aura::Window
* window
) {
68 window
->ClearProperty(kWindowOverviewState
);
70 ui::ScopedLayerAnimationSettings
settings(window
->layer()->GetAnimator());
71 settings
.SetPreemptionStrategy(
72 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
73 settings
.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250));
74 window
->SetTransform(gfx::Transform());
77 // Always returns the same target.
78 class StaticWindowTargeter
: public aura::WindowTargeter
{
80 explicit StaticWindowTargeter(aura::Window
* target
) : target_(target
) {}
81 virtual ~StaticWindowTargeter() {}
84 // aura::WindowTargeter:
85 virtual ui::EventTarget
* FindTargetForEvent(ui::EventTarget
* root
,
86 ui::Event
* event
) OVERRIDE
{
90 virtual ui::EventTarget
* FindTargetForLocatedEvent(
91 ui::EventTarget
* root
,
92 ui::LocatedEvent
* event
) OVERRIDE
{
96 aura::Window
* target_
;
97 DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter
);
100 class WindowOverviewModeImpl
: public WindowOverviewMode
,
101 public ui::EventHandler
,
102 public ui::CompositorAnimationObserver
{
104 WindowOverviewModeImpl(aura::Window
* container
,
105 WindowOverviewModeDelegate
* delegate
)
106 : container_(container
),
108 scoped_targeter_(new aura::ScopedWindowTargeter(
110 scoped_ptr
<ui::EventTargeter
>(
111 new StaticWindowTargeter(container
)))) {
112 container_
->set_target_handler(this);
114 // Prepare the desired transforms for all the windows, and set the initial
115 // state on the windows.
116 ComputeTerminalStatesForAllWindows();
117 SetInitialWindowStates();
120 virtual ~WindowOverviewModeImpl() {
121 container_
->set_target_handler(container_
->delegate());
122 RemoveAnimationObserver();
123 const aura::Window::Windows
& windows
= container_
->children();
124 for (aura::Window::Windows::const_iterator iter
= windows
.begin();
125 iter
!= windows
.end();
127 if ((*iter
)->GetProperty(kWindowOverviewState
))
128 RestoreWindowState(*iter
);
133 // Computes the transforms for all windows in both the topmost and bottom-most
134 // positions. The transforms are set in the |kWindowOverviewState| property of
136 void ComputeTerminalStatesForAllWindows() {
137 const aura::Window::Windows
& windows
= container_
->children();
138 size_t window_count
= std::count_if(windows
.begin(), windows
.end(),
139 ShouldShowWindowInOverviewMode
);
142 const gfx::Size container_size
= container_
->bounds().size();
144 const int kGapBetweenWindowsBottom
= 10;
145 const int kGapBetweenWindowsTop
= 5;
146 const float kMinScale
= 0.6f
;
147 const float kMaxScale
= 0.95f
;
149 for (aura::Window::Windows::const_reverse_iterator iter
= windows
.rbegin();
150 iter
!= windows
.rend();
152 aura::Window
* window
= (*iter
);
153 if (!ShouldShowWindowInOverviewMode(window
))
156 gfx::Transform top_transform
;
157 int top
= (window_count
- index
- 1) * kGapBetweenWindowsTop
;
158 float x_translate
= container_size
.width() * (1 - kMinScale
) / 2.;
159 top_transform
.Translate(x_translate
, top
);
160 top_transform
.Scale(kMinScale
, kMinScale
);
162 gfx::Transform bottom_transform
;
163 int bottom
= GetScrollableHeight() - (index
* kGapBetweenWindowsBottom
);
164 x_translate
= container_size
.width() * (1 - kMaxScale
) / 2.;
165 bottom_transform
.Translate(x_translate
, bottom
- window
->bounds().y());
166 bottom_transform
.Scale(kMaxScale
, kMaxScale
);
168 WindowOverviewState
* state
= new WindowOverviewState
;
169 state
->top
= top_transform
;
170 state
->bottom
= bottom_transform
;
171 state
->progress
= 0.f
;
172 state
->shadow
= CreateShadowForWindow(window
);
173 window
->SetProperty(kWindowOverviewState
, state
);
179 // Sets the initial position for the windows for the overview mode.
180 void SetInitialWindowStates() {
181 // The initial overview state of the topmost three windows.
182 const float kInitialProgress
[] = { 0.5f
, 0.05f
, 0.01f
};
184 const aura::Window::Windows
& windows
= container_
->children();
185 for (aura::Window::Windows::const_reverse_iterator iter
= windows
.rbegin();
186 iter
!= windows
.rend();
188 aura::Window
* window
= (*iter
);
189 if (!window
->GetProperty(kWindowOverviewState
))
192 float progress
= 0.f
;
193 if (index
< arraysize(kInitialProgress
))
194 progress
= kInitialProgress
[index
];
196 scoped_refptr
<ui::LayerAnimator
> animator
=
197 window
->layer()->GetAnimator();
199 // Unset any in-progress animation.
201 ui::ScopedLayerAnimationSettings
settings(animator
);
202 settings
.SetPreemptionStrategy(
203 ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET
);
205 window
->SetTransform(gfx::Transform());
207 // Setup the animation.
209 ui::ScopedLayerAnimationSettings
settings(animator
);
210 settings
.SetPreemptionStrategy(
211 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
212 settings
.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250));
213 SetWindowProgress(window
, progress
);
219 scoped_ptr
<wm::Shadow
> CreateShadowForWindow(aura::Window
* window
) {
220 scoped_ptr
<wm::Shadow
> shadow(new wm::Shadow());
221 shadow
->Init(wm::Shadow::STYLE_ACTIVE
);
222 shadow
->SetContentBounds(gfx::Rect(window
->bounds().size()));
223 shadow
->layer()->SetVisible(true);
224 window
->layer()->Add(shadow
->layer());
225 return shadow
.Pass();
228 aura::Window
* SelectWindowAt(ui::LocatedEvent
* event
) {
229 CHECK_EQ(container_
, event
->target());
230 // Find the old targeter to find the target of the event.
231 ui::EventTarget
* window
= container_
;
232 ui::EventTargeter
* targeter
= scoped_targeter_
->old_targeter();
233 while (!targeter
&& window
->GetParentTarget()) {
234 window
= window
->GetParentTarget();
235 targeter
= window
->GetEventTargeter();
239 aura::Window
* target
= static_cast<aura::Window
*>(
240 targeter
->FindTargetForLocatedEvent(container_
, event
));
241 while (target
&& target
->parent() != container_
)
242 target
= target
->parent();
246 // Scroll the window list by |delta_y| amount. |delta_y| is negative when
247 // scrolling up; and positive when scrolling down.
248 void DoScroll(float delta_y
) {
249 const float kEpsilon
= 1e-3f
;
250 float delta_y_p
= std::abs(delta_y
) / GetScrollableHeight();
251 const aura::Window::Windows
& windows
= container_
->children();
253 // Scroll up. Start with the top-most (i.e. behind-most in terms of
254 // z-index) window, and try to scroll them up.
255 for (aura::Window::Windows::const_iterator iter
= windows
.begin();
256 delta_y_p
> kEpsilon
&& iter
!= windows
.end();
258 aura::Window
* window
= (*iter
);
259 WindowOverviewState
* state
= window
->GetProperty(kWindowOverviewState
);
262 if (state
->progress
> kEpsilon
) {
263 // It is possible to scroll |window| up. Scroll it up, and update
264 // |delta_y_p| for the next window.
265 float apply
= delta_y_p
* state
->progress
;
266 SetWindowProgress(window
, std::max(0.f
, state
->progress
- apply
* 3));
271 // Scroll down. Start with the bottom-most (i.e. front-most in terms of
272 // z-index) window, and try to scroll them down.
273 for (aura::Window::Windows::const_reverse_iterator iter
=
275 delta_y_p
> kEpsilon
&& iter
!= windows
.rend();
277 aura::Window
* window
= (*iter
);
278 WindowOverviewState
* state
= window
->GetProperty(kWindowOverviewState
);
281 if (1.f
- state
->progress
> kEpsilon
) {
282 // It is possible to scroll |window| down. Scroll it down, and update
283 // |delta_y_p| for the next window.
284 SetWindowProgress(window
, std::min(1.f
, state
->progress
+ delta_y_p
));
291 int GetScrollableHeight() const {
292 const float kScrollableFraction
= 0.65f
;
293 return container_
->bounds().height() * kScrollableFraction
;
296 void CreateFlingerFor(const ui::GestureEvent
& event
) {
297 gfx::Vector2dF
velocity(event
.details().velocity_x(),
298 event
.details().velocity_y());
299 fling_
.reset(new ui::FlingCurve(velocity
, gfx::FrameTime::Now()));
302 void AddAnimationObserver() {
303 ui::Compositor
* compositor
= container_
->GetHost()->compositor();
304 if (!compositor
->HasAnimationObserver(this))
305 compositor
->AddAnimationObserver(this);
308 void RemoveAnimationObserver() {
309 ui::Compositor
* compositor
= container_
->GetHost()->compositor();
310 if (compositor
->HasAnimationObserver(this))
311 compositor
->RemoveAnimationObserver(this);
315 virtual void OnMouseEvent(ui::MouseEvent
* mouse
) OVERRIDE
{
316 if (mouse
->type() == ui::ET_MOUSE_PRESSED
) {
317 aura::Window
* select
= SelectWindowAt(mouse
);
320 delegate_
->OnSelectWindow(select
);
322 } else if (mouse
->type() == ui::ET_MOUSEWHEEL
) {
323 DoScroll(static_cast<ui::MouseWheelEvent
*>(mouse
)->y_offset());
327 virtual void OnScrollEvent(ui::ScrollEvent
* scroll
) OVERRIDE
{
328 if (scroll
->type() == ui::ET_SCROLL
)
329 DoScroll(scroll
->y_offset());
332 virtual void OnGestureEvent(ui::GestureEvent
* gesture
) OVERRIDE
{
333 if (gesture
->type() == ui::ET_GESTURE_TAP
) {
334 aura::Window
* select
= SelectWindowAt(gesture
);
336 gesture
->SetHandled();
337 delegate_
->OnSelectWindow(select
);
339 } else if (gesture
->type() == ui::ET_GESTURE_SCROLL_UPDATE
) {
340 DoScroll(gesture
->details().scroll_y());
341 gesture
->SetHandled();
342 } else if (gesture
->type() == ui::ET_SCROLL_FLING_START
) {
343 CreateFlingerFor(*gesture
);
344 AddAnimationObserver();
345 gesture
->SetHandled();
346 } else if (gesture
->type() == ui::ET_GESTURE_TAP_DOWN
&& fling_
) {
348 RemoveAnimationObserver();
349 gesture
->SetHandled();
353 // ui::CompositorAnimationObserver:
354 virtual void OnAnimationStep(base::TimeTicks timestamp
) OVERRIDE
{
356 if (fling_
->start_timestamp() > timestamp
)
358 gfx::Vector2dF scroll
= fling_
->GetScrollAmountAtTime(timestamp
);
359 if (scroll
.IsZero()) {
361 RemoveAnimationObserver();
363 DoScroll(scroll
.y());
367 aura::Window
* container_
;
368 WindowOverviewModeDelegate
* delegate_
;
369 scoped_ptr
<aura::ScopedWindowTargeter
> scoped_targeter_
;
370 scoped_ptr
<ui::FlingCurve
> fling_
;
372 DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl
);
378 scoped_ptr
<WindowOverviewMode
> WindowOverviewMode::Create(
379 aura::Window
* container
,
380 WindowOverviewModeDelegate
* delegate
) {
381 return scoped_ptr
<WindowOverviewMode
>(
382 new WindowOverviewModeImpl(container
, delegate
));
385 } // namespace athena