slide_out_controller.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. #include "ui/views/animation/slide_out_controller.h"
  5. #include <algorithm>
  6. #include "base/bind.h"
  7. #include "base/threading/thread_task_runner_handle.h"
  8. #include "ui/compositor/layer.h"
  9. #include "ui/compositor/scoped_layer_animation_settings.h"
  10. #include "ui/gfx/geometry/transform.h"
  11. #include "ui/views/animation/animation_builder.h"
  12. #include "ui/views/animation/slide_out_controller_delegate.h"
  13. namespace views {
  14. namespace {
  15. constexpr base::TimeDelta kSwipeRestoreDuration = base::Milliseconds(150);
  16. constexpr int kSwipeOutTotalDurationMs = 150;
  17. gfx::Tween::Type kSwipeTweenType = gfx::Tween::EASE_IN;
  18. // When we have a swipe control, we will close the target if it is slid more
  19. // than this amount plus the width of the swipe control.
  20. constexpr int kSwipeCloseMargin = 64;
  21. } // anonymous namespace
  22. SlideOutController::SlideOutController(ui::EventTarget* target,
  23. SlideOutControllerDelegate* delegate)
  24. : target_handling_(target, this), delegate_(delegate) {}
  25. SlideOutController::~SlideOutController() = default;
  26. void SlideOutController::CaptureControlOpenState() {
  27. if (!has_swipe_control_)
  28. return;
  29. if ((mode_ == SlideMode::kFull || mode_ == SlideMode::kPartial) &&
  30. fabs(gesture_amount_) >= swipe_control_width_) {
  31. control_open_state_ = gesture_amount_ < 0
  32. ? SwipeControlOpenState::kOpenOnRight
  33. : SwipeControlOpenState::kOpenOnLeft;
  34. } else {
  35. control_open_state_ = SwipeControlOpenState::kClosed;
  36. }
  37. }
  38. void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
  39. ui::Layer* layer = delegate_->GetSlideOutLayer();
  40. int width = layer->bounds().width();
  41. float scroll_amount_for_closing_notification =
  42. has_swipe_control_ ? swipe_control_width_ + kSwipeCloseMargin
  43. : width * 0.5;
  44. if (event->type() == ui::ET_SCROLL_FLING_START) {
  45. // The threshold for the fling velocity is computed empirically.
  46. // The unit is in pixels/second.
  47. const float kFlingThresholdForClose = 800.f;
  48. if (mode_ == SlideMode::kFull &&
  49. fabsf(event->details().velocity_x()) > kFlingThresholdForClose) {
  50. SlideOutAndClose(event->details().velocity_x());
  51. event->StopPropagation();
  52. return;
  53. }
  54. CaptureControlOpenState();
  55. RestoreVisualState();
  56. return;
  57. }
  58. if (!event->IsScrollGestureEvent())
  59. return;
  60. if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
  61. switch (control_open_state_) {
  62. case SwipeControlOpenState::kClosed:
  63. gesture_amount_ = 0.f;
  64. break;
  65. case SwipeControlOpenState::kOpenOnRight:
  66. gesture_amount_ = -swipe_control_width_;
  67. break;
  68. case SwipeControlOpenState::kOpenOnLeft:
  69. gesture_amount_ = swipe_control_width_;
  70. break;
  71. default:
  72. NOTREACHED();
  73. }
  74. delegate_->OnSlideStarted();
  75. } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
  76. // The scroll-update events include the incremental scroll amount.
  77. gesture_amount_ += event->details().scroll_x();
  78. float scroll_amount;
  79. float opacity;
  80. switch (mode_) {
  81. case SlideMode::kFull:
  82. scroll_amount = gesture_amount_;
  83. opacity = 1.f - std::min(fabsf(scroll_amount) / width, 1.f);
  84. break;
  85. case SlideMode::kNone:
  86. scroll_amount = 0.f;
  87. opacity = 1.f;
  88. break;
  89. case SlideMode::kPartial:
  90. if (gesture_amount_ >= 0) {
  91. scroll_amount = std::min(0.5f * gesture_amount_,
  92. scroll_amount_for_closing_notification);
  93. } else {
  94. scroll_amount =
  95. std::max(0.5f * gesture_amount_,
  96. -1.f * scroll_amount_for_closing_notification);
  97. }
  98. opacity = 1.f;
  99. break;
  100. }
  101. SetOpacityIfNecessary(opacity);
  102. gfx::Transform transform;
  103. transform.Translate(scroll_amount, 0.0);
  104. layer->SetTransform(transform);
  105. delegate_->OnSlideChanged(true);
  106. } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
  107. float scrolled_ratio = fabsf(gesture_amount_) / width;
  108. if (mode_ == SlideMode::kFull &&
  109. scrolled_ratio >= scroll_amount_for_closing_notification / width) {
  110. SlideOutAndClose(gesture_amount_);
  111. event->StopPropagation();
  112. return;
  113. }
  114. CaptureControlOpenState();
  115. RestoreVisualState();
  116. }
  117. event->SetHandled();
  118. }
  119. void SlideOutController::RestoreVisualState() {
  120. // Restore the layer state.
  121. gfx::Transform transform;
  122. switch (control_open_state_) {
  123. case SwipeControlOpenState::kClosed:
  124. gesture_amount_ = 0.f;
  125. break;
  126. case SwipeControlOpenState::kOpenOnRight:
  127. gesture_amount_ = -swipe_control_width_;
  128. transform.Translate(-swipe_control_width_, 0);
  129. break;
  130. case SwipeControlOpenState::kOpenOnLeft:
  131. gesture_amount_ = swipe_control_width_;
  132. transform.Translate(swipe_control_width_, 0);
  133. break;
  134. }
  135. SetOpacityIfNecessary(1.f);
  136. SetTransformWithAnimationIfNecessary(transform, kSwipeRestoreDuration);
  137. }
  138. void SlideOutController::SlideOutAndClose(int direction) {
  139. ui::Layer* layer = delegate_->GetSlideOutLayer();
  140. gfx::Transform transform;
  141. int width = layer->bounds().width();
  142. transform.Translate(direction < 0 ? -width : width, 0.0);
  143. int swipe_out_duration = kSwipeOutTotalDurationMs * opacity_;
  144. SetOpacityIfNecessary(0.f);
  145. SetTransformWithAnimationIfNecessary(transform,
  146. base::Milliseconds(swipe_out_duration));
  147. }
  148. void SlideOutController::SetOpacityIfNecessary(float opacity) {
  149. if (update_opacity_)
  150. delegate_->GetSlideOutLayer()->SetOpacity(opacity);
  151. opacity_ = opacity;
  152. }
  153. void SlideOutController::SetTransformWithAnimationIfNecessary(
  154. const gfx::Transform& transform,
  155. base::TimeDelta animation_duration) {
  156. ui::Layer* layer = delegate_->GetSlideOutLayer();
  157. if (layer->transform() != transform) {
  158. // Notify slide changed with inprogress=true, since the element will slide
  159. // with animation. OnSlideChanged(false) will be called after animation.
  160. delegate_->OnSlideChanged(true);
  161. // An animation starts. OnAnimationsCompleted will be called just
  162. // after the animation finishes.
  163. AnimationBuilder()
  164. .OnEnded(base::BindOnce(&SlideOutController::OnAnimationsCompleted,
  165. weak_ptr_factory_.GetWeakPtr()))
  166. .Once()
  167. .SetDuration(animation_duration)
  168. .SetTransform(layer, transform, kSwipeTweenType);
  169. } else {
  170. // Notify slide changed after the animation finishes.
  171. // The argument in_progress is true if the target view is back at the
  172. // origin or has been gone. False if the target is visible but not at
  173. // the origin. False if the target is visible but not at
  174. // the origin.
  175. const bool in_progress = !layer->transform().IsIdentity();
  176. delegate_->OnSlideChanged(in_progress);
  177. }
  178. }
  179. void SlideOutController::OnAnimationsCompleted() {
  180. // Here the situation is either of:
  181. // 1) Notification is slided out and is about to be removed
  182. // => |in_progress| is false, calling OnSlideOut
  183. // 2) Notification is at the origin => |in_progress| is false
  184. // 3) Notification is snapped to the swipe control => |in_progress| is true
  185. const bool is_completely_slid_out = (opacity_ == 0);
  186. const bool in_progress =
  187. !delegate_->GetSlideOutLayer()->transform().IsIdentity() &&
  188. !is_completely_slid_out;
  189. delegate_->OnSlideChanged(in_progress);
  190. if (!is_completely_slid_out)
  191. return;
  192. // Call SlideOutControllerDelegate::OnSlideOut() if this animation came from
  193. // SlideOutAndClose().
  194. // OnImplicitAnimationsCompleted is called from BeginMainFrame, so we should
  195. // delay operation that might result in deletion of LayerTreeHost.
  196. // https://crbug.com/895883
  197. base::ThreadTaskRunnerHandle::Get()->PostTask(
  198. FROM_HERE, base::BindOnce(&SlideOutController::OnSlideOut,
  199. weak_ptr_factory_.GetWeakPtr()));
  200. }
  201. void SlideOutController::OnSlideOut() {
  202. delegate_->OnSlideOut();
  203. }
  204. void SlideOutController::SetSwipeControlWidth(int swipe_control_width) {
  205. swipe_control_width_ = swipe_control_width;
  206. has_swipe_control_ = (swipe_control_width != 0);
  207. }
  208. void SlideOutController::CloseSwipeControl() {
  209. if (!has_swipe_control_)
  210. return;
  211. gesture_amount_ = 0;
  212. CaptureControlOpenState();
  213. RestoreVisualState();
  214. }
  215. } // namespace views