// // This class implements the layout calculatations for a radial // menu. // #pragma once #include "CoreMinimal.h" #include "Components/Widget.h" #include "Fonts/SlateFontInfo.h" #include "RadialMenu.generated.h" class SRadialMenu; class UTexture2D; USTRUCT(BlueprintType) struct FRadialMenuConfig { GENERATED_BODY() UPROPERTY(EditAnywhere, Category="RadialMenu") TArray MenuItems; UPROPERTY(EditAnywhere, Category="RadialMenu") FSlateFontInfo Font; UPROPERTY(EditAnywhere, Category="RadialMenu") float InnerRadius = 20.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") float MinSpoke = 20.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") float Spread = 20.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") float LineThickness = 4.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") FLinearColor ItemColor = FLinearColor::White; UPROPERTY(EditAnywhere, Category="RadialMenu") TObjectPtr DotTexture; UPROPERTY(EditAnywhere, Category="RadialMenu") float DotRadius = 3.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") TObjectPtr Teardrop; UPROPERTY(EditAnywhere, Category="RadialMenu") float TeardropRadius = 0.0f; UPROPERTY(EditAnywhere, Category="RadialMenu") int32 SelectedItem = -1; UPROPERTY(EditAnywhere, Category="RadialMenu") FLinearColor SelectedColor = FLinearColor::Yellow; UPROPERTY(EditAnywhere, Category="RadialMenu") float SelectedThickness = 2.0f; }; USTRUCT(BlueprintType) struct FRadialMenuItem { GENERATED_BODY() UPROPERTY(BlueprintReadOnly) FVector2D Point1 = {0,0}; UPROPERTY(BlueprintReadOnly) FVector2D Point2 = {0,0}; UPROPERTY(BlueprintReadOnly) FVector2D Point3 = {0,0}; UPROPERTY(BlueprintReadOnly) bool RightSide = false; UPROPERTY(BlueprintReadOnly) FVector2D TextSize = {0,0}; static TArray Calculate(const FRadialMenuConfig &Config); // Like Calculate, but only produces the unit-vector direction of // each spoke. Cheaper to evaluate when only the spoke directions // are needed (e.g. for hit-testing a pointer against the wheel). static TArray CalculateDirections(int32 NumItems); private: using View = TArrayView; // Give the unit vector for the selected spoke. NSide is // the number of spokes on the side of the wheel that // we're calculating, and NTotal is the total number of // spokes on both sides. The spokes on a given side are // organized top-to-bottom, and the angle between the // spokes is always equal to (1/NTotal) of the circle. static FVector2D SpokeVector(int32 I, int32 NSide, int32 NTotal); // Populate Point1 and Point2, these are the // endpoints of the spoke segment. Spokes are // designed to always be long enough to make room // for MinSpoke, but also to make enough room to // keep the menu items from overlapping. static void CalculateSpokes(View V, int32 TotalItems, float ItemHeight, float InnerRadius, float MinSpoke); // Search for the widest spoke, and return its X coordinate. static double WidestSpoke(View V); // Populate Point3, this is the endpoint of the spread // line that goes horizontal. static void CalculateSpread(View V, double Offset); // Flip everything in the specified view horizontally. static void FlipHorizontal(View V); }; UCLASS(BlueprintType, Blueprintable) class URadialMenuWidget : public UWidget { GENERATED_BODY() public: UFUNCTION(BlueprintCallable) void SetMenuItems(const TArray& NewMenuItems); UFUNCTION(BlueprintCallable) void ClearMenuItems() { SetMenuItems({}); } UFUNCTION(BlueprintCallable) void SetSelectedItem(int32 NewSelectedItem); // Returns true if the selected item changed. UFUNCTION(BlueprintCallable) void AddPointer(FVector2D Direction, float Scale); UFUNCTION(BlueprintCallable) FVector2D GetPointer() const { return PointerVector; } UFUNCTION(BlueprintCallable) FString GetSelectedMenuItem() const; protected: UPROPERTY(EditAnywhere) FRadialMenuConfig Config; TArray Directions; FVector2D PointerVector = {0,0}; virtual TSharedRef RebuildWidget() override; virtual void ReleaseSlateResources(bool bReleaseChildren) override; virtual void SynchronizeProperties() override; TSharedPtr MySlateWidget; };