Files
Naked-Desire/Source/NakedDesire/UI/RadialMenu/RadialMenuWidget.h
T
2026-05-25 23:13:25 +03:00

107 lines
4.4 KiB
C++

// RadialMenuWidget.h
// Container for the radial menu, now a CommonUI activatable widget. It owns the
// CommonUI input bindings (Confirm / Back) and routes them to the controller,
// which still owns all state (time dilation, hover, Entries). The widget also
// spawns one URadialSliceWidget per entry at runtime (variable slice count).
#pragma once
#include "CoreMinimal.h"
#include "CommonActivatableWidget.h"
#include "Engine/DataTable.h" // FDataTableRowHandle
#include "RadialMenuController.h" // for FRadialMenuEntry
#include "RadialMenuWidget.generated.h"
class UCanvasPanel;
class URadialSliceWidget;
struct FUIActionBindingHandle;
UCLASS(Abstract)
class URadialMenuWidget : public UCommonActivatableWidget
{
GENERATED_BODY()
public:
// Called by the controller after CreateWidget, before activation. Stores the
// owning controller so action handlers can route back to it, then builds the
// slices.
void InitializeFromController(URadialMenuController* InController,
const TArray<FRadialMenuEntry>& InEntries);
// Called when the hovered slice changes. Stored and applied each tick.
void SetHoveredSegment(int32 Index);
protected:
virtual void NativeOnActivated() override;
virtual void NativeOnDeactivated() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
// Left-click confirm. Handled directly here rather than as a CommonUI action
// binding, because CommonUI routes left mouse through its Default Click Action
// system (separate from RegisterUIActionBinding), so a click-mapped action
// never reaches a normal binding. Intercepting the pointer event sidesteps that.
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry,
const FPointerEvent& InMouseEvent) override;
// Gamepad slice selection. When this widget is the active CommonUI node, the
// analog sticks are routed here (NOT to the PlayerController), which is why
// GetInputAnalogStickState returns 0 from the controller while a menu is up.
// We read the right stick from the analog event and drive hover directly.
virtual FReply NativeOnAnalogValueChanged(const FGeometry& InGeometry,
const FAnalogInputEvent& InAnalogEvent) override;
// CommonUI focus: returning the root keeps gamepad/keyboard focus on us so
// the bound actions fire while the wheel is open.
virtual UWidget* NativeGetDesiredFocusTarget() const override;
// --- CommonUI input action assets (assign in the widget BP defaults) ------
// CommonUI input action rows. These are FDataTableRowHandle pointing at your
// CommonInputActionDataTable rows (the same rows your other screens use for
// accept/back), so the on-screen action bar and key mapping stay consistent.
UPROPERTY(EditDefaultsOnly, Category = "Radial Menu|Input",
meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"))
FDataTableRowHandle ConfirmAction;
// Bound explicitly so our cancel path runs (restore time, broadcast -1)
// rather than CommonUI just popping the widget on Back.
UPROPERTY(EditDefaultsOnly, Category = "Radial Menu|Input",
meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"))
FDataTableRowHandle BackAction;
// Root canvas the slices are spawned into.
UPROPERTY(meta = (BindWidget))
TObjectPtr<UCanvasPanel> SliceCanvas = nullptr;
UPROPERTY(EditDefaultsOnly, Category = "Radial Menu")
TSubclassOf<URadialSliceWidget> SliceWidgetClass;
UPROPERTY(EditDefaultsOnly, Category = "Radial Menu")
TObjectPtr<UMaterialInterface> SliceMaterial = nullptr;
UPROPERTY(EditDefaultsOnly, Category = "Radial Menu")
float MenuDiameter = 420.f;
private:
// Action handlers, bound in NativeOnActivated.
void HandleConfirm();
void HandleBack();
void BuildSlices(const TArray<FRadialMenuEntry>& InEntries);
UPROPERTY(Transient)
TWeakObjectPtr<URadialMenuController> OwningController;
UPROPERTY(Transient)
TArray<TObjectPtr<URadialSliceWidget>> Slices;
// Handles kept so we can unregister on deactivation.
FUIActionBindingHandle ConfirmHandle;
FUIActionBindingHandle BackHandle;
int32 HoveredIndex = -1;
// Latest right-stick axis values, cached across single-axis analog events so
// we can recompute the full 2D direction whenever either axis updates.
float RightStickX = 0.f;
float RightStickY = 0.f;
};