// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/navigation_transitions/progress_bar.h"
#include "ui/android/progress_bar_config.h"
namespace content {
ProgressBar::ProgressBar(int width_physical,
const ui::ProgressBarConfig& config)
: width_physical_(width_physical),
height_physical_(config.height_physical),
background_layer_(cc::slim::SolidColorLayer::Create()),
first_pulse_layer_(cc::slim::SolidColorLayer::Create()),
second_pulse_layer_(cc::slim::SolidColorLayer::Create()),
hairline_layer_(cc::slim::SolidColorLayer::Create()) {
background_layer_->SetBackgroundColor(config.background_color);
background_layer_->SetIsDrawable(true);
background_layer_->SetBounds(
gfx::Size(width_physical_, config.height_physical));
first_pulse_layer_->SetBackgroundColor(config.color);
first_pulse_layer_->SetIsDrawable(true);
background_layer_->AddChild(first_pulse_layer_);
second_pulse_layer_->SetBackgroundColor(config.color);
second_pulse_layer_->SetIsDrawable(true);
background_layer_->AddChild(second_pulse_layer_);
hairline_layer_->SetBackgroundColor(config.hairline_color);
hairline_layer_->SetIsDrawable(true);
hairline_layer_->SetBounds(
gfx::Size(width_physical_, config.hairline_height_physical));
hairline_layer_->SetPosition(gfx::PointF(0, config.height_physical));
SetupAnimation();
}
ProgressBar::~ProgressBar() = default;
scoped_refptr<cc::slim::Layer> ProgressBar::GetLayer() const {
return background_layer_;
}
void ProgressBar::Animate(base::TimeTicks frame_begin_time) {
CHECK(!effect_.keyframe_models().empty());
effect_.Tick(frame_begin_time);
// The first pulse fires off at the beginning of the animation.
float left =
width_physical_ * static_cast<float>(pow(current_value_, 1.5f) - 0.5f);
float right = width_physical_ * current_value_;
// TODO(bokan/khushalsagar): This needs to account for RTL.
first_pulse_layer_->SetBounds(gfx::Size(right - left, height_physical_));
first_pulse_layer_->SetPosition(gfx::PointF(left, 0));
// The second pulse fires off at some point after the first pulse has been
// fired.
constexpr float kSecondPulseStart = 1.1f;
constexpr float kSecondPulseLength = 1.0f;
if (current_value_ >= kSecondPulseStart) {
float percentage =
(current_value_ - kSecondPulseStart) / kSecondPulseLength;
left = width_physical_ * static_cast<float>(pow(percentage, 2.5f) - 0.1f);
right = width_physical_ * percentage;
second_pulse_layer_->SetBounds(gfx::Size(right - left, height_physical_));
second_pulse_layer_->SetPosition(gfx::PointF(left, 0));
} else {
second_pulse_layer_->SetBounds(gfx::Size(0, 0));
}
}
void ProgressBar::SetupAnimation() {
constexpr float kStartValue = 0.f;
constexpr float kEndValue = 3.f;
constexpr base::TimeDelta kDuration = base::Milliseconds(3000);
auto curve = gfx::KeyframedFloatAnimationCurve::Create();
curve->AddKeyframe(gfx::FloatKeyframe::Create(/*time=*/base::TimeDelta(),
/*value=*/kStartValue,
/*timing_function=*/nullptr));
curve->AddKeyframe(gfx::FloatKeyframe::Create(/*time=*/kDuration,
/*value=*/kEndValue,
/*timing_function=*/nullptr));
curve->set_target(this);
auto model = gfx::KeyframeModel::Create(
/*curve=*/std::move(curve),
/*keyframe_model_id=*/effect_.GetNextKeyframeModelId(),
/*target_property_id=*/1);
model->set_iterations(std::numeric_limits<double>::infinity());
effect_.AddKeyframeModel(std::move(model));
}
void ProgressBar::OnFloatAnimated(const float& value,
int target_property_id,
gfx::KeyframeModel* keyframe_model) {
current_value_ = value;
}
} // namespace content