Progress on radial menus
This commit is contained in:
BIN
Content/Widgets/WB_Menu.uasset
LFS
BIN
Content/Widgets/WB_Menu.uasset
LFS
Binary file not shown.
Binary file not shown.
@@ -5,27 +5,31 @@
|
|||||||
#include "Widgets/SLeafWidget.h"
|
#include "Widgets/SLeafWidget.h"
|
||||||
|
|
||||||
|
|
||||||
void URadialMenuLayout::Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread)
|
TArray<FRadialMenuItem> FRadialMenuItem::Calculate(const FRadialMenuConfig &Config)
|
||||||
{
|
{
|
||||||
NumRight = (NItems / 2);
|
TArray<FRadialMenuItem> Items;
|
||||||
NumLeft = NItems - NumRight;
|
if (Config.NumItems < 0) return Items;
|
||||||
Items.SetNum(NItems);
|
|
||||||
|
int32 NumRight = (Config.NumItems / 2);
|
||||||
|
int32 NumLeft = Config.NumItems - NumRight;
|
||||||
|
Items.SetNum(Config.NumItems);
|
||||||
|
|
||||||
View LeftItems(Items.GetData(), NumLeft);
|
View LeftItems(Items.GetData(), NumLeft);
|
||||||
View RightItems(Items.GetData() + NumLeft, NumRight);
|
View RightItems(Items.GetData() + NumLeft, NumRight);
|
||||||
|
|
||||||
CalculateSpokes(LeftItems, ItemHeight, InnerRadius, MinSpoke);
|
CalculateSpokes(LeftItems, Config.NumItems, Config.ItemHeight, Config.InnerRadius, Config.MinSpoke);
|
||||||
CalculateSpokes(RightItems, ItemHeight, InnerRadius, MinSpoke);
|
CalculateSpokes(RightItems, Config.NumItems, Config.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) + Spread;
|
double HalfWidth = FMath::Max(LeftWidth, RightWidth) + Config.Spread;
|
||||||
CalculateSpread(LeftItems, HalfWidth - LeftWidth);
|
CalculateSpread(LeftItems, HalfWidth - LeftWidth);
|
||||||
CalculateSpread(RightItems, HalfWidth - RightWidth);
|
CalculateSpread(RightItems, HalfWidth - RightWidth);
|
||||||
|
|
||||||
FlipHorizontal(LeftItems);
|
FlipHorizontal(LeftItems);
|
||||||
|
return Items;
|
||||||
}
|
}
|
||||||
|
|
||||||
FVector2D URadialMenuLayout::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;
|
||||||
double OffsetAngle = 0.5 * (0.5 - ((NSide - 1) * SpokeAngle));
|
double OffsetAngle = 0.5 * (0.5 - ((NSide - 1) * SpokeAngle));
|
||||||
@@ -34,7 +38,7 @@ FVector2D URadialMenuLayout::SpokeVector(int32 I, int32 NSide, int32 NTotal)
|
|||||||
return FVector2D(FMath::Sin(Radians), -FMath::Cos(Radians));
|
return FVector2D(FMath::Sin(Radians), -FMath::Cos(Radians));
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuLayout::FlipHorizontal(View V)
|
void FRadialMenuItem::FlipHorizontal(View V)
|
||||||
{
|
{
|
||||||
for (FRadialMenuItem& Item : V)
|
for (FRadialMenuItem& Item : V)
|
||||||
{
|
{
|
||||||
@@ -45,7 +49,7 @@ void URadialMenuLayout::FlipHorizontal(View V)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double URadialMenuLayout::WidestSpoke(View V)
|
double FRadialMenuItem::WidestSpoke(View V)
|
||||||
{
|
{
|
||||||
double Result = 0.0;
|
double Result = 0.0;
|
||||||
for (const FRadialMenuItem &Item : V)
|
for (const FRadialMenuItem &Item : V)
|
||||||
@@ -55,7 +59,7 @@ double URadialMenuLayout::WidestSpoke(View V)
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuLayout::CalculateSpread(View V, double Offset)
|
void FRadialMenuItem::CalculateSpread(View V, double Offset)
|
||||||
{
|
{
|
||||||
for (FRadialMenuItem &Item : V)
|
for (FRadialMenuItem &Item : V)
|
||||||
{
|
{
|
||||||
@@ -63,7 +67,7 @@ void URadialMenuLayout::CalculateSpread(View V, double Offset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuLayout::CalculateSpokes(View V, float ItemHeight, float InnerRadius, float MinSpoke)
|
void FRadialMenuItem::CalculateSpokes(View V, int32 TotalItems, float ItemHeight, float InnerRadius, float MinSpoke)
|
||||||
{
|
{
|
||||||
if (V.Num() == 0) return;
|
if (V.Num() == 0) return;
|
||||||
|
|
||||||
@@ -72,7 +76,7 @@ void URadialMenuLayout::CalculateSpokes(View V, float ItemHeight, float InnerRad
|
|||||||
for (int32 I = 0; I < V.Num(); I++)
|
for (int32 I = 0; I < V.Num(); I++)
|
||||||
{
|
{
|
||||||
V[I].RightSide = true;
|
V[I].RightSide = true;
|
||||||
V[I].Point1 = SpokeVector(I, V.Num(), Items.Num()) * InnerRadius;
|
V[I].Point1 = SpokeVector(I, V.Num(), TotalItems) * InnerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate point2 for all spokes.
|
// Calculate point2 for all spokes.
|
||||||
@@ -86,7 +90,7 @@ void URadialMenuLayout::CalculateSpokes(View V, float ItemHeight, float InnerRad
|
|||||||
}
|
}
|
||||||
for (int32 I = Mid; I < V.Num(); I++)
|
for (int32 I = Mid; I < V.Num(); I++)
|
||||||
{
|
{
|
||||||
FVector2D UnitVec = SpokeVector(I, V.Num(), Items.Num());
|
FVector2D UnitVec = SpokeVector(I, V.Num(), TotalItems);
|
||||||
double Y = (UnitVec.Y * (InnerRadius + MinSpoke));
|
double Y = (UnitVec.Y * (InnerRadius + MinSpoke));
|
||||||
if (Y < NextLineMin) Y = NextLineMin;
|
if (Y < NextLineMin) Y = NextLineMin;
|
||||||
NextLineMin = Y + ItemHeight;
|
NextLineMin = Y + ItemHeight;
|
||||||
@@ -114,48 +118,24 @@ public:
|
|||||||
SLATE_BEGIN_ARGS(SRadialMenu) {}
|
SLATE_BEGIN_ARGS(SRadialMenu) {}
|
||||||
SLATE_END_ARGS()
|
SLATE_END_ARGS()
|
||||||
|
|
||||||
void Construct(const FArguments& InArgs, URadialMenuLayout* InLayout)
|
void Construct(const FArguments& InArgs, const FRadialMenuConfig& InConfig)
|
||||||
{
|
{
|
||||||
Layout = InLayout;
|
Config = InConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Refresh()
|
void SetConfig(const FRadialMenuConfig& NewConfig)
|
||||||
{
|
{
|
||||||
|
Config = NewConfig;
|
||||||
|
Items = FRadialMenuItem::Calculate(Config);
|
||||||
Invalidate(EInvalidateWidgetReason::Layout);
|
Invalidate(EInvalidateWidgetReason::Layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetLineThickness(float NewLineThickness)
|
|
||||||
{
|
|
||||||
LineThickness = NewLineThickness;
|
|
||||||
Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLineColor(FLinearColor NewLineColor)
|
|
||||||
{
|
|
||||||
LineColor = NewLineColor;
|
|
||||||
Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDotTexture(UTexture2D* NewDotTexture)
|
|
||||||
{
|
|
||||||
DotTexture = NewDotTexture;
|
|
||||||
DotBrush.SetResourceObject(NewDotTexture);
|
|
||||||
Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDotRadius(float NewDotRadius)
|
|
||||||
{
|
|
||||||
DotRadius = NewDotRadius;
|
|
||||||
Invalidate(EInvalidateWidgetReason::Paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual FVector2D ComputeDesiredSize(float) const override
|
virtual FVector2D ComputeDesiredSize(float) const override
|
||||||
{
|
{
|
||||||
if (!Layout.IsValid()) return FVector2D::ZeroVector;
|
|
||||||
FVector2D Min(0.0, 0.0);
|
FVector2D Min(0.0, 0.0);
|
||||||
FVector2D Max(0.0, 0.0);
|
FVector2D Max(0.0, 0.0);
|
||||||
for (const FRadialMenuItem &Item : Layout->GetItems())
|
for (const FRadialMenuItem &Item : Items)
|
||||||
{
|
{
|
||||||
for (const FVector2D &P : { Item.Point1, Item.Point2, Item.Point3 })
|
for (const FVector2D &P : { Item.Point1, Item.Point2, Item.Point3 })
|
||||||
{
|
{
|
||||||
@@ -175,120 +155,149 @@ protected:
|
|||||||
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
|
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
|
||||||
int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override
|
int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override
|
||||||
{
|
{
|
||||||
if (!Layout.IsValid()) return LayerId;
|
|
||||||
|
|
||||||
const FVector2D Center = AllottedGeometry.GetLocalSize() * 0.5;
|
const FVector2D Center = AllottedGeometry.GetLocalSize() * 0.5;
|
||||||
const FPaintGeometry PaintGeom = AllottedGeometry.ToPaintGeometry();
|
const FPaintGeometry PaintGeom = AllottedGeometry.ToPaintGeometry();
|
||||||
|
|
||||||
for (const FRadialMenuItem &Item : Layout->GetItems())
|
for (int32 I = 0; I < Items.Num(); I++)
|
||||||
{
|
{
|
||||||
|
const FRadialMenuItem& Item = Items[I];
|
||||||
|
const bool bSelected = (I == Config.SelectedItem);
|
||||||
|
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.LineColor;
|
||||||
|
const float Thickness = bSelected ? Config.SelectedThickness : Config.LineThickness;
|
||||||
TArray<FVector2D> Points;
|
TArray<FVector2D> Points;
|
||||||
Points.Add(Center + Item.Point1);
|
Points.Add(Center + Item.Point1);
|
||||||
Points.Add(Center + Item.Point2);
|
Points.Add(Center + Item.Point2);
|
||||||
Points.Add(Center + Item.Point3);
|
Points.Add(Center + Item.Point3);
|
||||||
FSlateDrawElement::MakeLines(OutDrawElements, LayerId, PaintGeom, Points, ESlateDrawEffect::None, LineColor, true, LineThickness);
|
FSlateDrawElement::MakeLines(OutDrawElements, LayerId, PaintGeom, Points, ESlateDrawEffect::None, Color, true, Thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DotRadius <= 0.0f) return LayerId;
|
if (Config.DotRadius <= 0.0f) return LayerId;
|
||||||
if (!DotTexture.IsValid()) return LayerId;
|
if (Config.DotTexture == nullptr) return LayerId;
|
||||||
|
|
||||||
const FVector2D DotSize(DotRadius * 2.0f);
|
FSlateBrush DotBrush;
|
||||||
for (const FRadialMenuItem &Item : Layout->GetItems())
|
DotBrush.SetResourceObject(Config.DotTexture);
|
||||||
|
const FVector2D DotSize(Config.DotRadius * 2.0f);
|
||||||
|
for (int32 I = 0; I < Items.Num(); I++)
|
||||||
{
|
{
|
||||||
|
const FRadialMenuItem& Item = Items[I];
|
||||||
|
const bool bSelected = (I == Config.SelectedItem);
|
||||||
|
const FLinearColor& Color = bSelected ? Config.SelectedColor : Config.LineColor;
|
||||||
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,
|
||||||
AllottedGeometry.ToPaintGeometry(DotSize, FSlateLayoutTransform(LocalPoint - FVector2D(DotRadius))),
|
AllottedGeometry.ToPaintGeometry(DotSize, FSlateLayoutTransform(LocalPoint - FVector2D(Config.DotRadius))),
|
||||||
&DotBrush,
|
&DotBrush,
|
||||||
ESlateDrawEffect::None,
|
ESlateDrawEffect::None,
|
||||||
LineColor);
|
Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return LayerId;
|
return LayerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TWeakObjectPtr<URadialMenuLayout> Layout;
|
FRadialMenuConfig Config;
|
||||||
float LineThickness = 1.0f;
|
|
||||||
FLinearColor LineColor = FLinearColor::White;
|
TArray<FRadialMenuItem> Items;
|
||||||
TWeakObjectPtr<UTexture2D> DotTexture;
|
|
||||||
FSlateBrush DotBrush;
|
|
||||||
float DotRadius = 0.0f;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void URadialMenuWidget::SetNumItems(int32 NewNumItems)
|
void URadialMenuWidget::SetNumItems(int32 NewNumItems)
|
||||||
{
|
{
|
||||||
if (NumItems == NewNumItems) return;
|
if (Config.NumItems == NewNumItems) return;
|
||||||
|
|
||||||
NumItems = NewNumItems;
|
Config.NumItems = NewNumItems;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetItemHeight(float NewItemHeight)
|
void URadialMenuWidget::SetItemHeight(float NewItemHeight)
|
||||||
{
|
{
|
||||||
if (ItemHeight == NewItemHeight) return;
|
if (Config.ItemHeight == NewItemHeight) return;
|
||||||
|
|
||||||
ItemHeight = NewItemHeight;
|
Config.ItemHeight = NewItemHeight;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetInnerRadius(float NewInnerRadius)
|
void URadialMenuWidget::SetInnerRadius(float NewInnerRadius)
|
||||||
{
|
{
|
||||||
if (InnerRadius == NewInnerRadius) return;
|
if (Config.InnerRadius == NewInnerRadius) return;
|
||||||
|
|
||||||
InnerRadius = NewInnerRadius;
|
Config.InnerRadius = NewInnerRadius;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetMinSpoke(float NewMinSpoke)
|
void URadialMenuWidget::SetMinSpoke(float NewMinSpoke)
|
||||||
{
|
{
|
||||||
if (MinSpoke == NewMinSpoke) return;
|
if (Config.MinSpoke == NewMinSpoke) return;
|
||||||
|
|
||||||
MinSpoke = NewMinSpoke;
|
Config.MinSpoke = NewMinSpoke;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetSpread(float NewSpread)
|
void URadialMenuWidget::SetSpread(float NewSpread)
|
||||||
{
|
{
|
||||||
if (Spread == NewSpread) return;
|
if (Config.Spread == NewSpread) return;
|
||||||
|
|
||||||
Spread = NewSpread;
|
Config.Spread = NewSpread;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetLineThickness(float NewLineThickness)
|
void URadialMenuWidget::SetLineThickness(float NewLineThickness)
|
||||||
{
|
{
|
||||||
if (LineThickness == NewLineThickness) return;
|
if (Config.LineThickness == NewLineThickness) return;
|
||||||
|
|
||||||
LineThickness = NewLineThickness;
|
Config.LineThickness = NewLineThickness;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetLineColor(FLinearColor NewLineColor)
|
void URadialMenuWidget::SetLineColor(FLinearColor NewLineColor)
|
||||||
{
|
{
|
||||||
if (LineColor == NewLineColor) return;
|
if (Config.LineColor == NewLineColor) return;
|
||||||
|
|
||||||
LineColor = NewLineColor;
|
Config.LineColor = NewLineColor;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetDotTexture(UTexture2D* NewDotTexture)
|
void URadialMenuWidget::SetDotTexture(UTexture2D* NewDotTexture)
|
||||||
{
|
{
|
||||||
if (DotTexture == NewDotTexture) return;
|
if (Config.DotTexture == NewDotTexture) return;
|
||||||
|
|
||||||
DotTexture = NewDotTexture;
|
Config.DotTexture = NewDotTexture;
|
||||||
SynchronizeProperties();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
void URadialMenuWidget::SetDotRadius(float NewDotRadius)
|
void URadialMenuWidget::SetDotRadius(float NewDotRadius)
|
||||||
{
|
{
|
||||||
if (DotRadius == NewDotRadius) return;
|
if (Config.DotRadius == NewDotRadius) return;
|
||||||
|
|
||||||
DotRadius = NewDotRadius;
|
Config.DotRadius = NewDotRadius;
|
||||||
|
SynchronizeProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URadialMenuWidget::SetSelectedItem(int32 NewSelectedItem)
|
||||||
|
{
|
||||||
|
if (Config.SelectedItem == NewSelectedItem) return false;
|
||||||
|
|
||||||
|
Config.SelectedItem = NewSelectedItem;
|
||||||
|
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();
|
SynchronizeProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,29 +305,64 @@ void URadialMenuWidget::SynchronizeProperties()
|
|||||||
{
|
{
|
||||||
Super::SynchronizeProperties();
|
Super::SynchronizeProperties();
|
||||||
|
|
||||||
if (!Layout)
|
Items = FRadialMenuItem::Calculate(Config);
|
||||||
|
if (Items.Num() == 0)
|
||||||
{
|
{
|
||||||
Layout = NewObject<URadialMenuLayout>(this);
|
PointerVector = FVector2D(0,0);
|
||||||
}
|
}
|
||||||
Layout->Configure(NumItems, ItemHeight, InnerRadius, MinSpoke, Spread);
|
|
||||||
if (MySlateWidget.IsValid())
|
if (MySlateWidget.IsValid())
|
||||||
{
|
{
|
||||||
MySlateWidget->SetLineThickness(LineThickness);
|
MySlateWidget->SetConfig(Config);
|
||||||
MySlateWidget->SetLineColor(LineColor);
|
|
||||||
MySlateWidget->SetDotTexture(DotTexture);
|
|
||||||
MySlateWidget->SetDotRadius(DotRadius);
|
|
||||||
MySlateWidget->Refresh();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool URadialMenuWidget::AddPointer(FVector2D Direction, float Scale)
|
||||||
|
{
|
||||||
|
if (!IsVisible())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Items.Num() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerVector += (Direction * Scale);
|
||||||
|
|
||||||
|
if (PointerVector.Length() < 0.5)
|
||||||
|
{
|
||||||
|
return SetSelectedItem(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PointerVector.Length() > 1.0)
|
||||||
|
{
|
||||||
|
PointerVector.Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 BestIndex = -1;
|
||||||
|
double BestDot = -2.0;
|
||||||
|
for (int32 I = 0; I < Items.Num(); I++)
|
||||||
|
{
|
||||||
|
FVector2D SpokeDir = Items[I].Point1;
|
||||||
|
if (!SpokeDir.Normalize()) continue;
|
||||||
|
double Dot = FVector2D::DotProduct(SpokeDir, PointerVector);
|
||||||
|
if (Dot <= BestDot) continue;
|
||||||
|
BestDot = Dot;
|
||||||
|
BestIndex = I;
|
||||||
|
}
|
||||||
|
return SetSelectedItem(BestIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLinearColor URadialMenuWidget::GetItemColor(int N) const
|
||||||
|
{
|
||||||
|
return (N == Config.SelectedItem) ? Config.SelectedColor : Config.LineColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TSharedRef<SWidget> URadialMenuWidget::RebuildWidget()
|
TSharedRef<SWidget> URadialMenuWidget::RebuildWidget()
|
||||||
{
|
{
|
||||||
if (!Layout)
|
MySlateWidget = SNew(SRadialMenu, Config);
|
||||||
{
|
|
||||||
Layout = NewObject<URadialMenuLayout>(this);
|
|
||||||
}
|
|
||||||
MySlateWidget = SNew(SRadialMenu, Layout);
|
|
||||||
SynchronizeProperties();
|
|
||||||
return MySlateWidget.ToSharedRef();
|
return MySlateWidget.ToSharedRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,49 @@ class SRadialMenu;
|
|||||||
class UTexture2D;
|
class UTexture2D;
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FRadialMenuConfig
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
int32 NumItems = 8;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
float ItemHeight = 20.0f;
|
||||||
|
|
||||||
|
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 LineColor = FLinearColor::White;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
TObjectPtr<UTexture2D> DotTexture;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category="RadialMenu")
|
||||||
|
float DotRadius = 3.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)
|
USTRUCT(BlueprintType)
|
||||||
struct FRadialMenuItem
|
struct FRadialMenuItem
|
||||||
{
|
{
|
||||||
@@ -29,26 +72,8 @@ struct FRadialMenuItem
|
|||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly)
|
UPROPERTY(BlueprintReadOnly)
|
||||||
bool RightSide = false;
|
bool RightSide = false;
|
||||||
};
|
|
||||||
|
|
||||||
UCLASS(BlueprintType)
|
|
||||||
class URadialMenuLayout : public UObject
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
void Configure(int32 NItems, float ItemHeight, float InnerRadius, float MinSpoke, float Spread);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
int32 LeftNum() const { return NumLeft; }
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
int32 RightNum() const { return NumRight; }
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
|
||||||
const TArray<FRadialMenuItem> &GetItems() const { return Items; }
|
|
||||||
|
|
||||||
|
static TArray<FRadialMenuItem> Calculate(const FRadialMenuConfig &Config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using View = TArrayView<FRadialMenuItem>;
|
using View = TArrayView<FRadialMenuItem>;
|
||||||
@@ -59,31 +84,24 @@ private:
|
|||||||
// spokes on both sides. The spokes on a given side are
|
// spokes on both sides. The spokes on a given side are
|
||||||
// organized top-to-bottom, and the angle between the
|
// organized top-to-bottom, and the angle between the
|
||||||
// spokes is always equal to (1/NTotal) of the circle.
|
// spokes is always equal to (1/NTotal) of the circle.
|
||||||
FVector2D SpokeVector(int32 I, int32 NSide, int32 NTotal);
|
static FVector2D SpokeVector(int32 I, int32 NSide, int32 NTotal);
|
||||||
|
|
||||||
// Populate Point1 and Point2, these are the
|
// Populate Point1 and Point2, these are the
|
||||||
// endpoints of the spoke segment. Spokes are
|
// endpoints of the spoke segment. Spokes are
|
||||||
// designed to always be long enough to make room
|
// designed to always be long enough to make room
|
||||||
// for MinSpoke, but also to make enough room to
|
// for MinSpoke, but also to make enough room to
|
||||||
// keep the menu items from overlapping.
|
// keep the menu items from overlapping.
|
||||||
void CalculateSpokes(View V, float ItemHeight, float InnerRadius, float MinSpoke);
|
static void CalculateSpokes(View V, int32 TotalItems, float ItemHeight, float InnerRadius, float MinSpoke);
|
||||||
|
|
||||||
// Search for the widest spoke, and return its X coordinate.
|
// Search for the widest spoke, and return its X coordinate.
|
||||||
double WidestSpoke(View V);
|
static double WidestSpoke(View V);
|
||||||
|
|
||||||
// Populate Point3, this is the endpoint of the spread
|
// Populate Point3, this is the endpoint of the spread
|
||||||
// line that goes horizontal.
|
// line that goes horizontal.
|
||||||
void CalculateSpread(View V, double Offset);
|
static void CalculateSpread(View V, double Offset);
|
||||||
|
|
||||||
// Flip everything in the specified view horizontally.
|
// Flip everything in the specified view horizontally.
|
||||||
void FlipHorizontal(View V);
|
static void FlipHorizontal(View V);
|
||||||
|
|
||||||
// The array of items.
|
|
||||||
TArray<FRadialMenuItem> Items;
|
|
||||||
|
|
||||||
// Number of items on the left, and on the right.
|
|
||||||
int32 NumLeft = 0;
|
|
||||||
int32 NumRight = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -121,43 +139,38 @@ public:
|
|||||||
void SetDotRadius(float NewDotRadius);
|
void SetDotRadius(float NewDotRadius);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
URadialMenuLayout* GetLayout() const { return Layout; }
|
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.
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
bool AddPointer(FVector2D Direction, float Scale);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
FVector2D GetPointer() const { return PointerVector; }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
FLinearColor GetItemColor(int N) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
UPROPERTY(EditAnywhere)
|
||||||
|
FRadialMenuConfig Config;
|
||||||
|
|
||||||
|
TArray<FRadialMenuItem> Items;
|
||||||
|
|
||||||
|
FVector2D PointerVector = {0,0};
|
||||||
|
|
||||||
virtual TSharedRef<SWidget> RebuildWidget() override;
|
virtual TSharedRef<SWidget> RebuildWidget() override;
|
||||||
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
|
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
|
||||||
virtual void SynchronizeProperties() override;
|
virtual void SynchronizeProperties() override;
|
||||||
|
|
||||||
private:
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(ClampMin="0", AllowPrivateAccess="true"))
|
|
||||||
int32 NumItems = 8;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float ItemHeight = 20.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float InnerRadius = 20.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float MinSpoke = 20.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float Spread = 20.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float LineThickness = 2.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
FLinearColor LineColor = FLinearColor::White;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
TObjectPtr<UTexture2D> DotTexture;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Setter, Category="RadialMenu", meta=(AllowPrivateAccess="true"))
|
|
||||||
float DotRadius = 0.0f;
|
|
||||||
|
|
||||||
UPROPERTY()
|
|
||||||
TObjectPtr<URadialMenuLayout> Layout;
|
|
||||||
|
|
||||||
TSharedPtr<SRadialMenu> MySlateWidget;
|
TSharedPtr<SRadialMenu> MySlateWidget;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user