athena: Add support for fling-scroll in overview mode.
[chromium-blink-merge.git] / athena / wm / window_overview_mode.cc
bloba294cdec6e66df34328c89ec08afeb6d72ca16d6
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"
7 #include <algorithm>
8 #include <functional>
9 #include <vector>
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"
27 namespace {
29 struct WindowOverviewState {
30 // The transform for when the window is at the topmost position.
31 gfx::Transform top;
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.
38 float progress;
40 scoped_ptr<wm::Shadow> shadow;
43 } // namespace
45 DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*)
46 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState,
47 kWindowOverviewState,
48 NULL)
49 namespace athena {
51 namespace {
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 {
79 public:
80 explicit StaticWindowTargeter(aura::Window* target) : target_(target) {}
81 virtual ~StaticWindowTargeter() {}
83 private:
84 // aura::WindowTargeter:
85 virtual ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
86 ui::Event* event) OVERRIDE {
87 return target_;
90 virtual ui::EventTarget* FindTargetForLocatedEvent(
91 ui::EventTarget* root,
92 ui::LocatedEvent* event) OVERRIDE {
93 return target_;
96 aura::Window* target_;
97 DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter);
100 class WindowOverviewModeImpl : public WindowOverviewMode,
101 public ui::EventHandler,
102 public ui::CompositorAnimationObserver {
103 public:
104 WindowOverviewModeImpl(aura::Window* container,
105 WindowOverviewModeDelegate* delegate)
106 : container_(container),
107 delegate_(delegate),
108 scoped_targeter_(new aura::ScopedWindowTargeter(
109 container,
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();
126 ++iter) {
127 if ((*iter)->GetProperty(kWindowOverviewState))
128 RestoreWindowState(*iter);
132 private:
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
135 // the windows.
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);
141 size_t index = 0;
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();
151 ++iter) {
152 aura::Window* window = (*iter);
153 if (!ShouldShowWindowInOverviewMode(window))
154 continue;
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);
175 index++;
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 };
183 size_t index = 0;
184 const aura::Window::Windows& windows = container_->children();
185 for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
186 iter != windows.rend();
187 ++iter) {
188 aura::Window* window = (*iter);
189 if (!window->GetProperty(kWindowOverviewState))
190 continue;
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);
204 window->Show();
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);
215 index++;
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();
237 if (!targeter)
238 return NULL;
239 aura::Window* target = static_cast<aura::Window*>(
240 targeter->FindTargetForLocatedEvent(container_, event));
241 while (target && target->parent() != container_)
242 target = target->parent();
243 return target;
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();
252 if (delta_y < 0) {
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();
257 ++iter) {
258 aura::Window* window = (*iter);
259 WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
260 if (!state)
261 continue;
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));
267 delta_y_p -= apply;
270 } else {
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 =
274 windows.rbegin();
275 delta_y_p > kEpsilon && iter != windows.rend();
276 ++iter) {
277 aura::Window* window = (*iter);
278 WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
279 if (!state)
280 continue;
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));
285 delta_y_p /= 2.f;
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);
314 // ui::EventHandler:
315 virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE {
316 if (mouse->type() == ui::ET_MOUSE_PRESSED) {
317 aura::Window* select = SelectWindowAt(mouse);
318 if (select) {
319 mouse->SetHandled();
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);
335 if (select) {
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_) {
347 fling_.reset();
348 RemoveAnimationObserver();
349 gesture->SetHandled();
353 // ui::CompositorAnimationObserver:
354 virtual void OnAnimationStep(base::TimeTicks timestamp) OVERRIDE {
355 CHECK(fling_);
356 if (fling_->start_timestamp() > timestamp)
357 return;
358 gfx::Vector2dF scroll = fling_->GetScrollAmountAtTime(timestamp);
359 if (scroll.IsZero()) {
360 fling_.reset();
361 RemoveAnimationObserver();
362 } else {
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);
375 } // namespace
377 // static
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