// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Brushes/SlateRoundedBoxBrush.h" #include "CoreMinimal.h" #include "Curves/CurveFloat.h" #include "Framework/SlateDelegates.h" #include "Input/Reply.h" #include "Misc/Attribute.h" #include "Styling/CoreStyle.h" #include "Styling/SlateBrush.h" #include "Styling/SlateColor.h" #include "Styling/SlateTypes.h" #include "Styling/SlateWidgetStyle.h" #include "Styling/SlateWidgetStyleAsset.h" #include "Styling/StyleColors.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SLeafWidget.h" class FPaintArgs; class FSlateWindowElementList; class UCurveFloat; class SRadialSlider : public SLeafWidget { public: SLATE_BEGIN_ARGS(SRadialSlider) : _MouseUsesStep(false) , _RequiresControllerLock(true) , _Locked(false) , _SliderBarColor(FLinearColor::Gray) , _SliderProgressColor(FLinearColor::White) , _SliderHandleColor(FLinearColor::White) , _CenterBackgroundColor(FLinearColor::Transparent) , _CenterBackgroundBrush(FSlateRoundedBoxBrush(FStyleColors::Transparent, FVector2D(90.0f, 90.0f))) // todo: add to a custom radial slider style , _Style(&FCoreStyle::Get().GetWidgetStyle("Slider")) , _StepSize(0.01f) , _Value(1.f) , _bUseCustomDefaultValue(false) , _CustomDefaultValue(0.0f) , _SliderHandleStartAngle(60.0f) , _SliderHandleEndAngle(300.0f) , _AngularOffset(0.0f) , _HandStartEndRatio(FVector2D(0.0f, 1.0f)) , _IsFocusable(true) , _UseVerticalDrag(false) , _ShowSliderHandle(true) , _ShowSliderHand(false) , _OnMouseCaptureBegin() , _OnMouseCaptureEnd() , _OnValueChanged() { } /** Sets new value if mouse position is greater/less than half the step size. */ SLATE_ARGUMENT(bool, MouseUsesStep) /** Sets whether we have to lock input to change the slider value. */ SLATE_ARGUMENT(bool, RequiresControllerLock) /** Whether the handle is interactive or fixed. */ SLATE_ATTRIBUTE(bool, Locked) /** The color to draw the slider bar in. */ SLATE_ATTRIBUTE(FSlateColor, SliderBarColor) /** The color to draw completed progress of the slider bar in. */ SLATE_ATTRIBUTE(FSlateColor, SliderProgressColor) /** The color to draw the slider handle in. */ SLATE_ATTRIBUTE(FSlateColor, SliderHandleColor) /** The color to draw the center background in. */ SLATE_ATTRIBUTE(FSlateColor, CenterBackgroundColor) /** The thickness used for the slider bar. For backwards compatibility, this will only be used instead of the bar thickness from Style if it has been manually set with SetThickness. */ SLATE_ATTRIBUTE(float, Thickness) /** Center background image. */ SLATE_ARGUMENT(FSlateBrush, CenterBackgroundBrush) /** The style used to draw the slider. */ SLATE_STYLE_ARGUMENT(FSliderStyle, Style ) /** The input mode while using the controller. */ SLATE_ATTRIBUTE(float, StepSize) /** A value that drives where the slider handle appears. Value is normalized between 0 and 1. */ SLATE_ATTRIBUTE( float, Value ) /** Whether the slider should draw it's progress bar from a custom value on the slider */ SLATE_ATTRIBUTE(bool, bUseCustomDefaultValue) /** The value where the slider should draw it's progress bar from, independent of direction */ SLATE_ATTRIBUTE(float, CustomDefaultValue) /** A curve that defines how the slider should be sampled. Default is linear.*/ SLATE_ARGUMENT(FRuntimeFloatCurve, SliderRange) /** The angle at which the Slider Handle will start. */ SLATE_ARGUMENT(float, SliderHandleStartAngle) /** The angle at which the Slider Handle will end. */ SLATE_ARGUMENT(float, SliderHandleEndAngle) /** Rotates radial slider by arbitrary offset to support full gamut of configurations */ SLATE_ARGUMENT(float, AngularOffset) /** Start and end of the hand as a ratio to the slider radius (so 0.0 to 1.0 is from the slider center to the handle). */ SLATE_ARGUMENT(FVector2D, HandStartEndRatio) /** Distributes value tags along the slider */ SLATE_ARGUMENT(TArray, ValueTags) /** Sometimes a slider should only be mouse-clickable and never keyboard focusable. */ SLATE_ARGUMENT(bool, IsFocusable) /** Whether the value is changed when dragging vertically as opposed to along the radial curve. */ SLATE_ARGUMENT(bool, UseVerticalDrag) /** Whether to show the slider thumb. */ SLATE_ARGUMENT(bool, ShowSliderHandle) /** Whether to show the slider hand. */ SLATE_ARGUMENT(bool, ShowSliderHand) /** Invoked when the mouse is pressed and a capture begins. */ SLATE_EVENT(FSimpleDelegate, OnMouseCaptureBegin) /** Invoked when the mouse is released and a capture ends. */ SLATE_EVENT(FSimpleDelegate, OnMouseCaptureEnd) /** Invoked when the Controller is pressed and capture begins. */ SLATE_EVENT(FSimpleDelegate, OnControllerCaptureBegin) /** Invoked when the controller capture is released. */ SLATE_EVENT(FSimpleDelegate, OnControllerCaptureEnd) /** Called when the value is changed by the slider. */ SLATE_EVENT( FOnFloatValueChanged, OnValueChanged ) SLATE_END_ARGS() ADVANCEDWIDGETS_API SRadialSlider(); ADVANCEDWIDGETS_API virtual ~SRadialSlider(); /** * Construct the widget. * * @param InDeclaration A declaration from which to construct the widget. */ ADVANCEDWIDGETS_API void Construct( const SRadialSlider::FArguments& InDeclaration ); /** Get the SliderRange attribute */ FRuntimeFloatCurve GetSliderRange() const { return SliderRange; } /** Get the minumum value in Slider Range */ ADVANCEDWIDGETS_API float GetMinValue() const; /** Get the maximum value in Slider Range */ ADVANCEDWIDGETS_API float GetMaxValue() const; /** Get the MinSliderHandleAngle attribute */ float GetSliderHandleStartAngle() const { return SliderHandleStartAngle; } /** Get the MaxSliderHandleAngle attribute */ float GetSliderHandleEndAngle() const { return SliderHandleEndAngle; } /** Get the AngularOffset attribute */ float GetAngularOffset() const { return AngularOffset; } /** Get the ValueTags attribute */ TArray GetValueTags() const { return ValueTags; } /** Get the Value attribute */ ADVANCEDWIDGETS_API float GetValue() const; /** Get the bUseCustomDefaultValue attribute */ ADVANCEDWIDGETS_API bool GetUseCustomDefaultValue() const; /** Get ths CustomDefaultValue attribute */ ADVANCEDWIDGETS_API float GetCustomDefaultValue() const; /** Get the Value attribute scaled from 0 to 1 */ ADVANCEDWIDGETS_API float GetNormalizedValue(float RawValue) const; /** Get the Slider's Handle position scaled from 0 to 1 */ ADVANCEDWIDGETS_API float GetNormalizedSliderHandlePosition() const; /** Set the Value attribute */ ADVANCEDWIDGETS_API void SetValue(const TAttribute& InValueAttribute); /** Set the bUseCustomDefaultValue attribute */ ADVANCEDWIDGETS_API void SetUseCustomDefaultValue(const TAttribute& InValueAttribute); /** Set the CustomDefaultValue attribute */ ADVANCEDWIDGETS_API void SetCustomDefaultValue(const TAttribute& InValueAttribute); /** Set the SliderRange attribute */ void SetSliderRange(const FRuntimeFloatCurve& InSliderRange) { SliderRange = InSliderRange; } /** Set the SliderHandleStartAngle and SliderHandleEndAngle attributes. If the new SliderHandleStartAngle is more than the new SliderHandleEndAngle, SliderHandleEndAngle will be changed to equal SliderHandleStartAngle. */ ADVANCEDWIDGETS_API void SetSliderHandleStartAngleAndSliderHandleEndAngle(float InSliderHandleStartAngle, float InSliderHandleEndAngle); /** Set the AngularOffset attribute */ void SetAngularOffset(float InAngularOffset) { AngularOffset = InAngularOffset; } /** Set the HandStartEndRatio. Clamped to 0.0 to 1.0, and if the start ratio is more than the end ratio, end ratio will be set to the start ratio. */ ADVANCEDWIDGETS_API void SetHandStartEndRatio(FVector2D InHandStartEndRatio); /** Set the ValueTags attribute */ void SetValueTags(const TArray& InValueTags) { ValueTags = InValueTags; } /** Set the Locked attribute */ ADVANCEDWIDGETS_API void SetLocked(const TAttribute& InLocked); /** Set the SliderBarColor attribute */ ADVANCEDWIDGETS_API void SetSliderBarColor(FSlateColor InSliderBarColor); /** Set the SliderProgressColor attribute */ ADVANCEDWIDGETS_API void SetSliderProgressColor(FSlateColor InSliderProgressColor); /** Set the SliderHandleColor attribute */ ADVANCEDWIDGETS_API void SetSliderHandleColor(FSlateColor InSliderHandleColor); /** Set the SliderHandleColor attribute */ ADVANCEDWIDGETS_API void SetCenterBackgroundColor(FSlateColor InCenterHandleColor); /** Set the Thickness attribute. For backward compatibility, Thickness will be used for drawing instead of Style->BarThickness only if it has been set with this method, */ ADVANCEDWIDGETS_API void SetThickness(const float InThickness); /** Get the StepSize attribute */ ADVANCEDWIDGETS_API float GetStepSize() const; /** Set the StepSize attribute */ ADVANCEDWIDGETS_API void SetStepSize(const TAttribute& InStepSize); /** Set the MouseUsesStep attribute */ ADVANCEDWIDGETS_API void SetMouseUsesStep(bool MouseUsesStep); /** Set the RequiresControllerLock attribute */ ADVANCEDWIDGETS_API void SetRequiresControllerLock(bool RequiresControllerLock); /** Set the UseVerticalDrag attribute */ ADVANCEDWIDGETS_API void SetUseVerticalDrag(bool UseVerticalDrag); /** Set the ShowSliderHandle attribute */ ADVANCEDWIDGETS_API void SetShowSliderHandle(bool ShowSliderHandle); /** Set the ShowSliderHand attribute */ ADVANCEDWIDGETS_API void SetShowSliderHand(bool ShowSliderHand); public: // SWidget overrides ADVANCEDWIDGETS_API virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override; ADVANCEDWIDGETS_API virtual FVector2D ComputeDesiredSize(float) const override; ADVANCEDWIDGETS_API virtual FReply OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; ADVANCEDWIDGETS_API virtual FReply OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; ADVANCEDWIDGETS_API virtual FReply OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; ADVANCEDWIDGETS_API virtual FReply OnTouchStarted(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent) override; ADVANCEDWIDGETS_API virtual FReply OnTouchMoved(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent) override; ADVANCEDWIDGETS_API virtual FReply OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent) override; ADVANCEDWIDGETS_API virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; ADVANCEDWIDGETS_API virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; ADVANCEDWIDGETS_API virtual void OnFocusLost(const FFocusEvent& InFocusEvent) override; ADVANCEDWIDGETS_API virtual FNavigationReply OnNavigation(const FGeometry& MyGeometry, const FNavigationEvent& InNavigationEvent) override; ADVANCEDWIDGETS_API virtual bool SupportsKeyboardFocus() const override; ADVANCEDWIDGETS_API virtual bool IsInteractable() const override; #if WITH_ACCESSIBILITY ADVANCEDWIDGETS_API virtual TSharedRef CreateAccessibleWidget() override; #endif /** @return Is the handle locked or not? Defaults to false */ ADVANCEDWIDGETS_API bool IsLocked() const; protected: /** * Commits the specified slider value. * * @param NewValue The value to commit. */ ADVANCEDWIDGETS_API virtual void CommitValue(float NewValue); /** * Calculates the new value based on the given absolute coordinates. * * @param MyGeometry The slider's geometry. * @param AbsolutePosition The absolute position of the slider. * @return The new value. */ ADVANCEDWIDGETS_API float PositionToValue(const FGeometry& MyGeometry, const FVector2D& AbsolutePosition); ADVANCEDWIDGETS_API const FSlateBrush* GetBarImage() const; ADVANCEDWIDGETS_API const FSlateBrush* GetThumbImage() const; protected: // Holds the style passed to the widget upon construction. const FSliderStyle* Style; // Holds a flag indicating whether the slider is locked. TAttribute LockedAttribute; // Holds the color of the slider bar. TAttribute SliderBarColor; // Holds the color for the completed progress of the slider bar. TAttribute SliderProgressColor; // Holds the color of the slider handle. TAttribute SliderHandleColor; // Holds the color of the center background. TAttribute CenterBackgroundColor; // Center background image brush FSlateBrush CenterBackgroundBrush; // Thickness used for slider bar instead of Style->BarThickness (see SetThickness) TAttribute> Thickness; // Holds the slider's current value. TAttribute ValueAttribute; // Whether the slider should draw it's progress bar from a custom value on the slider TAttribute bUseCustomDefaultValue; // The value where the slider should draw it's progress bar from, independent of direction TAttribute CustomDefaultValue; // Holds the initial cursor in case a custom cursor has been specified, so we can restore it after dragging the slider EMouseCursor::Type CachedCursor; /** The location in screenspace the slider was pressed by a touch */ FVector2D PressedScreenSpaceTouchDownPosition = FVector2D(0, 0); /** Holds the amount to adjust the value by when using a controller or keyboard */ TAttribute StepSize; /** The angle at which the radial slider should begin */ float SliderHandleStartAngle; /** The angle at which the radial slider should end */ float SliderHandleEndAngle; /** The angle at which the radial slider should be offset by */ float AngularOffset; /** Start and end of the hand as a ratio to the slider radius (so 0.0 to 1.0 is from the slider center to the handle). */ FVector2D HandStartEndRatio; /** The values that should be drawn around the radial slider*/ TArray ValueTags; /** A curve that defines how the slider should be sampled. Default is linear. */ FRuntimeFloatCurve SliderRange; // Holds a flag indicating whether a controller/keyboard is manipulating the slider's value. // When true, navigation away from the widget is prevented until a new value has been accepted or canceled. bool bControllerInputCaptured; /** Sets new value if mouse position is greater/less than half the step size. */ bool bMouseUsesStep; /** Sets whether we have to lock input to change the slider value. */ bool bRequiresControllerLock; /** When true, this slider will be keyboard focusable. Defaults to false. */ bool bIsFocusable; /** When true, value is changed when dragging vertically as opposed to along the radial curve. */ bool bUseVerticalDrag; /** Whether to show the slider handle (thumb). */ bool bShowSliderHandle; /** Whether to show the slider hand. */ bool bShowSliderHand; private: // Resets controller input state. Fires delegates. ADVANCEDWIDGETS_API void ResetControllerState(); // Helper function to convert Absolute Position to Angle ADVANCEDWIDGETS_API float GetAngleFromPosition(const FGeometry& MyGeometry, const FVector2D& AbsolutePosition); // Called on Mouse / Touch input to cache relevant properties ADVANCEDWIDGETS_API void OnInputStarted(const FGeometry& MyGeometry, const FVector2D& InputAbsolutePosition); // Helper function for adding slider points to a slider points array ADVANCEDWIDGETS_API void AddSliderPointToArray(TArray& SliderPoints, const bool bIsUnique, const FVector2D& SliderPoint) const; // Holds a delegate that is executed when the mouse is pressed and a capture begins. FSimpleDelegate OnMouseCaptureBegin; // Holds a delegate that is executed when the mouse is let up and a capture ends. FSimpleDelegate OnMouseCaptureEnd; // Holds a delegate that is executed when capture begins for controller or keyboard. FSimpleDelegate OnControllerCaptureBegin; // Holds a delegate that is executed when capture ends for controller or keyboard. FSimpleDelegate OnControllerCaptureEnd; // Holds a delegate that is executed when the slider's value changed. FOnFloatValueChanged OnValueChanged; // Holds the current interaction's unclamped input angle. float AbsoluteInputAngle; // Stores the previous absolute position to support calculating rotational delta for relative input FVector2D PreviousAbsolutePosition; // Settings for behavior when IsUsingVerticalDrag is true // For when UseVerticalDrag is true, whether we're fine tuning the value bool bIsUsingFineTune; // The key to use when fine tuning vertical drag FKey FineTuneKey; float VerticalDragMouseSpeedNormal = 0.2f; float VerticalDragMouseSpeedFineTune = 0.05f; float VerticalDragPixelDelta = 50.0f; };