Component Visualizer GetWidgetLocation does not set widget location

Following [this wiki on component visualizers][1], I am currently implementing a component visualizer for a Spawner component which spawns flocks of pawns at the specified locations.

The spawn points are represented with a sprite and an arrow which shows the initial orientation of the item to spawn. The red spawn point is selected and the white spawn points are unselected roughly following the implementation defined in the wiki article.

The spawn points successfully receive key input which allows me to delete, cycle through, and move them with keyboard input, so I know the visualizer can successfuly communicate with the proper component and edit it. However, the GetWidgetLocation method does not set the widget location for handling the widget input delta:

bool FFlockSpawnPointVisualizer::GetWidgetLocation(
  const FEditorViewportClient *ViewportClient,
  FVector &OutLocation) const {

  if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {

    auto Comp = GetEditedSpawner();
    auto Pos = Comp->SpawnLocations[CurrentlySelectedTarget].Position;
    OutLocation = Pos;

    UE_LOG(
      LogTemp, Display, TEXT("Draw Widget for %i"), CurrentlySelectedTarget);

    return true;
  }

  return false;
}

However, the widget never appears with the selected spawner!

294509-log.png

The log shoes the correct code path is reached, but the widget is never drawn over the spawn point.

294510-nowidget.png

Here is the full .cpp source code for the visualizer:

#include "FlockSpawnerEditor.h"

IMPLEMENT_HIT_PROXY(HSpawnVisProxy, HComponentVisProxy)

IMPLEMENT_HIT_PROXY(HFlockSpawnProxy, HSpawnVisProxy)

FFlockSpawnPointVisualizer::FFlockSpawnPointVisualizer() {}

FFlockSpawnPointVisualizer::~FFlockSpawnPointVisualizer() {}

void FFlockSpawnPointVisualizer::OnRegister() {}

void FFlockSpawnPointVisualizer::DrawVisualization(
  const UActorComponent *Component,
  const FSceneView *View,
  FPrimitiveDrawInterface *PDI) {

  auto *Spawner = Cast<const UFlockSpawnerComponent>(Component);
  if (Spawner) {

    for (int32 i = 0; i < Spawner->SpawnLocations.Num(); ++i) {
      auto Spawn = Spawner->SpawnLocations[i];
      FLinearColor Color = (i == CurrentlySelectedTarget)
                             ? Spawner->SelectedColor
                             : Spawner->SpawnPointColor;

      PDI->SetHitProxy(new HFlockSpawnProxy(Component, i));
      auto ArrowLength = 25.0;
      FTransform ArrowTransform(
        Spawn.Orientation,
        Spawn.Position + Spawn.Orientation.Vector() * ArrowLength,
        FVector(1.0));
      DrawConnectedArrow(
        PDI,
        ArrowTransform.ToMatrixNoScale(),
        Color,
        ArrowLength,
        1.5,
        SDPG_Foreground,
        1.0,
        8);

      auto Sprite = Spawner->SpawnPointSprite;
      if (Sprite) {
        PDI->DrawSprite(
          Spawn.Position + FVector(0.0, 0.0, 15.0),
          20.0,
          20.0,
          Sprite->Resource,
          Color,
          SDPG_World,
          0.0,
          Sprite->Resource->GetSizeX(),
          0.0,
          Sprite->Resource->GetSizeY(),
          BLEND_Masked);
      }
      PDI->SetHitProxy(NULL);
    }
  }
}

void FFlockSpawnPointVisualizer::DrawVisualizationHUD(
  const UActorComponent *Component,
  const FViewport *Viewport,
  const FSceneView *View,
  FCanvas *Canvas) {
  // Canvas->DrawItem(FCanvasTextItem(FVector2D(10.0, 10.0), ));
}

bool FFlockSpawnPointVisualizer::VisProxyHandleClick(
  FEditorViewportClient *InViewportClient,
  HComponentVisProxy *VisProxy,
  const FViewportClick &Click) {

  bool bEditing = false;

  if (VisProxy && VisProxy->Component.IsValid()) {
    bEditing = true;

    const UFlockSpawnerComponent *Component
      = CastChecked<const UFlockSpawnerComponent>(VisProxy->Component);
    CompPropName = GetComponentPropertyName(Component);

    AActor *OldOwner = OwningActor.Get();
    OwningActor = Component->GetOwner();

    if (OwningActor != OldOwner) {
      CurrentlySelectedTarget = INDEX_NONE;
    }

    if (VisProxy->IsA(HFlockSpawnProxy::StaticGetType())) {
      HFlockSpawnProxy *Proxy = (HFlockSpawnProxy *)VisProxy;
      CurrentlySelectedTarget = Proxy->TargetIndex;
    }

  } else {
    CurrentlySelectedTarget = INDEX_NONE;
  }

  return bEditing;
}

void FFlockSpawnPointVisualizer::EndEditing() {
  CurrentlySelectedTarget = INDEX_NONE;
}

bool FFlockSpawnPointVisualizer::GetWidgetLocation(
  const FEditorViewportClient *ViewportClient,
  FVector &OutLocation) const {

  if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {

    auto Comp = GetEditedSpawner();
    auto Pos = Comp->SpawnLocations[CurrentlySelectedTarget].Position;
    OutLocation = Pos;

    UE_LOG(
      LogTemp, Display, TEXT("Draw Widget for %i"), CurrentlySelectedTarget);

    return true;
  }

  return false;
}

bool FFlockSpawnPointVisualizer::HandleInputDelta(
  FEditorViewportClient *ViewportClient,
  FViewport *Viewport,
  FVector &DeltaTranslate,
  FRotator &DeltaRotate,
  FVector &DeltaScale) {

  bool bHandled = false;

  if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {

    auto SpawnPoint
      = GetEditedSpawner()->SpawnLocations[CurrentlySelectedTarget];
    SpawnPoint.Position += DeltaTranslate;
    SpawnPoint.Orientation += DeltaRotate;
    SpawnPoint.InitialVelocity += DeltaScale.Size();
    bHandled = true;
  }

  return bHandled;
}

bool FFlockSpawnPointVisualizer::HandleInputKey(
  FEditorViewportClient *ViewportClient,
  FViewport *Viewport,
  FKey Key,
  EInputEvent Event) {

  if (!GetEditedSpawner() || CurrentlySelectedTarget == INDEX_NONE)
    return false;

  auto &Spawns = GetEditedSpawner()->SpawnLocations;
  auto &Selected = Spawns[CurrentlySelectedTarget];

  if (Key == EKeys::Delete) {
    Spawns.RemoveAt(CurrentlySelectedTarget);
    return true;
  } else if (Key == EKeys::Up) {
    Selected.Position
      += Selected.Orientation.RotateVector(FVector::ForwardVector);
    return true;
  } else if (Key == EKeys::Down) {
    Selected.Position
      -= Selected.Orientation.RotateVector(FVector::ForwardVector);
    return true;
  } else if (Key == EKeys::Left) {
    Selected.Position
      -= Selected.Orientation.RotateVector(FVector::RightVector);
    return true;
  } else if (Key == EKeys::Right) {
    Selected.Position
      += Selected.Orientation.RotateVector(FVector::RightVector);
    return true;
  } else if (Key == EKeys::PageUp) {
    Selected.Position += Selected.Orientation.RotateVector(FVector::UpVector);
    return true;
  } else if (Key == EKeys::PageDown) {
    Selected.Position -= Selected.Orientation.RotateVector(FVector::UpVector);
    return true;
  }

  return false;
}

TSharedPtr<SWidget> FFlockSpawnPointVisualizer::GenerateContextMenu() const {
  return TSharedPtr<SWidget>();
}

UFlockSpawnerComponent *FFlockSpawnPointVisualizer::GetEditedSpawner() const {
  if (!OwningActor.IsValid())
    return nullptr;

  return Cast<UFlockSpawnerComponent>(
    GetComponentFromPropertyName(OwningActor.Get(), CompPropName));
}

This is almost certainly too late for you, but I found your post while I was trying to figure it out so I’ll leave an answer for anybody else in my situation:

You need to override:

virtual bool IsVisualizingArchetype() const;

to return true, otherwise GetWidgetLocation() is ignored by the editor.