feat(ui/progressbar): edge (linha + glow) que acompanha PrimaryLevel + auto-hide nos extremos

Novo material UI_M_ProgressBar_Edge: linha vertical sharp no lado direito
do Fill (U = PrimaryLevel) com halo lateral configuravel.

Params:
- EdgeColour: cor + alpha da linha (default branco-creme 0.85)
- EdgeWidth: largura da linha em UV
- EdgeGlowWidth: largura do halo lateral (0 = sem glow)
- EdgeGlowIntensity: forca do halo (0..1)
- PrimaryLevel: posicao (sincronizada pelo C++)

WBP UI_ProgressBar_Master ganha EdgeImage entre Fill e Shimmer (Overlay).

C++ UUIProgressBar_Base:
- BindWidgetOptional EdgeImage + EdgeMID + UPROPERTYs override
- 5 setters runtime (Visible, Colour, Width, GlowWidth, GlowIntensity)
- ApplyPrimaryToMID propaga PrimaryLevel pro EdgeMID em cada tick de animacao
- UpdateEdgeVisibility() helper auto-oculta Edge quando PrimaryLevel <= 0
  ou >= 1 (linha encostaria na borda)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 21:47:54 -03:00
parent 8843ac3d1c
commit 88eb20eefa
4 changed files with 142 additions and 1 deletions

View File

@@ -36,6 +36,12 @@ namespace
static const FName P_ShimmerSpeed("ShimmerSpeed");
static const FName P_ShimmerSoftness("ShimmerSoftness");
// Edge material params
static const FName P_EdgeColour("EdgeColour");
static const FName P_EdgeWidth("EdgeWidth");
static const FName P_EdgeGlowWidth("EdgeGlowWidth");
static const FName P_EdgeGlowIntensity("EdgeGlowIntensity");
FORCEINLINE float EaseOutCubic(float Alpha)
{
return 1.f - FMath::Pow(1.f - Alpha, 3.f);
@@ -279,6 +285,40 @@ void UUIProgressBar_Base::SetGridLineThickness(float Thickness)
if (TrackGridMID) TrackGridMID->SetScalarParameterValue(P_GridLineThickness, GridLineThickness);
}
void UUIProgressBar_Base::SetEdgeVisible(bool bVisible)
{
bShowEdge = bVisible;
UpdateEdgeVisibility();
}
void UUIProgressBar_Base::SetEdgeColour(FLinearColor Color)
{
EdgeColour = Color;
bOverrideEdgeColour = true;
if (EdgeMID) EdgeMID->SetVectorParameterValue(P_EdgeColour, Color);
}
void UUIProgressBar_Base::SetEdgeWidth(float Width01)
{
EdgeWidth = FMath::Clamp(Width01, 0.001f, 0.1f);
bOverrideEdgeWidth = true;
if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeWidth, EdgeWidth);
}
void UUIProgressBar_Base::SetEdgeGlowWidth(float GlowWidth01)
{
EdgeGlowWidth = FMath::Clamp(GlowWidth01, 0.f, 0.2f);
bOverrideEdgeGlowWidth = true;
if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeGlowWidth, EdgeGlowWidth);
}
void UUIProgressBar_Base::SetEdgeGlowIntensity(float Intensity01)
{
EdgeGlowIntensity = FMath::Clamp(Intensity01, 0.f, 1.f);
bOverrideEdgeGlowIntensity = true;
if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeGlowIntensity, EdgeGlowIntensity);
}
void UUIProgressBar_Base::SetShimmerVisible(bool bVisible)
{
bShowShimmer = bVisible;
@@ -394,6 +434,15 @@ void UUIProgressBar_Base::EnsureMIDs()
ApplyBrushStyle();
}
}
if (EdgeImage && !EdgeMID)
{
if (UMaterialInterface* Mat = Cast<UMaterialInterface>(EdgeImage->GetBrush().GetResourceObject()))
{
EdgeMID = UMaterialInstanceDynamic::Create(Mat, this);
EdgeImage->SetBrushFromMaterial(EdgeMID);
ApplyBrushStyle();
}
}
}
void UUIProgressBar_Base::ApplyMaterialOverrides()
@@ -426,12 +475,20 @@ void UUIProgressBar_Base::ApplyMaterialOverrides()
if (bOverrideShimmerSpeed) ShimmerMID->SetScalarParameterValue(P_ShimmerSpeed, ShimmerSpeed);
if (bOverrideShimmerSoftness) ShimmerMID->SetScalarParameterValue(P_ShimmerSoftness, ShimmerSoftness);
}
if (EdgeMID)
{
if (bOverrideEdgeColour) EdgeMID->SetVectorParameterValue(P_EdgeColour, EdgeColour);
if (bOverrideEdgeWidth) EdgeMID->SetScalarParameterValue(P_EdgeWidth, EdgeWidth);
if (bOverrideEdgeGlowWidth) EdgeMID->SetScalarParameterValue(P_EdgeGlowWidth, EdgeGlowWidth);
if (bOverrideEdgeGlowIntensity) EdgeMID->SetScalarParameterValue(P_EdgeGlowIntensity, EdgeGlowIntensity);
}
// Visibilidade do TrackGrid + Shimmer acompanham os toggles
// Visibilidade dos widgets opcionais
if (TrackGridImage)
{
TrackGridImage->SetVisibility(bShowTrackGrid ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed);
}
UpdateEdgeVisibility();
if (ShimmerImage)
{
ShimmerImage->SetVisibility(bShowShimmer ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed);
@@ -478,6 +535,7 @@ void UUIProgressBar_Base::ApplyBrushStyle()
Apply(BackgroundImage, /*bAllowBorder=*/true, 0.f);
Apply(TrackGridImage, /*bAllowBorder=*/false, 0.f);
Apply(FillImage, /*bAllowBorder=*/false, 0.f);
Apply(EdgeImage, /*bAllowBorder=*/false, 0.f);
Apply(ShimmerImage, /*bAllowBorder=*/false, InsetForShimmer);
if (ShimmerImage)
@@ -495,6 +553,28 @@ void UUIProgressBar_Base::ApplyPrimaryToMID(float Value)
{
FillMID->SetScalarParameterValue(P_PrimaryLevel, Value);
}
// Edge acompanha o nível Primary (posição da linha = PrimaryLevel)
if (EdgeMID)
{
EdgeMID->SetScalarParameterValue(P_PrimaryLevel, Value);
}
UpdateEdgeVisibility();
}
void UUIProgressBar_Base::UpdateEdgeVisibility()
{
if (!EdgeImage)
{
return;
}
if (!bShowEdge)
{
EdgeImage->SetVisibility(ESlateVisibility::Collapsed);
return;
}
// Auto-hide quando Primary está nos extremos (0 ou 1) — Edge encostaria na borda.
const bool bAtExtreme = (CurrentPrimaryLevel <= 0.f) || (CurrentPrimaryLevel >= 1.f);
EdgeImage->SetVisibility(bAtExtreme ? ESlateVisibility::Collapsed : ESlateVisibility::HitTestInvisible);
}
void UUIProgressBar_Base::ApplySecondaryToMID(float Value)

View File

@@ -164,6 +164,45 @@ public:
meta = (EditCondition = "bOverrideGridLineThickness", ClampMin = "0.001", ClampMax = "0.2"))
float GridLineThickness = 0.02f;
// === Edge (linha vertical no fim do Primary) ===
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge")
bool bShowEdge = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (InlineEditConditionToggle))
bool bOverrideEdgeColour = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (EditCondition = "bOverrideEdgeColour"))
FLinearColor EdgeColour = FLinearColor(1.f, 0.94f, 0.78f, 0.85f);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (InlineEditConditionToggle))
bool bOverrideEdgeWidth = false;
/** Largura da linha em UV (fração da barra). 0.005 ≈ 2px em barra de 400px. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (EditCondition = "bOverrideEdgeWidth", ClampMin = "0.001", ClampMax = "0.1"))
float EdgeWidth = 0.005f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (InlineEditConditionToggle))
bool bOverrideEdgeGlowWidth = false;
/** Largura do halo lateral em UV. 0 = sem glow. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (EditCondition = "bOverrideEdgeGlowWidth", ClampMin = "0", ClampMax = "0.2"))
float EdgeGlowWidth = 0.02f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (InlineEditConditionToggle))
bool bOverrideEdgeGlowIntensity = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge",
meta = (EditCondition = "bOverrideEdgeGlowIntensity", ClampMin = "0", ClampMax = "1"))
float EdgeGlowIntensity = 0.5f;
// === Shimmer (faixa de luz animada por cima do Fill) ===
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer")
@@ -292,6 +331,21 @@ public:
UFUNCTION(BlueprintCallable, Category = "ProgressBar|TrackGrid")
void SetGridLineThickness(float Thickness);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge")
void SetEdgeVisible(bool bVisible);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge")
void SetEdgeColour(FLinearColor Color);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge")
void SetEdgeWidth(float Width01);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge")
void SetEdgeGlowWidth(float GlowWidth01);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge")
void SetEdgeGlowIntensity(float Intensity01);
UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer")
void SetShimmerVisible(bool bVisible);
@@ -341,6 +395,9 @@ protected:
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UImage> TrackGridImage;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UImage> EdgeImage;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UImage> ShimmerImage;
@@ -354,6 +411,9 @@ private:
UPROPERTY(Transient)
TObjectPtr<UMaterialInstanceDynamic> TrackGridMID;
UPROPERTY(Transient)
TObjectPtr<UMaterialInstanceDynamic> EdgeMID;
UPROPERTY(Transient)
TObjectPtr<UMaterialInstanceDynamic> ShimmerMID;
@@ -362,6 +422,7 @@ private:
void ApplySecondaryToMID(float Value);
void ApplyMaterialOverrides();
void ApplyBrushStyle();
void UpdateEdgeVisibility();
float CurrentPrimaryLevel = 1.f;
float TargetPrimaryLevel = 1.f;