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.
4
5#include "ui/views/controls/slide_out_view.h"
6
7#include "ui/compositor/layer.h"
8#include "ui/compositor/scoped_layer_animation_settings.h"
9#include "ui/gfx/transform.h"
10
11namespace views {
12
13SlideOutView::SlideOutView()
14    : gesture_scroll_amount_(0.f) {
15  // If accelerated compositing is not available, this widget tracks the
16  // OnSlideOut event but does not render any visible changes.
17  SetPaintToLayer(true);
18  SetFillsBoundsOpaquely(false);
19}
20
21SlideOutView::~SlideOutView() {
22}
23
24void SlideOutView::OnGestureEvent(ui::GestureEvent* event) {
25  if (event->type() == ui::ET_SCROLL_FLING_START) {
26    // The threshold for the fling velocity is computed empirically.
27    // The unit is in pixels/second.
28    const float kFlingThresholdForClose = 800.f;
29    if (fabsf(event->details().velocity_x()) > kFlingThresholdForClose) {
30      SlideOutAndClose(event->details().velocity_x() < 0 ? SLIDE_LEFT :
31                       SLIDE_RIGHT);
32      event->StopPropagation();
33      return;
34    }
35    RestoreVisualState();
36    return;
37  }
38
39  if (!event->IsScrollGestureEvent())
40    return;
41
42  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
43    gesture_scroll_amount_ = 0.f;
44  } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
45    // The scroll-update events include the incremental scroll amount.
46    gesture_scroll_amount_ += event->details().scroll_x();
47
48    gfx::Transform transform;
49    transform.Translate(gesture_scroll_amount_, 0.0);
50    layer()->SetTransform(transform);
51    layer()->SetOpacity(
52        1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f));
53
54  } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
55    const float kScrollRatioForClosingNotification = 0.5f;
56    float scrolled_ratio = fabsf(gesture_scroll_amount_) / width();
57    if (scrolled_ratio >= kScrollRatioForClosingNotification) {
58      SlideOutAndClose(gesture_scroll_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT);
59      event->StopPropagation();
60      return;
61    }
62    RestoreVisualState();
63  }
64
65  event->SetHandled();
66}
67
68void SlideOutView::RestoreVisualState() {
69  // Restore the layer state.
70  const int kSwipeRestoreDurationMS = 150;
71  ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
72  settings.SetTransitionDuration(
73      base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
74  layer()->SetTransform(gfx::Transform());
75  layer()->SetOpacity(1.f);
76}
77
78void SlideOutView::SlideOutAndClose(SlideDirection direction) {
79  const int kSwipeOutTotalDurationMS = 150;
80  int swipe_out_duration = kSwipeOutTotalDurationMS * layer()->opacity();
81  ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
82  settings.SetTransitionDuration(
83      base::TimeDelta::FromMilliseconds(swipe_out_duration));
84  settings.AddObserver(this);
85
86  gfx::Transform transform;
87  transform.Translate(direction == SLIDE_LEFT ? -width() : width(), 0.0);
88  layer()->SetTransform(transform);
89  layer()->SetOpacity(0.f);
90}
91
92void SlideOutView::OnImplicitAnimationsCompleted() {
93  OnSlideOut();
94}
95
96}  // namespace views
97