Context Menus

The context menu system lets actors provide right-click menus that are rendered as a native Slate widget (SContextMenuWidget). Any actor implementing IContextMenuProvider can supply menu items.

Native UI: The context menu is fully native Slate (zinc/indigo design tokens), positioned directly at the cursor in C++ and clamped to the viewport. It no longer uses a CEF web overlay.

Architecture

Alt + Right-Click
      │
      ▼
  Line Trace (from cursor position)
      │
      ▼
  Hit Actor → implements IContextMenuProvider?
      │
      ▼ Yes
  UContextMenuManager::TryOpenContextMenu()
      │
      ▼
  SContextMenuWidget::ShowMenu(displayName, items, cursorPos, viewportSize)
  (native Slate panel positioned at the cursor, clamped on-screen)
      │
      ▼
  User clicks an item → OnSelect(actionId) callback
      │
      ▼
  IContextMenuProvider::OnContextMenuAction(ActionId)

C++ Interface — IContextMenuProvider

UINTERFACE(MinimalAPI, Blueprintable)
class UContextMenuProvider : public UInterface
{
    GENERATED_BODY()
};

class IContextMenuProvider
{
    GENERATED_BODY()
public:
    virtual FString GetContextMenuDisplayName() const = 0;
    virtual TArray<FContextMenuItem> GetContextMenuItems() const = 0;
    virtual void OnContextMenuAction(const FString& ActionId) = 0;
};

FContextMenuItem

USTRUCT(BlueprintType)
struct FContextMenuItem
{
    GENERATED_BODY()

    UPROPERTY() FString ActionId;
    UPROPERTY() FString Label;
    UPROPERTY() FString Icon;     // Key into icon map (search, trash, edit, etc.)
    UPROPERTY() bool bEnabled;
    UPROPERTY() bool bDestructive;
};

Example — ADynamicBlock

ADynamicBlock implements IContextMenuProvider to provide block-specific actions:

FString ADynamicBlock::GetContextMenuDisplayName() const
{
    return FString::Printf(TEXT("Block (%s)"), *BlockId);
}

TArray<FContextMenuItem> ADynamicBlock::GetContextMenuItems() const
{
    return {
        { "inspect", "Inspect Block", "search", true, false },
        { "copy",    "Copy Block",    "copy",   true, false },
        { "delete",  "Delete Block",  "trash",  true, true  },
    };
}

void ADynamicBlock::OnContextMenuAction(const FString& ActionId)
{
    if (ActionId == "delete") { Destroy(); }
    // Handle other actions...
}

Example — AReplicatedBlock

Live, server-authoritative blocks (AReplicatedBlock) implement IContextMenuProvider to expose build-mode editing actions:

TArray<FContextMenuItem> AReplicatedBlock::GetContextMenuItems() const
{
    return {
        { "edit",      "Edit",      "edit",  true, false },
        { "duplicate", "Duplicate", "copy",  true, false },
        { "delete",    "Delete",    "trash", true, true  },
    };
}

OnContextMenuAction routes these to the local player controller: editAGridsPlayerController::BeginEditBlock (opens the Edit Object panel), deleteServerRequestBlockRemove, and duplicateServerRequestBlockPlace with the same shape, texture, rotation, and scale.

Rendering — native Slate

The menu is rendered by SContextMenuWidget (Source/Grids/Edge/UI/SContextMenuWidget.h/.cpp), added to the viewport once by UContextMenuManager and reused. ShowMenu builds a SGridsPanel with one SGridsButton per FContextMenuItem:

Selecting an item raises the OnSelect(actionId) callback, which UContextMenuManager forwards to IContextMenuProvider::OnContextMenuAction on the target actor, then dismisses the menu.

The Icon field on FContextMenuItem is currently unused by the native renderer (the former web overlay mapped icon keys to emoji); labels are shown without icons.