Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / athena / wm / window_overview_mode.cc
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.
4
5 #include "athena/wm/window_overview_mode.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <vector>
10
11 #include "athena/wm/overview_toolbar.h"
12 #include "athena/wm/public/window_list_provider.h"
13 #include "athena/wm/public/window_list_provider_observer.h"
14 #include "athena/wm/split_view_controller.h"
15 #include "base/bind.h"
16 #include "base/macros.h"
17 #include "ui/aura/scoped_window_targeter.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_delegate.h"
20 #include "ui/aura/window_property.h"
21 #include "ui/aura/window_targeter.h"
22 #include "ui/aura/window_tree_host.h"
23 #include "ui/compositor/closure_animation_observer.h"
24 #include "ui/compositor/compositor.h"
25 #include "ui/compositor/compositor_animation_observer.h"
26 #include "ui/compositor/scoped_layer_animation_settings.h"
27 #include "ui/events/event_handler.h"
28 #include "ui/events/gestures/fling_curve.h"
29 #include "ui/gfx/frame_time.h"
30 #include "ui/gfx/transform.h"
31 #include "ui/wm/core/shadow_types.h"
32 #include "ui/wm/core/window_animations.h"
33 #include "ui/wm/core/window_util.h"
34
35 namespace {
36
37 const float kOverviewDefaultScale = 0.75f;
38
39 struct WindowOverviewState {
40   // The current overview state of the window. 0.f means the window is at the
41   // topmost position. 1.f means the window is at the bottom-most position.
42   float progress;
43
44   // The top-most and bottom-most vertical position of the window in overview
45   // mode.
46   float max_y;
47   float min_y;
48
49   // |split| is set if this window is one of the two split windows in split-view
50   // mode.
51   bool split;
52 };
53
54 }  // namespace
55
56 DECLARE_WINDOW_PROPERTY_TYPE(WindowOverviewState*)
57 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowOverviewState,
58                                  kWindowOverviewState,
59                                  NULL)
60 namespace athena {
61
62 namespace {
63
64 gfx::Transform GetTransformForSplitWindow(aura::Window* window, float scale) {
65   const float kScrollWindowPositionInOverview = 0.65f;
66   int x_translate = window->bounds().width() * (1 - scale) / 2;
67   gfx::Transform transform;
68   transform.Translate(
69       x_translate, window->bounds().height() * kScrollWindowPositionInOverview);
70   transform.Scale(scale, scale);
71   return transform;
72 }
73
74 // Gets the transform for the window in its current state.
75 gfx::Transform GetTransformForState(aura::Window* window,
76                                     WindowOverviewState* state) {
77   if (state->split)
78     return GetTransformForSplitWindow(window, kOverviewDefaultScale);
79
80   const float kProgressToStartShrinking = 0.07;
81   const float kOverviewScale = 0.75f;
82   float scale = kOverviewScale;
83   if (state->progress < kProgressToStartShrinking) {
84     const float kShrunkMinimumScale = 0.7f;
85     scale = gfx::Tween::FloatValueBetween(
86         state->progress / kProgressToStartShrinking,
87         kShrunkMinimumScale,
88         kOverviewScale);
89   }
90   int container_width = window->parent()->bounds().width();
91   int window_width = window->bounds().width();
92   int window_x = window->bounds().x();
93   float x_translate = (container_width - (window_width * scale)) / 2 - window_x;
94   float y_translate = gfx::Tween::FloatValueBetween(
95       state->progress, state->min_y, state->max_y);
96   gfx::Transform transform;
97   transform.Translate(x_translate, y_translate);
98   transform.Scale(scale, scale);
99   return transform;
100 }
101
102 // Sets the progress-state for the window in the overview mode.
103 void SetWindowProgress(aura::Window* window, float progress) {
104   WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
105   state->progress = progress;
106
107   gfx::Transform transform = GetTransformForState(window, state);
108   window->SetTransform(transform);
109 }
110
111 void HideWindowIfNotVisible(aura::Window* window,
112                             SplitViewController* split_view_controller) {
113   bool should_hide = true;
114   if (split_view_controller->IsSplitViewModeActive()) {
115     should_hide = window != split_view_controller->left_window() &&
116                   window != split_view_controller->right_window();
117   } else {
118     should_hide = !wm::IsActiveWindow(window);
119   }
120   if (should_hide)
121     window->Hide();
122 }
123
124 // Resets the overview-related state for |window|.
125 void RestoreWindowState(aura::Window* window,
126                         SplitViewController* split_view_controller) {
127   window->ClearProperty(kWindowOverviewState);
128
129   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
130   settings.SetPreemptionStrategy(
131       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
132   settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250));
133
134   settings.AddObserver(new ui::ClosureAnimationObserver(
135       base::Bind(&HideWindowIfNotVisible, window, split_view_controller)));
136
137   window->SetTransform(gfx::Transform());
138
139   // Reset the window opacity in case the user is dragging a window.
140   window->layer()->SetOpacity(1.0f);
141
142   wm::SetShadowType(window, wm::SHADOW_TYPE_NONE);
143 }
144
145 gfx::RectF GetTransformedBounds(aura::Window* window) {
146   gfx::Transform transform;
147   gfx::RectF bounds = window->bounds();
148   transform.Translate(bounds.x(), bounds.y());
149   transform.PreconcatTransform(window->layer()->transform());
150   transform.Translate(-bounds.x(), -bounds.y());
151   transform.TransformRect(&bounds);
152   return bounds;
153 }
154
155 void TransformSplitWindowScale(aura::Window* window, float scale) {
156   gfx::Transform transform = window->layer()->GetTargetTransform();
157   if (transform.Scale2d() == gfx::Vector2dF(scale, scale))
158     return;
159   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
160   window->SetTransform(GetTransformForSplitWindow(window, scale));
161 }
162
163 void AnimateWindowTo(aura::Window* animate_window,
164                      aura::Window* target_window) {
165   ui::ScopedLayerAnimationSettings settings(
166       animate_window->layer()->GetAnimator());
167   settings.SetPreemptionStrategy(
168       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
169   WindowOverviewState* target_state =
170       target_window->GetProperty(kWindowOverviewState);
171   SetWindowProgress(animate_window, target_state->progress);
172 }
173
174 // Always returns the same target.
175 class StaticWindowTargeter : public aura::WindowTargeter {
176  public:
177   explicit StaticWindowTargeter(aura::Window* target) : target_(target) {}
178   virtual ~StaticWindowTargeter() {}
179
180  private:
181   // aura::WindowTargeter:
182   virtual ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
183                                               ui::Event* event) OVERRIDE {
184     return target_;
185   }
186
187   virtual ui::EventTarget* FindTargetForLocatedEvent(
188       ui::EventTarget* root,
189       ui::LocatedEvent* event) OVERRIDE {
190     return target_;
191   }
192
193   aura::Window* target_;
194   DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter);
195 };
196
197 class WindowOverviewModeImpl : public WindowOverviewMode,
198                                public ui::EventHandler,
199                                public ui::CompositorAnimationObserver,
200                                public WindowListProviderObserver {
201  public:
202   WindowOverviewModeImpl(aura::Window* container,
203                          WindowListProvider* window_list_provider,
204                          SplitViewController* split_view_controller,
205                          WindowOverviewModeDelegate* delegate)
206       : container_(container),
207         window_list_provider_(window_list_provider),
208         split_view_controller_(split_view_controller),
209         delegate_(delegate),
210         scoped_targeter_(new aura::ScopedWindowTargeter(
211             container,
212             scoped_ptr<ui::EventTargeter>(
213                 new StaticWindowTargeter(container)))),
214         dragged_window_(NULL) {
215     CHECK(delegate_);
216     container_->set_target_handler(this);
217
218     // Prepare the desired transforms for all the windows, and set the initial
219     // state on the windows.
220     ComputeTerminalStatesForAllWindows();
221     SetInitialWindowStates();
222
223     window_list_provider_->AddObserver(this);
224   }
225
226   virtual ~WindowOverviewModeImpl() {
227     window_list_provider_->RemoveObserver(this);
228     container_->set_target_handler(container_->delegate());
229     RemoveAnimationObserver();
230     const aura::Window::Windows& windows =
231         window_list_provider_->GetWindowList();
232     if (windows.empty())
233       return;
234     std::for_each(windows.begin(),
235                   windows.end(),
236                   std::bind2nd(std::ptr_fun(&RestoreWindowState),
237                                split_view_controller_));
238   }
239
240  private:
241   // Computes the transforms for all windows in both the topmost and bottom-most
242   // positions. The transforms are set in the |kWindowOverviewState| property of
243   // the windows.
244   void ComputeTerminalStatesForAllWindows() {
245     size_t index = 0;
246
247     const aura::Window::Windows& windows =
248         window_list_provider_->GetWindowList();
249     for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
250          iter != windows.rend();
251          ++iter, ++index) {
252       aura::Window* window = (*iter);
253       wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE);
254
255       WindowOverviewState* state = new WindowOverviewState;
256       window->SetProperty(kWindowOverviewState, state);
257       if (split_view_controller_->IsSplitViewModeActive() &&
258           (window == split_view_controller_->left_window() ||
259            window == split_view_controller_->right_window())) {
260         // Do not let the left/right windows be scrolled.
261         gfx::Transform transform =
262             GetTransformForSplitWindow(window, kOverviewDefaultScale);
263         state->max_y = state->min_y = transform.To2dTranslation().y();
264         state->split = true;
265         --index;
266         continue;
267       }
268       state->split = false;
269       UpdateTerminalStateForWindowAtIndex(window, index, windows.size());
270     }
271   }
272
273   // Computes the terminal states (i.e. the transforms for the top-most and
274   // bottom-most position in the stack) for |window|. |window_count| is the
275   // number of windows in the stack, and |index| is the position of the window
276   // in the stack (0 being the front-most window).
277   void UpdateTerminalStateForWindowAtIndex(aura::Window* window,
278                                            size_t index,
279                                            size_t window_count) {
280     const int kGapBetweenWindowsBottom = 10;
281     const int kGapBetweenWindowsTop = 5;
282
283     int top = (window_count - index - 1) * kGapBetweenWindowsTop;
284     int bottom = GetScrollableHeight() - (index * kGapBetweenWindowsBottom);
285
286     WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
287     CHECK(state);
288     if (state->split)
289       return;
290     state->min_y = top;
291     state->max_y = bottom - window->bounds().y();
292     state->progress = 0.f;
293   }
294
295   // Sets the initial position for the windows for the overview mode.
296   void SetInitialWindowStates() {
297     // The initial overview state of the topmost three windows.
298     const float kInitialProgress[] = { 0.5f, 0.05f, 0.01f };
299     size_t index = 0;
300     const aura::Window::Windows& windows =
301         window_list_provider_->GetWindowList();
302     for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
303          iter != windows.rend();
304          ++iter) {
305       float progress = 0.f;
306       aura::Window* window = *iter;
307       if (split_view_controller_->IsSplitViewModeActive() &&
308           (window == split_view_controller_->left_window() ||
309            window == split_view_controller_->right_window())) {
310         progress = 1;
311       } else {
312         if (index < arraysize(kInitialProgress))
313           progress = kInitialProgress[index];
314         ++index;
315       }
316
317       scoped_refptr<ui::LayerAnimator> animator =
318           window->layer()->GetAnimator();
319
320       // Unset any in-progress animation.
321       animator->AbortAllAnimations();
322       window->Show();
323       window->SetTransform(gfx::Transform());
324       // Setup the animation.
325       {
326         ui::ScopedLayerAnimationSettings settings(animator);
327         settings.SetPreemptionStrategy(
328             ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
329         settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250));
330         SetWindowProgress(window, progress);
331       }
332     }
333   }
334
335   aura::Window* SelectWindowAt(ui::LocatedEvent* event) {
336     CHECK_EQ(container_, event->target());
337     // Find the old targeter to find the target of the event.
338     ui::EventTarget* window = container_;
339     ui::EventTargeter* targeter = scoped_targeter_->old_targeter();
340     while (!targeter && window->GetParentTarget()) {
341       window = window->GetParentTarget();
342       targeter = window->GetEventTargeter();
343     }
344     if (!targeter)
345       return NULL;
346     aura::Window* target = static_cast<aura::Window*>(
347         targeter->FindTargetForLocatedEvent(container_, event));
348     while (target && target->parent() != container_)
349       target = target->parent();
350     return target;
351   }
352
353   // Scroll the window list by |delta_y| amount. |delta_y| is negative when
354   // scrolling up; and positive when scrolling down.
355   void DoScroll(float delta_y) {
356     const float kEpsilon = 1e-3f;
357     float delta_y_p = std::abs(delta_y) / GetScrollableHeight();
358     const aura::Window::Windows& windows =
359         window_list_provider_->GetWindowList();
360     if (delta_y < 0) {
361       // Scroll up. Start with the top-most (i.e. behind-most in terms of
362       // z-index) window, and try to scroll them up.
363       for (aura::Window::Windows::const_iterator iter = windows.begin();
364            delta_y_p > kEpsilon && iter != windows.end();
365            ++iter) {
366         aura::Window* window = (*iter);
367         WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
368         if (state->progress > kEpsilon) {
369           // It is possible to scroll |window| up. Scroll it up, and update
370           // |delta_y_p| for the next window.
371           float apply = delta_y_p * state->progress;
372           SetWindowProgress(window, std::max(0.f, state->progress - apply * 3));
373           delta_y_p -= apply;
374         }
375       }
376     } else {
377       // Scroll down. Start with the bottom-most (i.e. front-most in terms of
378       // z-index) window, and try to scroll them down.
379       aura::Window::Windows::const_reverse_iterator iter;
380       for (iter = windows.rbegin();
381            delta_y_p > kEpsilon && iter != windows.rend();
382            ++iter) {
383         aura::Window* window = (*iter);
384         WindowOverviewState* state = window->GetProperty(kWindowOverviewState);
385         if (1.f - state->progress > kEpsilon) {
386           // It is possible to scroll |window| down. Scroll it down, and update
387           // |delta_y_p| for the next window.
388           SetWindowProgress(window, std::min(1.f, state->progress + delta_y_p));
389           delta_y_p /= 2.f;
390         }
391       }
392     }
393   }
394
395   int GetScrollableHeight() const {
396     const float kScrollableFraction = 0.85f;
397     const float kScrollableFractionInSplit = 0.5f;
398     const float fraction = split_view_controller_->IsSplitViewModeActive()
399                                ? kScrollableFractionInSplit
400                                : kScrollableFraction;
401     return container_->bounds().height() * fraction;
402   }
403
404   void CreateFlingerFor(const ui::GestureEvent& event) {
405     gfx::Vector2dF velocity(event.details().velocity_x(),
406                             event.details().velocity_y());
407     fling_.reset(new ui::FlingCurve(velocity, gfx::FrameTime::Now()));
408   }
409
410   void AddAnimationObserver() {
411     ui::Compositor* compositor = container_->GetHost()->compositor();
412     if (!compositor->HasAnimationObserver(this))
413       compositor->AddAnimationObserver(this);
414   }
415
416   void RemoveAnimationObserver() {
417     ui::Compositor* compositor = container_->GetHost()->compositor();
418     if (compositor->HasAnimationObserver(this))
419       compositor->RemoveAnimationObserver(this);
420   }
421
422   aura::Window* GetSplitWindowDropTarget(const ui::GestureEvent& event) const {
423     if (!split_view_controller_->IsSplitViewModeActive())
424       return NULL;
425     CHECK(dragged_window_);
426     CHECK_NE(split_view_controller_->left_window(), dragged_window_);
427     CHECK_NE(split_view_controller_->right_window(), dragged_window_);
428     aura::Window* window = split_view_controller_->left_window();
429     if (GetTransformedBounds(window).Contains(event.location()))
430       return window;
431     window = split_view_controller_->right_window();
432     if (GetTransformedBounds(window).Contains(event.location()))
433       return window;
434     return NULL;
435   }
436
437   void DragWindow(const ui::GestureEvent& event) {
438     CHECK(dragged_window_);
439     CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type());
440     CHECK(overview_toolbar_);
441     gfx::Vector2dF dragged_distance =
442         dragged_start_location_ - event.location();
443     WindowOverviewState* dragged_state =
444         dragged_window_->GetProperty(kWindowOverviewState);
445     CHECK(dragged_state);
446     gfx::Transform transform =
447         GetTransformForState(dragged_window_, dragged_state);
448     transform.Translate(-dragged_distance.x(), 0);
449     dragged_window_->SetTransform(transform);
450
451     // Update the toolbar.
452     const int kMinDistanceForActionButtons = 20;
453     if (fabs(dragged_distance.x()) > kMinDistanceForActionButtons)
454       overview_toolbar_->ShowActionButtons();
455     else
456       overview_toolbar_->HideActionButtons();
457
458     // See if the touch-point is above one of the action-buttons.
459     OverviewToolbar::ActionType new_action =
460         overview_toolbar_->GetHighlightAction(event);
461
462     // If the touch-point is not above any of the action buttons, then highlight
463     // the close-button by default, if the user has dragged enough to close the
464     // window.
465     if (new_action == OverviewToolbar::ACTION_TYPE_NONE) {
466       if (fabs(dragged_distance.x()) > kMinDistanceForDismissal)
467         new_action = OverviewToolbar::ACTION_TYPE_CLOSE;
468       else
469         new_action = OverviewToolbar::ACTION_TYPE_NONE;
470     }
471     OverviewToolbar::ActionType previous_action =
472         overview_toolbar_->current_action();
473     overview_toolbar_->SetHighlightAction(new_action);
474
475     aura::Window* split_drop = GetSplitWindowDropTarget(event);
476
477     // If the user has selected to get into split-view mode, then show the
478     // window with full opacity. Otherwise, fade it out as it closes. Animate
479     // the opacity if transitioning to/from the split-view button.
480     bool animate_opacity =
481         (new_action != previous_action) &&
482         ((new_action == OverviewToolbar::ACTION_TYPE_SPLIT) ||
483          (previous_action == OverviewToolbar::ACTION_TYPE_SPLIT));
484     float ratio = std::min(
485         1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal);
486     float opacity =
487         (new_action == OverviewToolbar::ACTION_TYPE_SPLIT || split_drop)
488             ? 1
489             : gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity);
490     if (animate_opacity) {
491       ui::ScopedLayerAnimationSettings settings(
492           dragged_window_->layer()->GetAnimator());
493       dragged_window_->layer()->SetOpacity(opacity);
494     } else {
495       dragged_window_->layer()->SetOpacity(opacity);
496     }
497
498     if (split_view_controller_->IsSplitViewModeActive()) {
499       float scale = kOverviewDefaultScale;
500       if (split_drop == split_view_controller_->left_window())
501         scale = kMaxScaleForSplitTarget;
502       TransformSplitWindowScale(split_view_controller_->left_window(), scale);
503
504       scale = kOverviewDefaultScale;
505       if (split_drop == split_view_controller_->right_window())
506         scale = kMaxScaleForSplitTarget;
507       TransformSplitWindowScale(split_view_controller_->right_window(), scale);
508     }
509   }
510
511   bool ShouldCloseDragWindow(const ui::GestureEvent& event) const {
512     gfx::Vector2dF dragged_distance =
513         dragged_start_location_ - event.location();
514     if (event.type() == ui::ET_GESTURE_SCROLL_END)
515       return std::abs(dragged_distance.x()) >= kMinDistanceForDismissal;
516     CHECK_EQ(ui::ET_SCROLL_FLING_START, event.type());
517     const bool dragging_towards_right = dragged_distance.x() < 0;
518     const bool swipe_towards_right = event.details().velocity_x() > 0;
519     if (dragging_towards_right != swipe_towards_right)
520       return false;
521     const float kMinVelocityForDismissal = 500.f;
522     return std::abs(event.details().velocity_x()) > kMinVelocityForDismissal;
523   }
524
525   void CloseDragWindow(const ui::GestureEvent& gesture) {
526     // Animate |dragged_window_| offscreen first, then destroy it.
527     {
528       wm::ScopedHidingAnimationSettings settings(dragged_window_);
529       settings.layer_animation_settings()->SetPreemptionStrategy(
530           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
531
532       WindowOverviewState* dragged_state =
533           dragged_window_->GetProperty(kWindowOverviewState);
534       CHECK(dragged_state);
535       gfx::Transform transform = dragged_window_->layer()->transform();
536       gfx::RectF transformed_bounds = dragged_window_->bounds();
537       transform.TransformRect(&transformed_bounds);
538       float transform_x = 0.f;
539       if (gesture.location().x() > dragged_start_location_.x())
540         transform_x = container_->bounds().right() - transformed_bounds.x();
541       else
542         transform_x = -(transformed_bounds.x() + transformed_bounds.width());
543       transform.Translate(transform_x / kOverviewDefaultScale, 0);
544       dragged_window_->SetTransform(transform);
545       dragged_window_->layer()->SetOpacity(kMinOpacity);
546     }
547     delete dragged_window_;
548     dragged_window_ = NULL;
549   }
550
551   void RestoreDragWindow() {
552     CHECK(dragged_window_);
553     WindowOverviewState* dragged_state =
554         dragged_window_->GetProperty(kWindowOverviewState);
555     CHECK(dragged_state);
556
557     ui::ScopedLayerAnimationSettings settings(
558         dragged_window_->layer()->GetAnimator());
559     settings.SetPreemptionStrategy(
560         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
561     dragged_window_->SetTransform(
562         GetTransformForState(dragged_window_, dragged_state));
563     dragged_window_->layer()->SetOpacity(1.f);
564     dragged_window_ = NULL;
565   }
566
567   void EndDragWindow(const ui::GestureEvent& gesture) {
568     CHECK(dragged_window_);
569     CHECK(overview_toolbar_);
570     OverviewToolbar::ActionType action = overview_toolbar_->current_action();
571     overview_toolbar_.reset();
572     if (action == OverviewToolbar::ACTION_TYPE_SPLIT) {
573       delegate_->OnSelectSplitViewWindow(NULL,
574                                          dragged_window_,
575                                          dragged_window_);
576       return;
577     }
578
579     // If the window is dropped on one of the left/right windows in split-mode,
580     // then switch that window.
581     aura::Window* split_drop = GetSplitWindowDropTarget(gesture);
582     if (split_drop) {
583       aura::Window* left = split_view_controller_->left_window();
584       aura::Window* right = split_view_controller_->right_window();
585       if (left == split_drop)
586         left = dragged_window_;
587       else
588         right = dragged_window_;
589       delegate_->OnSelectSplitViewWindow(left, right, dragged_window_);
590       return;
591     }
592
593     if (ShouldCloseDragWindow(gesture))
594       CloseDragWindow(gesture);
595     else
596       RestoreDragWindow();
597   }
598
599   void SelectWindow(aura::Window* window) {
600     if (!split_view_controller_->IsSplitViewModeActive()) {
601       delegate_->OnSelectWindow(window);
602     } else {
603       // If the selected window is one of the left/right windows, then keep the
604       // current state.
605       if (window == split_view_controller_->left_window() ||
606           window == split_view_controller_->right_window()) {
607         delegate_->OnSelectSplitViewWindow(
608             split_view_controller_->left_window(),
609             split_view_controller_->right_window(),
610             window);
611       } else {
612         delegate_->OnSelectWindow(window);
613       }
614     }
615   }
616
617   // ui::EventHandler:
618   virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE {
619     if (mouse->type() == ui::ET_MOUSE_PRESSED) {
620       aura::Window* select = SelectWindowAt(mouse);
621       if (select) {
622         mouse->SetHandled();
623         SelectWindow(select);
624       }
625     } else if (mouse->type() == ui::ET_MOUSEWHEEL) {
626       DoScroll(static_cast<ui::MouseWheelEvent*>(mouse)->y_offset());
627     }
628   }
629
630   virtual void OnScrollEvent(ui::ScrollEvent* scroll) OVERRIDE {
631     if (scroll->type() == ui::ET_SCROLL)
632       DoScroll(scroll->y_offset());
633   }
634
635   virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE {
636     if (gesture->type() == ui::ET_GESTURE_TAP) {
637       aura::Window* select = SelectWindowAt(gesture);
638       if (select) {
639         gesture->SetHandled();
640         SelectWindow(select);
641       }
642     } else if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
643       if (std::abs(gesture->details().scroll_x_hint()) >
644           std::abs(gesture->details().scroll_y_hint()) * 2) {
645         dragged_start_location_ = gesture->location();
646         dragged_window_ = SelectWindowAt(gesture);
647         if (split_view_controller_->IsSplitViewModeActive() &&
648             (dragged_window_ == split_view_controller_->left_window() ||
649              dragged_window_ == split_view_controller_->right_window())) {
650           // TODO(sad): Allow closing the left/right window. Closing one of
651           // these windows will terminate the split-view mode. Until then, do
652           // not allow closing these (since otherwise it gets into an undefined
653           // state).
654           dragged_window_ = NULL;
655         }
656
657         if (dragged_window_) {
658           // Show the toolbar (for closing a window, or going into split-view
659           // mode). If already in split-view mode, then do not show the 'Split'
660           // option.
661           overview_toolbar_.reset(new OverviewToolbar(container_));
662           if (!split_view_controller_->CanActivateSplitViewMode()) {
663             overview_toolbar_->DisableAction(
664                 OverviewToolbar::ACTION_TYPE_SPLIT);
665           }
666         }
667       }
668     } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
669       if (dragged_window_)
670         DragWindow(*gesture);
671       else
672         DoScroll(gesture->details().scroll_y());
673       gesture->SetHandled();
674     } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) {
675       if (dragged_window_)
676         EndDragWindow(*gesture);
677       gesture->SetHandled();
678     } else if (gesture->type() == ui::ET_SCROLL_FLING_START) {
679       if (dragged_window_) {
680         EndDragWindow(*gesture);
681       } else {
682         CreateFlingerFor(*gesture);
683         AddAnimationObserver();
684       }
685       gesture->SetHandled();
686     } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) {
687       if (fling_) {
688         fling_.reset();
689         RemoveAnimationObserver();
690         gesture->SetHandled();
691       }
692       dragged_window_ = NULL;
693     }
694   }
695
696   // ui::CompositorAnimationObserver:
697   virtual void OnAnimationStep(base::TimeTicks timestamp) OVERRIDE {
698     CHECK(fling_);
699     if (fling_->start_timestamp() > timestamp)
700       return;
701     gfx::Vector2dF scroll = fling_->GetScrollAmountAtTime(timestamp);
702     if (scroll.IsZero()) {
703       fling_.reset();
704       RemoveAnimationObserver();
705     } else {
706       DoScroll(scroll.y());
707     }
708   }
709
710   // WindowListProviderObserver:
711   virtual void OnWindowStackingChanged() OVERRIDE {
712     // Recompute the states of all windows. There isn't enough information at
713     // this point to do anything more clever.
714     ComputeTerminalStatesForAllWindows();
715     SetInitialWindowStates();
716   }
717
718   virtual void OnWindowRemoved(aura::Window* removed_window,
719                                int index) OVERRIDE {
720     const aura::Window::Windows& windows =
721         window_list_provider_->GetWindowList();
722     if (windows.empty())
723       return;
724     CHECK_LE(index, static_cast<int>(windows.size()));
725     if (index == 0) {
726       // The back-most window has been removed. Move all the remaining windows
727       // one step backwards.
728       for (int i = windows.size() - 1; i > 0; --i) {
729         UpdateTerminalStateForWindowAtIndex(
730             windows[i], windows.size() - 1 - i, windows.size());
731         AnimateWindowTo(windows[i], windows[i - 1]);
732       }
733       UpdateTerminalStateForWindowAtIndex(windows.front(),
734                                           windows.size() - 1,
735                                           windows.size());
736       AnimateWindowTo(windows.front(), removed_window);
737     } else {
738       // Move all windows behind the removed window one step forwards.
739       for (int i = 0; i < index - 1; ++i) {
740         UpdateTerminalStateForWindowAtIndex(windows[i], windows.size() - 1 - i,
741                                             windows.size());
742         AnimateWindowTo(windows[i], windows[i + 1]);
743       }
744       UpdateTerminalStateForWindowAtIndex(windows[index - 1],
745                                           windows.size() - index,
746                                           windows.size());
747       AnimateWindowTo(windows[index - 1], removed_window);
748     }
749   }
750
751   const int kMinDistanceForDismissal = 300;
752   const float kMaxOpacity = 1.0f;
753   const float kMinOpacity = 0.2f;
754   const float kMaxScaleForSplitTarget = 0.9f;
755
756   aura::Window* container_;
757   // Provider of the stack of windows to show in the overview mode. Not owned.
758   WindowListProvider* window_list_provider_;
759   SplitViewController* split_view_controller_;
760
761   WindowOverviewModeDelegate* delegate_;
762   scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter_;
763   scoped_ptr<ui::FlingCurve> fling_;
764
765   aura::Window* dragged_window_;
766   gfx::Point dragged_start_location_;
767   scoped_ptr<OverviewToolbar> overview_toolbar_;
768
769   DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl);
770 };
771
772 }  // namespace
773
774 // static
775 scoped_ptr<WindowOverviewMode> WindowOverviewMode::Create(
776     aura::Window* container,
777     WindowListProvider* window_list_provider,
778     SplitViewController* split_view_controller,
779     WindowOverviewModeDelegate* delegate) {
780   return scoped_ptr<WindowOverviewMode>(
781       new WindowOverviewModeImpl(container, window_list_provider,
782                                  split_view_controller, delegate));
783 }
784
785 }  // namespace athena