#include "ui/native_theme/scrollbar_animator_mac.h"
#include <algorithm>
#include "base/feature_list.h"
#include "base/task/single_thread_task_runner.h"
#include "ui/native_theme/features/native_theme_features.h"
namespace ui {
ScrollbarAnimationTimerMac::ScrollbarAnimationTimerMac(
base::RepeatingCallback<void(double)> callback,
base::TimeDelta duration,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: duration_(duration), callback_(std::move(callback)) {
timing_function_ = gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
}
ScrollbarAnimationTimerMac::~ScrollbarAnimationTimerMac() {}
void ScrollbarAnimationTimerMac::Start() {
start_time_ = base::TimeTicks::Now();
timer_.Start(FROM_HERE, base::Seconds(1.0 / 60.0), this,
&ScrollbarAnimationTimerMac::TimerFired);
}
void ScrollbarAnimationTimerMac::Stop() {
timer_.Stop();
}
void ScrollbarAnimationTimerMac::SetDuration(base::TimeDelta duration) {
duration_ = duration;
}
void ScrollbarAnimationTimerMac::TimerFired() {
base::TimeTicks current_time = base::TimeTicks::Now();
base::TimeDelta delta = current_time - start_time_;
if (delta >= duration_) {
timer_.Stop();
}
double fraction = delta / duration_;
fraction = std::clamp(fraction, 0.0, 1.0);
double progress = timing_function_->GetValue(fraction);
callback_.Run(progress);
}
OverlayScrollbarAnimatorMac::OverlayScrollbarAnimatorMac(
Client* client,
int thumb_width_expanded,
int thumb_width_unexpanded,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: client_(client),
thumb_width_expanded_(thumb_width_expanded),
thumb_width_unexpanded_(thumb_width_unexpanded),
thumb_width_(thumb_width_unexpanded),
animations_enabled_(
base::FeatureList::IsEnabled(features::kScrollbarAnimations)),
task_runner_(task_runner),
weak_factory_(this) {}
OverlayScrollbarAnimatorMac::~OverlayScrollbarAnimatorMac() = default;
void OverlayScrollbarAnimatorMac::MouseDidEnter() {
if (thumb_alpha_ == 0.f) {
return;
}
if (mouse_in_track_) {
return;
}
mouse_in_track_ = true;
if (fade_out_animation_) {
FadeOutAnimationCancel();
}
FadeOutTimerUpdate();
if (!fade_in_track_animation_ && track_alpha_ != 1.f) {
FadeInTrackAnimationStart();
}
if (!expand_thumb_animation_ && thumb_width_ != thumb_width_expanded_) {
ExpandThumbAnimationStart();
}
}
void OverlayScrollbarAnimatorMac::MouseDidExit() {
mouse_in_track_ = false;
FadeOutTimerUpdate();
}
void OverlayScrollbarAnimatorMac::DidScroll() {
if (fade_out_animation_) {
FadeOutAnimationCancel();
FadeOutTimerUpdate();
return;
}
if (thumb_alpha_ == 1.f) {
FadeOutTimerUpdate();
return;
}
DCHECK_EQ(thumb_width_, thumb_width_unexpanded_);
DCHECK_EQ(thumb_alpha_, 0.f);
DCHECK(!fade_in_track_animation_);
thumb_width_ = thumb_width_unexpanded_;
thumb_alpha_ = 1;
client_->SetThumbNeedsDisplay();
client_->SetHidden(false);
if (client_->IsMouseInScrollbarFrameRect()) {
mouse_in_track_ = true;
thumb_width_ = thumb_width_expanded_;
track_alpha_ = 1;
client_->SetTrackNeedsDisplay();
}
FadeOutTimerUpdate();
}
void OverlayScrollbarAnimatorMac::ExpandThumbAnimationStart() {
DCHECK(!expand_thumb_animation_);
DCHECK_NE(thumb_width_, thumb_width_expanded_);
expand_thumb_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
base::BindRepeating(
&OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked,
weak_factory_.GetWeakPtr()),
animations_enabled_ ? kAnimationDuration : base::TimeDelta(),
task_runner_);
expand_thumb_animation_->Start();
}
void OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked(double progress) {
thumb_width_ = (1 - progress) * thumb_width_unexpanded_ +
progress * thumb_width_expanded_;
client_->SetThumbNeedsDisplay();
if (progress == 1) {
expand_thumb_animation_.reset();
}
}
void OverlayScrollbarAnimatorMac::FadeInTrackAnimationStart() {
DCHECK(!fade_in_track_animation_);
DCHECK(!fade_out_animation_);
fade_in_track_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
base::BindRepeating(
&OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked,
weak_factory_.GetWeakPtr()),
animations_enabled_ ? kAnimationDuration : base::TimeDelta(),
task_runner_);
fade_in_track_animation_->Start();
}
void OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked(double progress) {
DCHECK(!fade_out_animation_);
track_alpha_ = progress;
client_->SetTrackNeedsDisplay();
if (progress == 1) {
fade_in_track_animation_.reset();
}
}
void OverlayScrollbarAnimatorMac::FadeOutTimerUpdate() {
if (mouse_in_track_) {
start_scrollbar_fade_out_timer_.reset();
return;
}
if (!animations_enabled_ && thumb_width_ != thumb_width_expanded_) {
return;
}
if (!start_scrollbar_fade_out_timer_) {
start_scrollbar_fade_out_timer_ =
std::make_unique<base::RetainingOneShotTimer>(
FROM_HERE, animations_enabled_ ? kFadeOutDelay : base::TimeDelta(),
base::BindRepeating(
&OverlayScrollbarAnimatorMac::FadeOutAnimationStart,
weak_factory_.GetWeakPtr()));
start_scrollbar_fade_out_timer_->SetTaskRunner(task_runner_);
}
start_scrollbar_fade_out_timer_->Reset();
}
void OverlayScrollbarAnimatorMac::FadeOutAnimationStart() {
start_scrollbar_fade_out_timer_.reset();
fade_in_track_animation_.reset();
fade_out_animation_.reset();
fade_out_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
base::BindRepeating(&OverlayScrollbarAnimatorMac::FadeOutAnimationTicked,
weak_factory_.GetWeakPtr()),
animations_enabled_ ? kAnimationDuration : base::TimeDelta(),
task_runner_);
fade_out_animation_->Start();
}
void OverlayScrollbarAnimatorMac::FadeOutAnimationTicked(double progress) {
DCHECK(!fade_in_track_animation_);
thumb_alpha_ = 1 - progress;
client_->SetThumbNeedsDisplay();
if (track_alpha_ != 0) {
track_alpha_ = 1 - progress;
client_->SetTrackNeedsDisplay();
}
if (progress == 1) {
expand_thumb_animation_.reset();
fade_out_animation_.reset();
thumb_width_ = thumb_width_unexpanded_;
DCHECK_EQ(thumb_alpha_, 0.f);
DCHECK_EQ(track_alpha_, 0.f);
client_->SetHidden(true);
}
}
void OverlayScrollbarAnimatorMac::FadeOutAnimationCancel() {
DCHECK(fade_out_animation_);
fade_out_animation_.reset();
thumb_alpha_ = 1;
client_->SetThumbNeedsDisplay();
if (track_alpha_ > 0) {
track_alpha_ = 1;
client_->SetTrackNeedsDisplay();
}
}
}