Files
Naked-Desire/Source/NakedDesire/UI/RadialMenu/RadialMenuWidget.cpp
T
2026-06-03 15:16:21 +03:00

145 lines
3.8 KiB
C++

// RadialMenuWidget.cpp
#include "RadialMenuWidget.h"
#include "RadialSliceWidget.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "Input/CommonUIInputTypes.h" // FBindUIActionArgs
void URadialMenuWidget::InitializeFromController(
URadialMenuController* InController, const TArray<FRadialMenuEntry>& InEntries)
{
OwningController = InController;
BuildSlices(InEntries);
}
void URadialMenuWidget::NativeOnActivated()
{
Super::NativeOnActivated();
SetIsFocusable(true);
// Bind Confirm. FBindUIActionArgs accepts the row handle directly.
if (!ConfirmAction.IsNull())
{
FBindUIActionArgs ConfirmArgs(
ConfirmAction,
FSimpleDelegate::CreateUObject(this, &URadialMenuWidget::HandleConfirm));
// Show in the on-screen action bar so the player sees the prompt.
ConfirmArgs.bDisplayInActionBar = true;
ConfirmHandle = RegisterUIActionBinding(ConfirmArgs);
}
if (!BackAction.IsNull())
{
FBindUIActionArgs BackArgs(
BackAction,
FSimpleDelegate::CreateUObject(this, &URadialMenuWidget::HandleBack));
BackArgs.bDisplayInActionBar = true;
BackHandle = RegisterUIActionBinding(BackArgs);
}
}
void URadialMenuWidget::NativeOnDeactivated()
{
// Clean up bindings so they don't linger if the widget is pooled/reused.
if (ConfirmHandle.IsValid())
{
ConfirmHandle.Unregister();
}
if (BackHandle.IsValid())
{
BackHandle.Unregister();
}
Super::NativeOnDeactivated();
}
UWidget* URadialMenuWidget::NativeGetDesiredFocusTarget() const
{
// Keep focus on the menu root so the bound actions receive input.
return const_cast<URadialMenuWidget*>(this);
}
void URadialMenuWidget::HandleConfirm()
{
// Route to the controller, which owns the state, time dilation, and the
// OnSelectionConfirmed broadcast. The controller also tears down the widget.
if (OwningController.IsValid())
{
OwningController->CloseAndConfirm();
}
}
void URadialMenuWidget::HandleBack()
{
if (OwningController.IsValid())
{
OwningController->CloseAndCancel();
}
}
void URadialMenuWidget::BuildSlices(const TArray<FRadialMenuEntry>& InEntries)
{
if (!SliceCanvas || !SliceWidgetClass)
{
return;
}
SliceCanvas->ClearChildren();
Slices.Reset();
HoveredIndex = -1;
const int32 Count = InEntries.Num();
if (Count == 0)
{
return;
}
const float AngleSize = 360.f / Count;
for (int32 i = 0; i < Count; ++i)
{
URadialSliceWidget* Slice =
CreateWidget<URadialSliceWidget>(GetOwningPlayer(), SliceWidgetClass);
if (!Slice)
{
continue;
}
const float StartAngle = i * AngleSize - AngleSize * 0.5f;
Slice->Setup(SliceMaterial, StartAngle, AngleSize,
InEntries[i].Icon, InEntries[i].bEnabled);
SliceCanvas->AddChild(Slice);
if (UCanvasPanelSlot* CanvasSlot = Cast<UCanvasPanelSlot>(Slice->Slot))
{
CanvasSlot->SetAnchors(FAnchors(0.5f, 0.5f));
CanvasSlot->SetAlignment(FVector2D(0.5f, 0.5f));
CanvasSlot->SetPosition(FVector2D::ZeroVector);
CanvasSlot->SetSize(FVector2D(MenuDiameter, MenuDiameter));
}
Slices.Add(Slice);
}
}
void URadialMenuWidget::SetHoveredSegment(int32 Index)
{
HoveredIndex = Index;
}
void URadialMenuWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
const float RealDelta = FApp::GetDeltaTime(); // undilated
for (int32 i = 0; i < Slices.Num(); ++i)
{
if (Slices[i])
{
Slices[i]->UpdateHighlight(i == HoveredIndex, RealDelta);
}
}
}