From e7c9c373c3783fb7b35eb40f182ad8f2d6c2877d Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Wed, 30 Oct 2024 16:02:00 +0000 Subject: [PATCH] Added smooth changing progress bar --- .../StevesUI/SmoothChangingProgressBar.cpp | 62 +++++++++++++++++++ .../StevesUI/SmoothChangingProgressBar.h | 54 ++++++++++++++++ doc/RichTextInputDecorator.md | 3 +- doc/SmoothChangingProgress.md | 19 ++++++ doc/Widgets.md | 4 ++ 5 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 Source/StevesUEHelpers/Private/StevesUI/SmoothChangingProgressBar.cpp create mode 100644 Source/StevesUEHelpers/Public/StevesUI/SmoothChangingProgressBar.h create mode 100644 doc/SmoothChangingProgress.md diff --git a/Source/StevesUEHelpers/Private/StevesUI/SmoothChangingProgressBar.cpp b/Source/StevesUEHelpers/Private/StevesUI/SmoothChangingProgressBar.cpp new file mode 100644 index 0000000..423d511 --- /dev/null +++ b/Source/StevesUEHelpers/Private/StevesUI/SmoothChangingProgressBar.cpp @@ -0,0 +1,62 @@ +#include "StevesUI/SmoothChangingProgressBar.h" + +void USmoothChangingProgressBar::SetPercentSmoothly(float InPercent) +{ + UnregisterTimer(); + + TargetPercent = InPercent; + const float Curr = GetPercent(); + if (!FMath::IsNearlyEqual(Curr, TargetPercent)) + { + if (FMath::IsNearlyZero(PercentChangeSpeed) || !MyProgressBar.IsValid()) + { + SetPercent(InPercent); + } + else + { + SmoothChangeHandle = MyProgressBar->RegisterActiveTimer( + PercentChangeFrequency, + FWidgetActiveTimerDelegate::CreateUObject(this, &USmoothChangingProgressBar::TickPercent)); + } + } +} + +void USmoothChangingProgressBar::StopSmoothPercentChange() +{ + UnregisterTimer(); +} + +void USmoothChangingProgressBar::BeginDestroy() +{ + Super::BeginDestroy(); + + UnregisterTimer(); +} + +void USmoothChangingProgressBar::UnregisterTimer() +{ + if (SmoothChangeHandle.IsValid() && MyProgressBar.IsValid()) + { + MyProgressBar->UnRegisterActiveTimer(SmoothChangeHandle.Pin().ToSharedRef()); + SmoothChangeHandle.Reset(); + } +} + +EActiveTimerReturnType USmoothChangingProgressBar::TickPercent(double CurrTime, float DeltaTime) +{ + const float CurrPercent = GetPercent(); + const float Direction = FMath::Sign(TargetPercent - CurrPercent); + const float Change = DeltaTime * Direction * PercentChangeSpeed; + + const float NewPercent = Direction > 0 + ? FMath::Min(CurrPercent + Change, TargetPercent) + : FMath::Max(CurrPercent + Change, TargetPercent); + SetPercent(NewPercent); + + // Stop this if reached target (will unregister itself) + if (FMath::IsNearlyEqual(TargetPercent, GetPercent())) + { + return EActiveTimerReturnType::Stop; + } + return EActiveTimerReturnType::Continue; +} diff --git a/Source/StevesUEHelpers/Public/StevesUI/SmoothChangingProgressBar.h b/Source/StevesUEHelpers/Public/StevesUI/SmoothChangingProgressBar.h new file mode 100644 index 0000000..06867bc --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesUI/SmoothChangingProgressBar.h @@ -0,0 +1,54 @@ +// + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ProgressBar.h" +#include "SmoothChangingProgressBar.generated.h" + +/** + * A specialised progress bar that can be told to change its percent smoothly instead of all at once. + * Note: Because SetPercent isn't virtual on UProgressBar, you need to use the alternate SetPercentSmoothly + * function instead, and call StopSmoothPercentChange to interrupt it if you need to manually set it using + * SetPercent. + */ +UCLASS() +class STEVESUEHELPERS_API USmoothChangingProgressBar : public UProgressBar +{ + GENERATED_BODY() + +protected: + TWeakPtr SmoothChangeHandle; + + float TargetPercent; + + void UnregisterTimer(); + EActiveTimerReturnType TickPercent(double CurrTime, float DeltaTime); +public: + /// The speed at which the progress bar changes. This value means the max percentage changes + /// in one second. Set this to 0 to make changes instant. Changes to this value only affect + /// the next call to SetPercentSmoothly. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Progress") + float PercentChangeSpeed = 1.0f; + + /// The frequency at which we should update the bar. Set this to 0 to update every frame, + /// or > 0 to update every X seconds (useful to save tick time for slow updates). + /// Changes to this value only affect the next call to SetPercentSmoothly. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Progress") + float PercentChangeFrequency = 0.0f; + + /// Changes the bar percentage smoothly from its current value. + /// Automatically interrupts any existing smooth change. + UFUNCTION(BlueprintCallable, Category="Progress") + void SetPercentSmoothly(float InPercent); + + /// Stop any pending smooth changes to percent + /// Call this if you need to interrupt any current smooth change and + UFUNCTION(BlueprintCallable, Category="Progress") + void StopSmoothPercentChange(); + + virtual void BeginDestroy() override; + + + +}; diff --git a/doc/RichTextInputDecorator.md b/doc/RichTextInputDecorator.md index af689a7..51a8677 100644 --- a/doc/RichTextInputDecorator.md +++ b/doc/RichTextInputDecorator.md @@ -107,4 +107,5 @@ Alternatives are: ## See Also - * [Input Image](InputImage.md) \ No newline at end of file + * [Input Image](InputImage.md) + * [Widgets](Widgets.md) \ No newline at end of file diff --git a/doc/SmoothChangingProgress.md b/doc/SmoothChangingProgress.md new file mode 100644 index 0000000..1bd5b9b --- /dev/null +++ b/doc/SmoothChangingProgress.md @@ -0,0 +1,19 @@ +# Smooth Changing Progress Bar + +This is a fairly simple extension to Progress Bar to allow it to change smoothly +rather than jumping. + +You configure this as follows: + +* Set `PercentChangeSpeed` to the amount of percent change per second +* Optionally set `PercentChangeFrequency`; at 0, it updates every frame, otherwise + it can update less frequently by setting this to a number of seconds +* Call `SetPercentSmoothly` instead of `SetPercent` + * (`SetPercent` is not virtual in `UProgressBar` so we cannot override that. This + also means that if you want to interrupt the smooth change, you need to call + `StopSmoothPercentChange`) + + +## See Also + + * [Widgets](Widgets.md) \ No newline at end of file diff --git a/doc/Widgets.md b/doc/Widgets.md index 72c23ff..5e25d81 100644 --- a/doc/Widgets.md +++ b/doc/Widgets.md @@ -66,3 +66,7 @@ Several custom widgets are supplied to assist with some common challenges: and also remembers the last focus widget if you switch away & back without destroying it. +* [Smooth Changing Progress Bar](SmoothChangingProgress.md) + + A ProgressBar subclass which adds the ability to smoothly change rather than + jumping straight to the target value. \ No newline at end of file