Radial menus are in good shape
This commit is contained in:
Binary file not shown.
BIN
Content/Widgets/WB_Menu.uasset
LFS
BIN
Content/Widgets/WB_Menu.uasset
LFS
Binary file not shown.
Binary file not shown.
BIN
Content/Widgets/basic-border.uasset
LFS
Normal file
BIN
Content/Widgets/basic-border.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Widgets/teardrop.uasset
LFS
Normal file
BIN
Content/Widgets/teardrop.uasset
LFS
Normal file
Binary file not shown.
@@ -1,6 +1,8 @@
|
|||||||
#include "RadialMenu.h"
|
#include "RadialMenu.h"
|
||||||
#include "Rendering/DrawElements.h"
|
#include "Rendering/DrawElements.h"
|
||||||
#include "Engine/Texture2D.h"
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/FontMeasure.h"
|
||||||
|
#include "Framework/Application/SlateApplication.h"
|
||||||
#include "Styling/SlateBrush.h"
|
#include "Styling/SlateBrush.h"
|
||||||
#include "Widgets/SLeafWidget.h"
|
#include "Widgets/SLeafWidget.h"
|
||||||
|
|
||||||
@@ -8,17 +10,30 @@
|
|||||||
TArray<FRadialMenuItem> FRadialMenuItem::Calculate(const FRadialMenuConfig &Config)
|
TArray<FRadialMenuItem> FRadialMenuItem::Calculate(const FRadialMenuConfig &Config)
|
||||||
{
|
{
|
||||||
TArray<FRadialMenuItem> Items;
|
TArray<FRadialMenuItem> Items;
|
||||||
if (Config.NumItems < 0) return Items;
|
const int32 NumItems = Config.MenuItems.Num();
|
||||||
|
if (NumItems <= 0) return Items;
|
||||||
|
|
||||||
int32 NumRight = (Config.NumItems / 2);
|
int32 NumRight = (NumItems / 2);
|
||||||
int32 NumLeft = Config.NumItems - NumRight;
|
int32 NumLeft = NumItems - NumRight;
|
||||||
Items.SetNum(Config.NumItems);
|
Items.SetNum(NumItems);
|
||||||
|
|
||||||
|
// Measure each non-empty label first; the spoke layout's item height
|
||||||
|
// is derived from the tallest label.
|
||||||
|
const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
||||||
|
double MaxTextHeight = 5.0;
|
||||||
|
for (int32 I = 0; I < NumItems; I++)
|
||||||
|
{
|
||||||
|
if (Config.MenuItems[I].IsEmpty()) continue;
|
||||||
|
Items[I].TextSize = FontMeasure->Measure(Config.MenuItems[I], Config.Font);
|
||||||
|
MaxTextHeight = FMath::Max(MaxTextHeight, Items[I].TextSize.Y);
|
||||||
|
}
|
||||||
|
const float ItemHeight = static_cast<float>(MaxTextHeight * 1.2);
|
||||||
|
|
||||||
View LeftItems(Items.GetData(), NumLeft);
|
View LeftItems(Items.GetData(), NumLeft);
|
||||||
View RightItems(Items.GetData() + NumLeft, NumRight);
|
View RightItems(Items.GetData() + NumLeft, NumRight);
|
||||||
|
|
||||||
CalculateSpokes(LeftItems, Config.NumItems, Config.ItemHeight, Config.InnerRadius, Config.MinSpoke);
|
CalculateSpokes(LeftItems, NumItems, ItemHeight, Config.InnerRadius, Config.MinSpoke);
|
||||||
CalculateSpokes(RightItems, Config.NumItems, Config.ItemHeight, Config.InnerRadius, Config.MinSpoke);
|
CalculateSpokes(RightItems, NumItems, ItemHeight, Config.InnerRadius, Config.MinSpoke);
|
||||||
double LeftWidth = WidestSpoke(LeftItems);
|
double LeftWidth = WidestSpoke(LeftItems);
|
||||||
double RightWidth = WidestSpoke(RightItems);
|
double RightWidth = WidestSpoke(RightItems);
|
||||||
double HalfWidth = FMath::Max(LeftWidth, RightWidth) + Config.Spread;
|
double HalfWidth = FMath::Max(LeftWidth, RightWidth) + Config.Spread;
|
||||||
@@ -29,6 +44,29 @@ TArray<FRadialMenuItem> FRadialMenuItem::Calculate(const FRadialMenuConfig &Conf
|
|||||||
return Items;
|
return Items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TArray<FVector2D> FRadialMenuItem::CalculateDirections(int32 NumItems)
|
||||||
|
{
|
||||||
|
TArray<FVector2D> Result;
|
||||||
|
if (NumItems <= 0) return Result;
|
||||||
|
|
||||||
|
const int32 NumRight = (NumItems / 2);
|
||||||
|
const int32 NumLeft = NumItems - NumRight;
|
||||||
|
Result.SetNum(NumItems);
|
||||||
|
|
||||||
|
// Left side: SpokeVector with X flipped (matches FlipHorizontal in Calculate).
|
||||||
|
for (int32 I = 0; I < NumLeft; I++)
|
||||||
|
{
|
||||||
|
FVector2D V = SpokeVector(I, NumLeft, NumItems);
|
||||||
|
V.X = -V.X;
|
||||||
|
Result[I] = V;
|
||||||
|
}
|
||||||
|
for (int32 I = 0; I < NumRight; I++)
|
||||||
|
{
|
||||||
|
Result[NumLeft + I] = SpokeVector(I, NumRight, NumItems);
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
FVector2D FRadialMenuItem::SpokeVector(int32 I, int32 NSide, int32 NTotal)
|
FVector2D FRadialMenuItem::SpokeVector(int32 I, int32 NSide, int32 NTotal)
|
||||||
{
|
{
|
||||||
double SpokeAngle = 1.0 / NTotal;
|
double SpokeAngle = 1.0 / NTotal;
|
||||||
@@ -130,6 +168,13 @@ public:
|
|||||||
Invalidate(EInvalidateWidgetReason::Layout);
|
Invalidate(EInvalidateWidgetReason::Layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetPointer(FVector2D NewPointer)
|
||||||
|
{
|
||||||
|
if (PointerVector == NewPointer) return;
|
||||||
|
PointerVector = NewPointer;
|
||||||
|
Invalidate(EInvalidateWidgetReason::Paint);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual FVector2D ComputeDesiredSize(float) const override
|
virtual FVector2D ComputeDesiredSize(float) const override
|
||||||
{
|
{
|
||||||
@@ -144,6 +189,14 @@ protected:
|
|||||||
Max.X = FMath::Max(Max.X, P.X);
|
Max.X = FMath::Max(Max.X, P.X);
|
||||||
Max.Y = FMath::Max(Max.Y, P.Y);
|
Max.Y = FMath::Max(Max.Y, P.Y);
|
||||||
}
|
}
|
||||||
|
if (Item.TextSize.X <= 0) continue;
|
||||||
|
const double Gap = Config.Spread * 0.5;
|
||||||
|
const double TextLeft = Item.RightSide ? (Item.Point3.X + Gap) : (Item.Point3.X - Gap - Item.TextSize.X);
|
||||||
|
const double TextRight = Item.RightSide ? (Item.Point3.X + Gap + Item.TextSize.X) : (Item.Point3.X - Gap);
|
||||||
|
Min.X = FMath::Min(Min.X, TextLeft);
|
||||||
|
Max.X = FMath::Max(Max.X, TextRight);
|
||||||
|
Min.Y = FMath::Min(Min.Y, Item.Point3.Y - Item.TextSize.Y * 0.5);
|
||||||
|
Max.Y = FMath::Max(Max.Y, Item.Point3.Y + Item.TextSize.Y * 0.5);
|
||||||
}
|
}
|
||||||
// Symmetric around the origin so the menu draws centered.
|
// Symmetric around the origin so the menu draws centered.
|
||||||
double HalfW = FMath::Max(FMath::Abs(Min.X), FMath::Abs(Max.X));
|
double HalfW = FMath::Max(FMath::Abs(Min.X), FMath::Abs(Max.X));
|
||||||
@@ -162,7 +215,7 @@ protected:
|
|||||||
{
|
{
|
||||||
const FRadialMenuItem& Item = Items[I];
|
const FRadialMenuItem& Item = Items[I];
|
||||||
const bool bSelected = (I == Config.SelectedItem);
|
const bool bSelected = (I == Config.SelectedItem);
|
||||||
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.LineColor;
|
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.ItemColor;
|
||||||
const float Thickness = bSelected ? Config.SelectedThickness : Config.LineThickness;
|
const float Thickness = bSelected ? Config.SelectedThickness : Config.LineThickness;
|
||||||
TArray<FVector2D> Points;
|
TArray<FVector2D> Points;
|
||||||
Points.Add(Center + Item.Point1);
|
Points.Add(Center + Item.Point1);
|
||||||
@@ -171,9 +224,30 @@ protected:
|
|||||||
FSlateDrawElement::MakeLines(OutDrawElements, LayerId, PaintGeom, Points, ESlateDrawEffect::None, Color, true, Thickness);
|
FSlateDrawElement::MakeLines(OutDrawElements, LayerId, PaintGeom, Points, ESlateDrawEffect::None, Color, true, Thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.DotRadius <= 0.0f) return LayerId;
|
// Draw item labels, vertically centered on Point3. Right-side items
|
||||||
if (Config.DotTexture == nullptr) return LayerId;
|
// are left-aligned to Point3; left-side items are right-aligned.
|
||||||
|
for (int32 I = 0; I < Items.Num(); I++)
|
||||||
|
{
|
||||||
|
const FRadialMenuItem& Item = Items[I];
|
||||||
|
if (Item.TextSize.X <= 0) continue;
|
||||||
|
const bool bSelected = (I == Config.SelectedItem);
|
||||||
|
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.ItemColor;
|
||||||
|
const double Gap = Config.Spread * 0.5;
|
||||||
|
FVector2D TextPos;
|
||||||
|
TextPos.X = Item.RightSide ? (Item.Point3.X + Gap) : (Item.Point3.X - Gap - Item.TextSize.X);
|
||||||
|
TextPos.Y = Item.Point3.Y - Item.TextSize.Y * 0.5;
|
||||||
|
FSlateDrawElement::MakeText(
|
||||||
|
OutDrawElements,
|
||||||
|
LayerId + 1,
|
||||||
|
AllottedGeometry.ToPaintGeometry(Item.TextSize, FSlateLayoutTransform(Center + TextPos)),
|
||||||
|
Config.MenuItems[I],
|
||||||
|
Config.Font,
|
||||||
|
ESlateDrawEffect::None,
|
||||||
|
Color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.DotRadius > 0.0f && Config.DotTexture != nullptr)
|
||||||
|
{
|
||||||
FSlateBrush DotBrush;
|
FSlateBrush DotBrush;
|
||||||
DotBrush.SetResourceObject(Config.DotTexture);
|
DotBrush.SetResourceObject(Config.DotTexture);
|
||||||
const FVector2D DotSize(Config.DotRadius * 2.0f);
|
const FVector2D DotSize(Config.DotRadius * 2.0f);
|
||||||
@@ -181,158 +255,109 @@ protected:
|
|||||||
{
|
{
|
||||||
const FRadialMenuItem& Item = Items[I];
|
const FRadialMenuItem& Item = Items[I];
|
||||||
const bool bSelected = (I == Config.SelectedItem);
|
const bool bSelected = (I == Config.SelectedItem);
|
||||||
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.LineColor;
|
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.ItemColor;
|
||||||
for (const FVector2D& Point : { Item.Point1, Item.Point3 })
|
for (const FVector2D& Point : { Item.Point1, Item.Point3 })
|
||||||
{
|
{
|
||||||
const FVector2D LocalPoint = Center + Point;
|
const FVector2D LocalPoint = Center + Point;
|
||||||
FSlateDrawElement::MakeBox(
|
FSlateDrawElement::MakeBox(
|
||||||
OutDrawElements,
|
OutDrawElements,
|
||||||
LayerId,
|
LayerId + 1,
|
||||||
AllottedGeometry.ToPaintGeometry(DotSize, FSlateLayoutTransform(LocalPoint - FVector2D(Config.DotRadius))),
|
AllottedGeometry.ToPaintGeometry(DotSize, FSlateLayoutTransform(LocalPoint - FVector2D(Config.DotRadius))),
|
||||||
&DotBrush,
|
&DotBrush,
|
||||||
ESlateDrawEffect::None,
|
ESlateDrawEffect::None,
|
||||||
Color);
|
Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return LayerId;
|
}
|
||||||
|
|
||||||
|
// Teardrop: visualizes PointerVector inside InnerRadius. The texture's
|
||||||
|
// tip naturally points up (0,-1); rotate it to align with PointerVector.
|
||||||
|
// When PointerVector is (0,0), the teardrop stays upright at the center.
|
||||||
|
if (Config.TeardropRadius > 0.0f && Config.Teardrop != nullptr)
|
||||||
|
{
|
||||||
|
FSlateBrush TeardropBrush;
|
||||||
|
TeardropBrush.SetResourceObject(Config.Teardrop);
|
||||||
|
const FVector2D TeardropSize(Config.TeardropRadius * 2.0f);
|
||||||
|
const FVector2D TeardropCenter = Center + PointerVector * Config.InnerRadius;
|
||||||
|
const float Angle = PointerVector.IsZero()
|
||||||
|
? 0.0f
|
||||||
|
: static_cast<float>(FMath::Atan2(PointerVector.X, -PointerVector.Y));
|
||||||
|
FSlateDrawElement::MakeRotatedBox(
|
||||||
|
OutDrawElements,
|
||||||
|
LayerId + 1,
|
||||||
|
AllottedGeometry.ToPaintGeometry(TeardropSize, FSlateLayoutTransform(TeardropCenter - FVector2D(Config.TeardropRadius))),
|
||||||
|
&TeardropBrush,
|
||||||
|
ESlateDrawEffect::None,
|
||||||
|
Angle);
|
||||||
|
}
|
||||||
|
return LayerId + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FRadialMenuConfig Config;
|
FRadialMenuConfig Config;
|
||||||
|
|
||||||
TArray<FRadialMenuItem> Items;
|
TArray<FRadialMenuItem> Items;
|
||||||
|
|
||||||
|
FVector2D PointerVector = {0,0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetNumItems(int32 NewNumItems)
|
void URadialMenuWidget::SetMenuItems(const TArray<FString>& NewMenuItems)
|
||||||
{
|
{
|
||||||
if (Config.NumItems == NewNumItems) return;
|
if (Config.MenuItems == NewMenuItems) return;
|
||||||
|
|
||||||
Config.NumItems = NewNumItems;
|
Config.MenuItems = NewMenuItems;
|
||||||
|
Config.SelectedItem = -1;
|
||||||
|
PointerVector = FVector2D();
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetItemHeight(float NewItemHeight)
|
FString URadialMenuWidget::GetSelectedMenuItem() const
|
||||||
{
|
{
|
||||||
if (Config.ItemHeight == NewItemHeight) return;
|
if (!Config.MenuItems.IsValidIndex(Config.SelectedItem)) return FString();
|
||||||
|
return Config.MenuItems[Config.SelectedItem];
|
||||||
Config.ItemHeight = NewItemHeight;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetInnerRadius(float NewInnerRadius)
|
void URadialMenuWidget::SetSelectedItem(int32 NewSelectedItem)
|
||||||
{
|
{
|
||||||
if (Config.InnerRadius == NewInnerRadius) return;
|
if (Config.SelectedItem == NewSelectedItem) return;
|
||||||
|
|
||||||
Config.InnerRadius = NewInnerRadius;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetMinSpoke(float NewMinSpoke)
|
|
||||||
{
|
|
||||||
if (Config.MinSpoke == NewMinSpoke) return;
|
|
||||||
|
|
||||||
Config.MinSpoke = NewMinSpoke;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetSpread(float NewSpread)
|
|
||||||
{
|
|
||||||
if (Config.Spread == NewSpread) return;
|
|
||||||
|
|
||||||
Config.Spread = NewSpread;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetLineThickness(float NewLineThickness)
|
|
||||||
{
|
|
||||||
if (Config.LineThickness == NewLineThickness) return;
|
|
||||||
|
|
||||||
Config.LineThickness = NewLineThickness;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetLineColor(FLinearColor NewLineColor)
|
|
||||||
{
|
|
||||||
if (Config.LineColor == NewLineColor) return;
|
|
||||||
|
|
||||||
Config.LineColor = NewLineColor;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetDotTexture(UTexture2D* NewDotTexture)
|
|
||||||
{
|
|
||||||
if (Config.DotTexture == NewDotTexture) return;
|
|
||||||
|
|
||||||
Config.DotTexture = NewDotTexture;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetDotRadius(float NewDotRadius)
|
|
||||||
{
|
|
||||||
if (Config.DotRadius == NewDotRadius) return;
|
|
||||||
|
|
||||||
Config.DotRadius = NewDotRadius;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool URadialMenuWidget::SetSelectedItem(int32 NewSelectedItem)
|
|
||||||
{
|
|
||||||
if (Config.SelectedItem == NewSelectedItem) return false;
|
|
||||||
|
|
||||||
Config.SelectedItem = NewSelectedItem;
|
Config.SelectedItem = NewSelectedItem;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetSelectedColor(FLinearColor NewSelectedColor)
|
|
||||||
{
|
|
||||||
if (Config.SelectedColor == NewSelectedColor) return;
|
|
||||||
|
|
||||||
Config.SelectedColor = NewSelectedColor;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetSelectedThickness(float NewSelectedThickness)
|
|
||||||
{
|
|
||||||
if (Config.SelectedThickness == NewSelectedThickness) return;
|
|
||||||
|
|
||||||
Config.SelectedThickness = NewSelectedThickness;
|
|
||||||
SynchronizeProperties();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SynchronizeProperties()
|
void URadialMenuWidget::SynchronizeProperties()
|
||||||
{
|
{
|
||||||
Super::SynchronizeProperties();
|
Super::SynchronizeProperties();
|
||||||
|
|
||||||
Items = FRadialMenuItem::Calculate(Config);
|
if (Directions.Num() != Config.MenuItems.Num())
|
||||||
if (Items.Num() == 0)
|
|
||||||
{
|
{
|
||||||
PointerVector = FVector2D(0,0);
|
Directions = FRadialMenuItem::CalculateDirections(Config.MenuItems.Num());
|
||||||
}
|
}
|
||||||
if (MySlateWidget.IsValid())
|
if (MySlateWidget.IsValid())
|
||||||
{
|
{
|
||||||
MySlateWidget->SetConfig(Config);
|
MySlateWidget->SetConfig(Config);
|
||||||
|
MySlateWidget->SetPointer(PointerVector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool URadialMenuWidget::AddPointer(FVector2D Direction, float Scale)
|
void URadialMenuWidget::AddPointer(FVector2D Direction, float Scale)
|
||||||
{
|
{
|
||||||
if (!IsVisible())
|
if ((!IsVisible()) || (Directions.Num() == 0))
|
||||||
{
|
{
|
||||||
return false;
|
PointerVector = FVector2D();
|
||||||
}
|
SetSelectedItem(-1);
|
||||||
|
if (MySlateWidget.IsValid()) MySlateWidget->SetPointer(PointerVector);
|
||||||
if (Items.Num() == 0)
|
return;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerVector += (Direction * Scale);
|
PointerVector += (Direction * Scale);
|
||||||
|
|
||||||
if (PointerVector.Length() < 0.5)
|
if (PointerVector.Length() < 0.5)
|
||||||
{
|
{
|
||||||
return SetSelectedItem(-1);
|
SetSelectedItem(-1);
|
||||||
|
if (MySlateWidget.IsValid()) MySlateWidget->SetPointer(PointerVector);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PointerVector.Length() > 1.0)
|
if (PointerVector.Length() > 1.0)
|
||||||
@@ -342,24 +367,19 @@ bool URadialMenuWidget::AddPointer(FVector2D Direction, float Scale)
|
|||||||
|
|
||||||
int32 BestIndex = -1;
|
int32 BestIndex = -1;
|
||||||
double BestDot = -2.0;
|
double BestDot = -2.0;
|
||||||
for (int32 I = 0; I < Items.Num(); I++)
|
for (int32 I = 0; I < Directions.Num(); I++)
|
||||||
{
|
{
|
||||||
FVector2D SpokeDir = Items[I].Point1;
|
FVector2D SpokeDir = Directions[I];
|
||||||
if (!SpokeDir.Normalize()) continue;
|
if (!SpokeDir.Normalize()) continue;
|
||||||
double Dot = FVector2D::DotProduct(SpokeDir, PointerVector);
|
double Dot = FVector2D::DotProduct(SpokeDir, PointerVector);
|
||||||
if (Dot <= BestDot) continue;
|
if (Dot <= BestDot) continue;
|
||||||
BestDot = Dot;
|
BestDot = Dot;
|
||||||
BestIndex = I;
|
BestIndex = I;
|
||||||
}
|
}
|
||||||
return SetSelectedItem(BestIndex);
|
SetSelectedItem(BestIndex);
|
||||||
|
if (MySlateWidget.IsValid()) MySlateWidget->SetPointer(PointerVector);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLinearColor URadialMenuWidget::GetItemColor(int N) const
|
|
||||||
{
|
|
||||||
return (N == Config.SelectedItem) ? Config.SelectedColor : Config.LineColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TSharedRef<SWidget> URadialMenuWidget::RebuildWidget()
|
TSharedRef<SWidget> URadialMenuWidget::RebuildWidget()
|
||||||
{
|
{
|
||||||
MySlateWidget = SNew(SRadialMenu, Config);
|
MySlateWidget = SNew(SRadialMenu, Config);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Components/Widget.h"
|
#include "Components/Widget.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
#include "RadialMenu.generated.h"
|
#include "RadialMenu.generated.h"
|
||||||
|
|
||||||
class SRadialMenu;
|
class SRadialMenu;
|
||||||
@@ -19,10 +20,10 @@ struct FRadialMenuConfig
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
int32 NumItems = 8;
|
TArray<FString> MenuItems;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
float ItemHeight = 20.0f;
|
FSlateFontInfo Font;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
float InnerRadius = 20.0f;
|
float InnerRadius = 20.0f;
|
||||||
@@ -37,7 +38,7 @@ struct FRadialMenuConfig
|
|||||||
float LineThickness = 4.0f;
|
float LineThickness = 4.0f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
FLinearColor LineColor = FLinearColor::White;
|
FLinearColor ItemColor = FLinearColor::White;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
TObjectPtr<UTexture2D> DotTexture;
|
TObjectPtr<UTexture2D> DotTexture;
|
||||||
@@ -45,6 +46,12 @@ struct FRadialMenuConfig
|
|||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
float DotRadius = 3.0f;
|
float DotRadius = 3.0f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
TObjectPtr<UTexture2D> Teardrop;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
float TeardropRadius = 0.0f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
int32 SelectedItem = -1;
|
int32 SelectedItem = -1;
|
||||||
|
|
||||||
@@ -73,8 +80,16 @@ struct FRadialMenuItem
|
|||||||
UPROPERTY(BlueprintReadOnly)
|
UPROPERTY(BlueprintReadOnly)
|
||||||
bool RightSide = false;
|
bool RightSide = false;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FVector2D TextSize = {0,0};
|
||||||
|
|
||||||
static TArray<FRadialMenuItem> Calculate(const FRadialMenuConfig &Config);
|
static TArray<FRadialMenuItem> 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<FVector2D> CalculateDirections(int32 NumItems);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using View = TArrayView<FRadialMenuItem>;
|
using View = TArrayView<FRadialMenuItem>;
|
||||||
|
|
||||||
@@ -112,59 +127,29 @@ class URadialMenuWidget : public UWidget
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
void SetNumItems(int32 NewNumItems);
|
void SetMenuItems(const TArray<FString>& NewMenuItems);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
void SetItemHeight(float NewItemHeight);
|
void ClearMenuItems() { SetMenuItems({}); }
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
void SetInnerRadius(float NewInnerRadius);
|
void SetSelectedItem(int32 NewSelectedItem);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetMinSpoke(float NewMinSpoke);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetSpread(float NewSpread);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetLineThickness(float NewLineThickness);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetLineColor(FLinearColor NewLineColor);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetDotTexture(UTexture2D* NewDotTexture);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetDotRadius(float NewDotRadius);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
bool SetSelectedItem(int32 NewSelectedItem);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetSelectedColor(FLinearColor NewSelectedColor);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void SetSelectedThickness(float NewSelectedThickness);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
const TArray<FRadialMenuItem> &GetItems() const { return Items; }
|
|
||||||
|
|
||||||
// Returns true if the selected item changed.
|
// Returns true if the selected item changed.
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
bool AddPointer(FVector2D Direction, float Scale);
|
void AddPointer(FVector2D Direction, float Scale);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
FVector2D GetPointer() const { return PointerVector; }
|
FVector2D GetPointer() const { return PointerVector; }
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
FLinearColor GetItemColor(int N) const;
|
FString GetSelectedMenuItem() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UPROPERTY(EditAnywhere)
|
UPROPERTY(EditAnywhere)
|
||||||
FRadialMenuConfig Config;
|
FRadialMenuConfig Config;
|
||||||
|
|
||||||
TArray<FRadialMenuItem> Items;
|
TArray<FVector2D> Directions;
|
||||||
|
|
||||||
FVector2D PointerVector = {0,0};
|
FVector2D PointerVector = {0,0};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user