scrollbar_animator_mac.cc 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright 2021 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/native_theme/scrollbar_animator_mac.h"
  5. #include "base/cxx17_backports.h"
  6. namespace ui {
  7. ///////////////////////////////////////////////////////////////////////////////
  8. // ScrollbarAnimationTimerMac
  9. ScrollbarAnimationTimerMac::ScrollbarAnimationTimerMac(
  10. base::RepeatingCallback<void(double)> callback,
  11. double duration,
  12. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  13. : start_time_(0.0), duration_(duration), callback_(std::move(callback)) {
  14. timing_function_ = gfx::CubicBezierTimingFunction::CreatePreset(
  15. gfx::CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
  16. }
  17. ScrollbarAnimationTimerMac::~ScrollbarAnimationTimerMac() {}
  18. void ScrollbarAnimationTimerMac::Start() {
  19. start_time_ = base::Time::Now().ToDoubleT();
  20. // Set the framerate of the animation. NSAnimation uses a default
  21. // framerate of 60 Hz, so use that here.
  22. timer_.Start(FROM_HERE, base::Seconds(1.0 / 60.0), this,
  23. &ScrollbarAnimationTimerMac::TimerFired);
  24. }
  25. void ScrollbarAnimationTimerMac::Stop() {
  26. timer_.Stop();
  27. }
  28. void ScrollbarAnimationTimerMac::SetDuration(double duration) {
  29. duration_ = duration;
  30. }
  31. void ScrollbarAnimationTimerMac::TimerFired() {
  32. double current_time = base::Time::Now().ToDoubleT();
  33. double delta = current_time - start_time_;
  34. if (delta >= duration_)
  35. timer_.Stop();
  36. double fraction = delta / duration_;
  37. fraction = base::clamp(fraction, 0.0, 1.0);
  38. double progress = timing_function_->GetValue(fraction);
  39. // Note that `this` may be destroyed from within `callback_`, so it is not
  40. // safe to call any other code after it.
  41. callback_.Run(progress);
  42. }
  43. ///////////////////////////////////////////////////////////////////////////////
  44. // OverlayScrollbarAnimatorMac
  45. OverlayScrollbarAnimatorMac::OverlayScrollbarAnimatorMac(
  46. Client* client,
  47. int thumb_width_expanded,
  48. int thumb_width_unexpanded,
  49. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  50. : client_(client),
  51. thumb_width_expanded_(thumb_width_expanded),
  52. thumb_width_unexpanded_(thumb_width_unexpanded),
  53. thumb_width_(thumb_width_unexpanded),
  54. task_runner_(task_runner),
  55. weak_factory_(this) {}
  56. OverlayScrollbarAnimatorMac::~OverlayScrollbarAnimatorMac() = default;
  57. void OverlayScrollbarAnimatorMac::MouseDidEnter() {
  58. // If the scrollbar is completely hidden, ignore this. We will initialize
  59. // the `mouse_in_track_` state if there's a scroll.
  60. if (thumb_alpha_ == 0.f)
  61. return;
  62. if (mouse_in_track_)
  63. return;
  64. mouse_in_track_ = true;
  65. // Cancel any in-progress fade-out, and ensure that the fade-out timer be
  66. // disabled.
  67. if (fade_out_animation_)
  68. FadeOutAnimationCancel();
  69. FadeOutTimerUpdate();
  70. // Start the fade-in animation (unless it is in progress or has already
  71. // completed).
  72. if (!fade_in_track_animation_ && track_alpha_ != 1.f)
  73. FadeInTrackAnimationStart();
  74. // Start the expand-thumb animation (unless it is in progress or has already
  75. // completed).
  76. if (!expand_thumb_animation_ && thumb_width_ != thumb_width_expanded_)
  77. ExpandThumbAnimationStart();
  78. }
  79. void OverlayScrollbarAnimatorMac::MouseDidExit() {
  80. // Ensure that the fade-out timer be re-enabled.
  81. mouse_in_track_ = false;
  82. FadeOutTimerUpdate();
  83. }
  84. void OverlayScrollbarAnimatorMac::DidScroll() {
  85. // If we were fading out, then immediately return to being fully opaque for
  86. // both the thumb and track.
  87. if (fade_out_animation_) {
  88. FadeOutAnimationCancel();
  89. FadeOutTimerUpdate();
  90. return;
  91. }
  92. // If the scrollbar was already fully visible, then just update the fade-out
  93. // timer.
  94. if (thumb_alpha_ == 1.f) {
  95. FadeOutTimerUpdate();
  96. return;
  97. }
  98. // This is an initial scroll causing the thumb to appear.
  99. DCHECK_EQ(thumb_width_, thumb_width_unexpanded_);
  100. DCHECK_EQ(thumb_alpha_, 0.f);
  101. DCHECK(!fade_in_track_animation_);
  102. thumb_width_ = thumb_width_unexpanded_;
  103. thumb_alpha_ = 1;
  104. client_->SetThumbNeedsDisplay();
  105. client_->SetHidden(false);
  106. // If the initial scroll is done inside the scrollbar's area, then also
  107. // cause the track to appear and the thumb to enlarge.
  108. if (client_->IsMouseInScrollbarFrameRect()) {
  109. mouse_in_track_ = true;
  110. thumb_width_ = thumb_width_expanded_;
  111. track_alpha_ = 1;
  112. client_->SetTrackNeedsDisplay();
  113. }
  114. // Update the fade-out timer (now that we know whether or not the mouse
  115. // is in the track).
  116. FadeOutTimerUpdate();
  117. }
  118. void OverlayScrollbarAnimatorMac::ExpandThumbAnimationStart() {
  119. DCHECK(!expand_thumb_animation_);
  120. DCHECK_NE(thumb_width_, thumb_width_expanded_);
  121. expand_thumb_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
  122. base::BindRepeating(
  123. &OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked,
  124. weak_factory_.GetWeakPtr()),
  125. kAnimationDurationSeconds, task_runner_);
  126. expand_thumb_animation_->Start();
  127. }
  128. void OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked(double progress) {
  129. thumb_width_ = (1 - progress) * thumb_width_unexpanded_ +
  130. progress * thumb_width_expanded_;
  131. client_->SetThumbNeedsDisplay();
  132. if (progress == 1)
  133. expand_thumb_animation_.reset();
  134. }
  135. void OverlayScrollbarAnimatorMac::FadeInTrackAnimationStart() {
  136. DCHECK(!fade_in_track_animation_);
  137. DCHECK(!fade_out_animation_);
  138. fade_in_track_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
  139. base::BindRepeating(
  140. &OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked,
  141. weak_factory_.GetWeakPtr()),
  142. kAnimationDurationSeconds, task_runner_);
  143. fade_in_track_animation_->Start();
  144. }
  145. void OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked(double progress) {
  146. // Fade-in and fade-out are mutually exclusive.
  147. DCHECK(!fade_out_animation_);
  148. track_alpha_ = progress;
  149. client_->SetTrackNeedsDisplay();
  150. if (progress == 1) {
  151. fade_in_track_animation_.reset();
  152. }
  153. }
  154. void OverlayScrollbarAnimatorMac::FadeOutTimerUpdate() {
  155. if (mouse_in_track_) {
  156. start_scrollbar_fade_out_timer_.reset();
  157. return;
  158. }
  159. if (!start_scrollbar_fade_out_timer_) {
  160. start_scrollbar_fade_out_timer_ =
  161. std::make_unique<base::RetainingOneShotTimer>(
  162. FROM_HERE, kFadeOutDelay,
  163. base::BindRepeating(
  164. &OverlayScrollbarAnimatorMac::FadeOutAnimationStart,
  165. weak_factory_.GetWeakPtr()));
  166. start_scrollbar_fade_out_timer_->SetTaskRunner(task_runner_);
  167. }
  168. start_scrollbar_fade_out_timer_->Reset();
  169. }
  170. void OverlayScrollbarAnimatorMac::FadeOutAnimationStart() {
  171. start_scrollbar_fade_out_timer_.reset();
  172. fade_in_track_animation_.reset();
  173. fade_out_animation_.reset();
  174. fade_out_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
  175. base::BindRepeating(&OverlayScrollbarAnimatorMac::FadeOutAnimationTicked,
  176. weak_factory_.GetWeakPtr()),
  177. kAnimationDurationSeconds, task_runner_);
  178. fade_out_animation_->Start();
  179. }
  180. void OverlayScrollbarAnimatorMac::FadeOutAnimationTicked(double progress) {
  181. DCHECK(!fade_in_track_animation_);
  182. // Fade out the thumb.
  183. thumb_alpha_ = 1 - progress;
  184. client_->SetThumbNeedsDisplay();
  185. // If the track is not already invisible, fade it out.
  186. if (track_alpha_ != 0) {
  187. track_alpha_ = 1 - progress;
  188. client_->SetTrackNeedsDisplay();
  189. }
  190. // Once completely faded out, reset all state.
  191. if (progress == 1) {
  192. expand_thumb_animation_.reset();
  193. fade_out_animation_.reset();
  194. thumb_width_ = thumb_width_unexpanded_;
  195. DCHECK_EQ(thumb_alpha_, 0.f);
  196. DCHECK_EQ(track_alpha_, 0.f);
  197. // Mark that the scrollbars were hidden.
  198. client_->SetHidden(true);
  199. }
  200. }
  201. void OverlayScrollbarAnimatorMac::FadeOutAnimationCancel() {
  202. DCHECK(fade_out_animation_);
  203. fade_out_animation_.reset();
  204. thumb_alpha_ = 1;
  205. client_->SetThumbNeedsDisplay();
  206. if (track_alpha_ > 0) {
  207. track_alpha_ = 1;
  208. client_->SetTrackNeedsDisplay();
  209. }
  210. }
  211. const float OverlayScrollbarAnimatorMac::kAnimationDurationSeconds = 0.25f;
  212. const base::TimeDelta OverlayScrollbarAnimatorMac::kFadeOutDelay =
  213. base::Milliseconds(500);
  214. } // namespace ui