Files
UnrealEngine/Engine/Source/Runtime/AdvancedWidgets/Private/Slate/SRadialSlider.cpp
2025-05-18 13:04:45 +08:00

840 lines
26 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/SRadialSlider.h"
#include "Brushes/SlateColorBrush.h"
#include "Fonts/FontMeasure.h"
#include "Fonts/SlateFontInfo.h"
#include "Framework/Application/SlateApplication.h"
#include "Rendering/DrawElements.h"
#if WITH_ACCESSIBILITY
#include "Widgets/Accessibility/SlateAccessibleWidgets.h"
#endif
SRadialSlider::SRadialSlider()
{
#if WITH_ACCESSIBILITY
AccessibleBehavior = EAccessibleBehavior::Summary;
bCanChildrenBeAccessible = false;
#endif
}
SRadialSlider::~SRadialSlider() = default;
void SRadialSlider::Construct( const SRadialSlider::FArguments& InDeclaration )
{
check(InDeclaration._Style);
Style = InDeclaration._Style;
bMouseUsesStep = InDeclaration._MouseUsesStep;
bRequiresControllerLock = InDeclaration._RequiresControllerLock;
LockedAttribute = InDeclaration._Locked;
StepSize = InDeclaration._StepSize;
ValueAttribute = InDeclaration._Value;
bUseCustomDefaultValue = InDeclaration._bUseCustomDefaultValue;
CustomDefaultValue = InDeclaration._CustomDefaultValue;
SliderRange = InDeclaration._SliderRange;
SliderHandleStartAngle = InDeclaration._SliderHandleStartAngle;
SliderHandleEndAngle = InDeclaration._SliderHandleEndAngle;
AngularOffset = InDeclaration._AngularOffset;
HandStartEndRatio = InDeclaration._HandStartEndRatio;
SliderBarColor = InDeclaration._SliderBarColor;
SliderProgressColor = InDeclaration._SliderProgressColor;
SliderHandleColor = InDeclaration._SliderHandleColor;
CenterBackgroundColor = InDeclaration._CenterBackgroundColor;
CenterBackgroundBrush = InDeclaration._CenterBackgroundBrush;
bIsFocusable = InDeclaration._IsFocusable;
bUseVerticalDrag = InDeclaration._UseVerticalDrag;
bShowSliderHandle = InDeclaration._ShowSliderHandle;
bShowSliderHand = InDeclaration._ShowSliderHand;
OnMouseCaptureBegin = InDeclaration._OnMouseCaptureBegin;
OnMouseCaptureEnd = InDeclaration._OnMouseCaptureEnd;
OnControllerCaptureBegin = InDeclaration._OnControllerCaptureBegin;
OnControllerCaptureEnd = InDeclaration._OnControllerCaptureEnd;
OnValueChanged = InDeclaration._OnValueChanged;
bControllerInputCaptured = false;
bIsUsingFineTune = false;
FineTuneKey = EKeys::LeftShift;
}
int32 SRadialSlider::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
const float AllottedWidth = AllottedGeometry.GetLocalSize().X;
const float AllottedHeight = AllottedGeometry.GetLocalSize().Y;
FGeometry SliderGeometry = AllottedGeometry;
const bool bEnabled = ShouldBeEnabled(bParentEnabled);
const ESlateDrawEffect DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
// Draw radial slider bar
const FVector2D HandleSize = GetThumbImage()->ImageSize;
const FVector2D HalfHandleSize = 0.5f * HandleSize;
const float SliderRadius = FMath::Min(AllottedWidth, AllottedHeight) * 0.5f - HalfHandleSize.Y;
const FVector2D StartPoint(0.0f, SliderRadius);
const float MidPointAngle = FMath::Lerp(SliderHandleStartAngle, SliderHandleEndAngle, GetNormalizedSliderHandlePosition());
const FVector2D HandleLocation = StartPoint.GetRotated(MidPointAngle + AngularOffset);
TArray<FVector2D> SliderBarPoints;
TArray<FVector2D> ProgressBarPoints;
const float NormalizedCustomDefaultValue = bUseCustomDefaultValue.Get() ? GetNormalizedValue(CustomDefaultValue.Get()) : GetMinValue();
const bool bIsCCW = GetNormalizedSliderHandlePosition() < NormalizedCustomDefaultValue;
const float CustomDefaultValueAngle = FMath::Lerp(SliderHandleStartAngle, SliderHandleEndAngle, NormalizedCustomDefaultValue);
const FVector2D CustomDefaultValueLocation = StartPoint.GetRotated(CustomDefaultValueAngle + AngularOffset);
ProgressBarPoints.Emplace(bIsCCW ? HandleLocation : CustomDefaultValueLocation);
static const int32 CircleResolution = 100;
for (int32 i = 0; i <= CircleResolution; i++)
{
const float CircleAnglePercent = float(i) / float(CircleResolution);
const float CurrentPointAngle = FMath::Lerp(SliderHandleStartAngle, SliderHandleEndAngle, CircleAnglePercent);
const FVector2D NewCirclePoint = StartPoint.GetRotated(CurrentPointAngle + AngularOffset);
const bool bIsUniquePoint(i != CircleResolution);
AddSliderPointToArray(SliderBarPoints, bIsUniquePoint, NewCirclePoint);
// draw completed progress bar, given directionality
const bool bShouldDrawProgressbar = (
(bIsCCW && CircleAnglePercent >= GetNormalizedSliderHandlePosition() && CircleAnglePercent <= NormalizedCustomDefaultValue) ||
(!bIsCCW && CircleAnglePercent <= GetNormalizedSliderHandlePosition() && CircleAnglePercent >= NormalizedCustomDefaultValue)
);
if (bShouldDrawProgressbar)
{
AddSliderPointToArray(ProgressBarPoints, bIsUniquePoint, NewCirclePoint);
}
}
ProgressBarPoints.AddUnique(bIsCCW ? CustomDefaultValueLocation : HandleLocation);
const FVector2D SliderMidPoint(AllottedGeometry.GetLocalSize() * 0.5f);
const FVector2D SliderDiameter(SliderRadius * 2.0f);
// combine bar color alpha with widget alpha
FLinearColor SliderBarColorWithAlpha = SliderBarColor.Get().GetColor(InWidgetStyle);
SliderBarColorWithAlpha.A *= GetRenderOpacity();
FLinearColor ProgressBarColorWithAlpha = SliderProgressColor.Get().GetColor(InWidgetStyle);
ProgressBarColorWithAlpha.A *= GetRenderOpacity();
float BarThickness;
// For backwards compat, if Thickness isn't set, use Style->BarThickness
if (Thickness.Get().IsSet())
{
// Compensate for the widget size when passing screenspace thickness to MakeLines
// TODO: Compensate thickness based on zoom level
float ScaleFactor = FMath::Min(AllottedWidth, AllottedHeight) / 100.0f;
BarThickness = Thickness.Get().GetValue() * ScaleFactor;
}
else
{
BarThickness = Style->BarThickness;
}
// draw slider bar
FSlateDrawElement::MakeLines
(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(SliderDiameter, FSlateLayoutTransform(SliderMidPoint)),
SliderBarPoints,
DrawEffects,
SliderBarColorWithAlpha,
true,
BarThickness
);
// draw completed progress bar
FSlateDrawElement::MakeLines
(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(SliderDiameter, FSlateLayoutTransform(SliderMidPoint)),
ProgressBarPoints,
DrawEffects,
ProgressBarColorWithAlpha,
true,
BarThickness
);
// draw center background circle
FLinearColor CenterBackgroundColorWithAlpha = CenterBackgroundColor.Get().GetColor(InWidgetStyle);
CenterBackgroundColorWithAlpha.A *= GetRenderOpacity();
FSlateDrawElement::MakeBox
(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(SliderDiameter, FSlateLayoutTransform(SliderMidPoint - SliderRadius)),
&CenterBackgroundBrush,
DrawEffects,
CenterBackgroundColorWithAlpha
);
++LayerId;
// Draw hand from middle to handle
if (bShowSliderHand)
{
const FVector2D HandStart(0.0f, SliderRadius * HandStartEndRatio.X);
const FVector2D HandEnd(0.0f, SliderRadius * HandStartEndRatio.Y);
const FVector2D HandStartRotated = HandStart.GetRotated(MidPointAngle + AngularOffset);
const FVector2D HandEndRotated = HandEnd.GetRotated(MidPointAngle + AngularOffset);
FSlateDrawElement::MakeLines
(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(SliderDiameter, FSlateLayoutTransform(SliderMidPoint)),
TArray<FVector2D>{HandStartRotated, HandEndRotated},
DrawEffects,
SliderBarColorWithAlpha,
true,
BarThickness
);
++LayerId;
}
TArray<float> DrawnTagValues;
for (int32 TagIndex = 0; TagIndex < ValueTags.Num(); TagIndex++)
{
const float CandidateValue = ValueTags[TagIndex];
if (CandidateValue < GetMinValue() || CandidateValue > GetMaxValue())
{
// Do not draw tags for values that are outside of the slider range
continue;
}
else if (DrawnTagValues.Find(CandidateValue) != INDEX_NONE) // Ignore duplicated tags
{
continue;
}
else
{
DrawnTagValues.Emplace(CandidateValue);
}
const float NormalizedCandidateTagPosition = GetNormalizedValue(CandidateValue);
static const float TextSpacing(5.0f);
const float TagAngle = FMath::Lerp(SliderHandleStartAngle, SliderHandleEndAngle, NormalizedCandidateTagPosition);
FVector2D LineStartLocation(FVector2D(StartPoint.X, StartPoint.Y));
FVector2D LineEndLocation(FVector2D(StartPoint.X, StartPoint.Y + TextSpacing));
LineStartLocation = LineStartLocation.GetRotated(TagAngle + AngularOffset);
LineEndLocation = LineEndLocation.GetRotated(TagAngle + AngularOffset);
TArray<FVector2D> TagLinePoints;
TagLinePoints.Emplace(LineStartLocation);
TagLinePoints.Emplace(LineEndLocation);
const bool bIsTagLineInProgressBarRange = (
(bIsCCW && NormalizedCandidateTagPosition >= GetNormalizedSliderHandlePosition() && NormalizedCandidateTagPosition <= NormalizedCustomDefaultValue) ||
(!bIsCCW && NormalizedCandidateTagPosition <= GetNormalizedSliderHandlePosition() && NormalizedCandidateTagPosition >= NormalizedCustomDefaultValue)
);
FLinearColor TagLineColor = bIsTagLineInProgressBarRange ? ProgressBarColorWithAlpha : SliderBarColorWithAlpha;
// draw tag line
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(SliderDiameter, FSlateLayoutTransform(SliderMidPoint)),
TagLinePoints,
DrawEffects,
TagLineColor,
true,
BarThickness
);
const FText TextToWrite = FText::FromString(FString::SanitizeFloat(CandidateValue));
const TSharedRef<FSlateFontMeasure> FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
FSlateFontInfo MyFont = FCoreStyle::GetDefaultFontStyle("Regular", 10);
const FVector2D TextDrawSize = FontMeasureService->Measure(TextToWrite, MyFont);
FVector2D TextLocation(FVector2D(StartPoint.X, StartPoint.Y + TextSpacing + 10.0f));
TextLocation = TextLocation.GetRotated(TagAngle + AngularOffset);
const FVector2D TextTopLeftPoint = TextLocation + (AllottedGeometry.GetLocalSize() * 0.5f) - TextDrawSize * 0.5f;
// draw tag value
FSlateDrawElement::MakeText(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(GetThumbImage()->ImageSize, FSlateLayoutTransform(TextTopLeftPoint)),
TextToWrite,
MyFont,
DrawEffects,
FLinearColor::White
);
}
// Draw slider handle (thumb)
if (bShowSliderHandle)
{
const FVector2D HandleTopLeftPoint = HandleLocation + (AllottedGeometry.GetLocalSize() * 0.5f) - HalfHandleSize;
auto ThumbImage = GetThumbImage();
FSlateDrawElement::MakeRotatedBox(
OutDrawElements,
LayerId,
SliderGeometry.ToPaintGeometry(GetThumbImage()->ImageSize, FSlateLayoutTransform(HandleTopLeftPoint)),
ThumbImage,
DrawEffects,
(180.0f + MidPointAngle + AngularOffset) * (PI / 180.0f),
HalfHandleSize,
FSlateDrawElement::RelativeToElement,
ThumbImage->GetTint(InWidgetStyle) * SliderHandleColor.Get().GetColor(InWidgetStyle) * InWidgetStyle.GetColorAndOpacityTint()
);
}
++LayerId;
return LayerId;
}
void SRadialSlider::AddSliderPointToArray(TArray<FVector2D>& SliderPoints, const bool bIsUnique, const FVector2D& SliderPoint) const
{
if (bIsUnique)
{
SliderPoints.AddUnique(SliderPoint);
}
else
{
SliderPoints.Emplace(SliderPoint);
}
}
FVector2D SRadialSlider::ComputeDesiredSize( float ) const
{
return FVector2D(200.0f);
}
bool SRadialSlider::IsLocked() const
{
return LockedAttribute.Get();
}
bool SRadialSlider::IsInteractable() const
{
return IsEnabled() && !IsLocked() && SupportsKeyboardFocus();
}
bool SRadialSlider::SupportsKeyboardFocus() const
{
return bIsFocusable;
}
void SRadialSlider::ResetControllerState()
{
if (bControllerInputCaptured)
{
OnControllerCaptureEnd.ExecuteIfBound();
bControllerInputCaptured = false;
}
}
FNavigationReply SRadialSlider::OnNavigation(const FGeometry& MyGeometry, const FNavigationEvent& InNavigationEvent)
{
if (bControllerInputCaptured || !bRequiresControllerLock)
{
FNavigationReply Reply = FNavigationReply::Escape();
float NewValue = ValueAttribute.Get();
if (InNavigationEvent.GetNavigationType() == EUINavigation::Left)
{
NewValue -= StepSize.Get();
Reply = FNavigationReply::Stop();
}
else if (InNavigationEvent.GetNavigationType() == EUINavigation::Right)
{
NewValue += StepSize.Get();
Reply = FNavigationReply::Stop();
}
if (ValueAttribute.Get() != NewValue)
{
CommitValue(FMath::Clamp(NewValue, GetMinValue(), GetMaxValue()));
return Reply;
}
}
return SLeafWidget::OnNavigation(MyGeometry, InNavigationEvent);
}
FReply SRadialSlider::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
FReply Reply = FReply::Unhandled();
if (InKeyEvent.GetKey() == FineTuneKey)
{
bIsUsingFineTune = true;
}
if (IsInteractable())
{
// The controller's bottom face button must be pressed once to begin manipulating the slider's value.
// Navigation away from the widget is prevented until the button has been pressed again or focus is lost.
// The value can be manipulated by using the game pad's directional arrows ( relative to slider orientation ).
if (FSlateApplication::Get().GetNavigationActionFromKey(InKeyEvent) == EUINavigationAction::Accept && bRequiresControllerLock)
{
if (bControllerInputCaptured == false)
{
// Begin capturing controller input and allow user to modify the slider's value.
bControllerInputCaptured = true;
OnControllerCaptureBegin.ExecuteIfBound();
Reply = FReply::Handled();
}
else
{
ResetControllerState();
Reply = FReply::Handled();
}
}
else
{
Reply = SLeafWidget::OnKeyDown(MyGeometry, InKeyEvent);
}
}
else
{
Reply = SLeafWidget::OnKeyDown(MyGeometry, InKeyEvent);
}
return Reply;
}
FReply SRadialSlider::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
FReply Reply = FReply::Unhandled();
bIsUsingFineTune = false;
if (bControllerInputCaptured)
{
Reply = FReply::Handled();
}
return Reply;
}
void SRadialSlider::OnFocusLost(const FFocusEvent& InFocusEvent)
{
if (bControllerInputCaptured)
{
// Commit and reset state
CommitValue(ValueAttribute.Get());
ResetControllerState();
}
}
void SRadialSlider::OnInputStarted(const FGeometry& MyGeometry, const FVector2D& InputAbsolutePosition)
{
AbsoluteInputAngle = GetAngleFromPosition(MyGeometry, InputAbsolutePosition);
PreviousAbsolutePosition = InputAbsolutePosition;
}
FReply SRadialSlider::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ((MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) && !IsLocked())
{
CachedCursor = GetCursor().Get(EMouseCursor::Default);
OnInputStarted(MyGeometry, MouseEvent.GetLastScreenSpacePosition());
OnMouseCaptureBegin.ExecuteIfBound();
CommitValue(PositionToValue(MyGeometry, MouseEvent.GetLastScreenSpacePosition()));
// Release capture for controller/keyboard when switching to mouse.
ResetControllerState();
return FReply::Handled().CaptureMouse(SharedThis(this));
}
return FReply::Unhandled();
}
FReply SRadialSlider::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ((MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) && HasMouseCaptureByUser(MouseEvent.GetUserIndex(), MouseEvent.GetPointerIndex()))
{
SetCursor(CachedCursor);
OnMouseCaptureEnd.ExecuteIfBound();
// Release capture for controller/keyboard when switching to mouse.
ResetControllerState();
return FReply::Handled().ReleaseMouseCapture();
}
return FReply::Unhandled();
}
FReply SRadialSlider::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if (HasMouseCaptureByUser(MouseEvent.GetUserIndex(), MouseEvent.GetPointerIndex()) && !IsLocked())
{
SetCursor(EMouseCursor::GrabHandClosed);
CommitValue(PositionToValue(MyGeometry, MouseEvent.GetLastScreenSpacePosition()));
// Release capture for controller/keyboard when switching to mouse
ResetControllerState();
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SRadialSlider::OnTouchStarted(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent)
{
if (!IsLocked())
{
// Release capture for controller/keyboard when switching to mouse.
ResetControllerState();
PressedScreenSpaceTouchDownPosition = InTouchEvent.GetScreenSpacePosition();
OnInputStarted(MyGeometry, PressedScreenSpaceTouchDownPosition);
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SRadialSlider::OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent)
{
if (HasMouseCaptureByUser(InTouchEvent.GetUserIndex(), InTouchEvent.GetPointerIndex()))
{
CommitValue(PositionToValue(MyGeometry, InTouchEvent.GetScreenSpacePosition()));
// Release capture for controller/keyboard when switching to mouse
ResetControllerState();
return FReply::Handled();
}
else if (!HasMouseCapture())
{
if (FSlateApplication::Get().HasTraveledFarEnoughToTriggerDrag(InTouchEvent, PressedScreenSpaceTouchDownPosition, EOrientation::Orient_Horizontal))
{
CachedCursor = GetCursor().Get(EMouseCursor::Default);
OnMouseCaptureBegin.ExecuteIfBound();
CommitValue(PositionToValue(MyGeometry, InTouchEvent.GetScreenSpacePosition()));
// Release capture for controller/keyboard when switching to mouse
ResetControllerState();
return FReply::Handled().CaptureMouse(SharedThis(this));
}
}
return FReply::Unhandled();
}
FReply SRadialSlider::OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent)
{
if (HasMouseCaptureByUser(InTouchEvent.GetUserIndex(), InTouchEvent.GetPointerIndex()))
{
SetCursor(CachedCursor);
OnMouseCaptureEnd.ExecuteIfBound();
CommitValue(PositionToValue(MyGeometry, InTouchEvent.GetScreenSpacePosition()));
// Release capture for controller/keyboard when switching to mouse.
ResetControllerState();
return FReply::Handled().ReleaseMouseCapture();
}
return FReply::Unhandled();
}
void SRadialSlider::CommitValue(float NewValue)
{
ValueAttribute.Set(NewValue);
Invalidate(EInvalidateWidgetReason::Paint);
OnValueChanged.ExecuteIfBound(FMath::Clamp(NewValue, GetMinValue(), GetMaxValue()));
}
float SRadialSlider::GetAngleFromPosition(const FGeometry& MyGeometry, const FVector2D& AbsolutePosition)
{
const FVector2D LocalPosition = MyGeometry.AbsoluteToLocal(AbsolutePosition) - MyGeometry.GetLocalSize() * 0.5f;
const FVector2D InputDirection = LocalPosition.GetSafeNormal().GetRotated(90.0f - AngularOffset);
const float Angle = 180.0f + (180.0f / PI * FMath::Atan2(InputDirection.Y, InputDirection.X));
return Angle;
}
float SRadialSlider::PositionToValue(const FGeometry& MyGeometry, const FVector2D& AbsolutePosition)
{
float NewValue;
float CurveTime;
const FRichCurve* SliderRangeCurve = SliderRange.GetRichCurveConst();
float CurveMinTime;
float CurveMaxTime;
SliderRangeCurve->GetTimeRange(CurveMinTime, CurveMaxTime);
if (bUseVerticalDrag)
{
const float VerticalDragMouseSpeed = bIsUsingFineTune ? VerticalDragMouseSpeedFineTune : VerticalDragMouseSpeedNormal;
float ValueDelta = (float)(AbsolutePosition.Y - PreviousAbsolutePosition.Y) / VerticalDragPixelDelta * VerticalDragMouseSpeed;
PreviousAbsolutePosition = AbsolutePosition;
CurveTime = FMath::Clamp(ValueAttribute.Get() - ValueDelta, 0.0f, 1.0f);
}
else
{
const float OldAngle = GetAngleFromPosition(MyGeometry, PreviousAbsolutePosition);
const float NewAngle = GetAngleFromPosition(MyGeometry, AbsolutePosition);
float WrappedDelta = FMath::Abs(FMath::Fmod((NewAngle - OldAngle), 360.0f));
float AngularDelta = WrappedDelta > 180.0f ? 360.0f - WrappedDelta : WrappedDelta;
int32 Sign = (NewAngle - OldAngle >= 0.0f && NewAngle - OldAngle <= 180.0f) || (NewAngle - OldAngle <= -180 && NewAngle - OldAngle >= -360) ? 1 : -1;
AngularDelta *= Sign;
PreviousAbsolutePosition = AbsolutePosition;
AbsoluteInputAngle += AngularDelta;
CurveTime = FMath::GetMappedRangeValueClamped(FVector2D(SliderHandleStartAngle, SliderHandleEndAngle), FVector2D(CurveMinTime, CurveMaxTime), AbsoluteInputAngle);
}
NewValue = SliderRangeCurve->Eval(CurveTime);
if (bMouseUsesStep)
{
const float SteppedValue = FMath::RoundToInt(NewValue / StepSize.Get()) * StepSize.Get();
NewValue = SteppedValue;
}
return NewValue;
}
const FSlateBrush* SRadialSlider::GetBarImage() const
{
if (!IsEnabled() || LockedAttribute.Get())
{
return &Style->DisabledBarImage;
}
else if (IsHovered())
{
return &Style->HoveredBarImage;
}
else
{
return &Style->NormalBarImage;
}
}
const FSlateBrush* SRadialSlider::GetThumbImage() const
{
if (!IsEnabled() || LockedAttribute.Get())
{
return &Style->DisabledThumbImage;
}
else if (IsHovered())
{
return &Style->HoveredThumbImage;
}
else
{
return &Style->NormalThumbImage;
}
}
float SRadialSlider::GetMinValue() const
{
float MinValue = 0.0f;
const FRichCurve* SliderRangeCurve = SliderRange.GetRichCurveConst();
if (SliderRangeCurve->GetNumKeys() > 0)
{
MinValue = SliderRangeCurve->GetFirstKey().Value;
}
return MinValue;
}
float SRadialSlider::GetMaxValue() const
{
float MaxValue = 0.0f;
const FRichCurve* SliderRangeCurve = SliderRange.GetRichCurveConst();
if (SliderRangeCurve->GetNumKeys() > 0)
{
MaxValue = SliderRangeCurve->GetLastKey().Value;
}
return MaxValue;
}
float SRadialSlider::GetValue() const
{
return ValueAttribute.Get();
}
bool SRadialSlider::GetUseCustomDefaultValue() const
{
return bUseCustomDefaultValue.Get();
}
float SRadialSlider::GetCustomDefaultValue() const
{
return CustomDefaultValue.Get();
}
float SRadialSlider::GetNormalizedValue(float RawValue) const
{
float NormalizedValue(0.0f);
const TArray<FRichCurveKey>& SliderRangeCurveKeys = SliderRange.GetRichCurveConst()->GetConstRefOfKeys();
if (SliderRangeCurveKeys.Num() > 0)
{
float SliderPercentMin(0.0f);
float SliderPercentMax(1.0f);
float SampleValueMin(GetMinValue());
float SampleValueMax(GetMaxValue());
const float MinCurveTime = SliderRangeCurveKeys[0].Time;
const float MaxCurveTime = SliderRangeCurveKeys[SliderRangeCurveKeys.Num() -1].Time;
for (int32 i = 0; i < SliderRangeCurveKeys.Num(); i++)
{
if (RawValue < SliderRangeCurveKeys[i].Value)
{
SliderPercentMax = (SliderRangeCurveKeys[i].Time - MinCurveTime) / (MaxCurveTime - MinCurveTime);
SampleValueMax = SliderRangeCurveKeys[i].Value;
break;
}
SliderPercentMin = (SliderRangeCurveKeys[i].Time - MinCurveTime) / (MaxCurveTime - MinCurveTime);
SampleValueMin = SliderRangeCurveKeys[i].Value;
}
NormalizedValue = FMath::GetMappedRangeValueClamped(FVector2f(SampleValueMin, SampleValueMax), FVector2f(SliderPercentMin, SliderPercentMax), RawValue);
}
return NormalizedValue;
}
float SRadialSlider::GetNormalizedSliderHandlePosition() const
{
return GetNormalizedValue(ValueAttribute.Get());
}
void SRadialSlider::SetValue(const TAttribute<float>& InValueAttribute)
{
SetAttribute(ValueAttribute, InValueAttribute, EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetUseCustomDefaultValue(const TAttribute<bool>& InValueAttribute)
{
SetAttribute(bUseCustomDefaultValue, InValueAttribute, EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetCustomDefaultValue(const TAttribute<float>& InValueAttribute)
{
SetAttribute(CustomDefaultValue, InValueAttribute, EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetSliderHandleStartAngleAndSliderHandleEndAngle(float InSliderHandleStartAngle, float InSliderHandleEndAngle)
{
SliderHandleStartAngle = InSliderHandleStartAngle;
SliderHandleEndAngle = InSliderHandleEndAngle;
if (SliderHandleStartAngle > SliderHandleEndAngle)
{
SliderHandleEndAngle = SliderHandleStartAngle;
}
}
void SRadialSlider::SetHandStartEndRatio(FVector2D InHandStartEndRatio)
{
const FVector2D ClampedRatio = InHandStartEndRatio.ClampAxes(0.0f, 1.0f);
if (ClampedRatio.X > ClampedRatio.Y)
{
HandStartEndRatio = FVector2D(ClampedRatio.X);
}
else
{
HandStartEndRatio = ClampedRatio;
}
}
void SRadialSlider::SetLocked(const TAttribute<bool>& InLocked)
{
SetAttribute(LockedAttribute, InLocked, EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetSliderBarColor(FSlateColor InSliderBarColor)
{
SetAttribute(SliderBarColor, TAttribute<FSlateColor>(InSliderBarColor), EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetSliderProgressColor(FSlateColor InSliderProgressColor)
{
SetAttribute(SliderProgressColor, TAttribute<FSlateColor>(InSliderProgressColor), EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetSliderHandleColor(FSlateColor InSliderHandleColor)
{
SetAttribute(SliderHandleColor, TAttribute<FSlateColor>(InSliderHandleColor), EInvalidateWidgetReason::Paint);
}
void SRadialSlider::SetCenterBackgroundColor(FSlateColor InCenterBackgroundColor)
{
SetAttribute(CenterBackgroundColor, TAttribute<FSlateColor>(InCenterBackgroundColor), EInvalidateWidgetReason::Paint);
}
float SRadialSlider::GetStepSize() const
{
return StepSize.Get();
}
void SRadialSlider::SetStepSize(const TAttribute<float>& InStepSize)
{
StepSize = InStepSize;
}
void SRadialSlider::SetMouseUsesStep(bool MouseUsesStep)
{
bMouseUsesStep = MouseUsesStep;
}
void SRadialSlider::SetRequiresControllerLock(bool RequiresControllerLock)
{
bRequiresControllerLock = RequiresControllerLock;
}
void SRadialSlider::SetUseVerticalDrag(bool UseVerticalDrag)
{
bUseVerticalDrag = UseVerticalDrag;
}
void SRadialSlider::SetShowSliderHandle(bool ShowSliderHandle)
{
bShowSliderHandle = ShowSliderHandle;
}
void SRadialSlider::SetShowSliderHand(bool ShowSliderHand)
{
bShowSliderHand = ShowSliderHand;
}
void SRadialSlider::SetThickness(const float InThickness)
{
SetAttribute(Thickness, TAttribute<TOptional<float>>(InThickness), EInvalidateWidgetReason::Paint);
}
#if WITH_ACCESSIBILITY
TSharedRef<FSlateAccessibleWidget> SRadialSlider::CreateAccessibleWidget()
{
return MakeShareable<FSlateAccessibleWidget>(new FSlateAccessibleSlider(SharedThis(this)));
}
#endif