TMap with USTRUCT(FName wrapper) as key displays unusable UI in Details panel

Hello,

I am trying to keep strong typing in C++ while still having a usable DataAsset workflow in the editor.

I have a simple goal:

I want a TMap that uses a strongly typed FName to behave exactly like a normal TMap<FName, FName> in the Details panel.

Right now these two properties behave very differently in the editor:

TMap<FName, FName> → nice inline editable key field (works perfectly)

TMap<FGameIdDialogueScene, FName> → nested struct UI that makes authoring the map extremely painful when it grows :

Here is the code:

UCLASS(BlueprintType)
class MYGAME_API UTMapTestDataAsset : public UDataAsset
{
  GENERATED_BODY()

public:
  UPROPERTY(EditDefaultsOnly)
  TMap<FGameIdDialogueScene, FName> mMapWithFGameIdDialogueSceneAsKey{};

  UPROPERTY(EditDefaultsOnly)
  TMap<FName, FName> mMapWithFNameAsKey{};
};

USTRUCT(BlueprintType)
struct MYGAME_API FGameIdDialogueScene
{
  GENERATED_BODY()

public:
  FGameIdDialogueScene() = default;

  explicit FGameIdDialogueScene(const FName InValue) : mValue(InValue) {}

  bool IsValid() const { return mValue != NAME_None; }

  friend bool operator==(const FGameIdDialogueScene& A, const FGameIdDialogueScene& B)
  {
	  return A.mValue == B.mValue;
  }

  friend uint32 GetTypeHash(const FGameIdDialogueScene& Id)
  {
	  return GetTypeHash(Id.mValue);
  }

  UPROPERTY(EditAnywhere, BlueprintReadWrite)
  FName mValue{ NAME_None };

  static const FGameIdDialogueScene None;
};

For now the only workaround I found is to use a DataTable and use the row name as the value to initialize my ID type. It technically preserves the strong typing in C++, but it forces me to maintain a DataTable and parse it every time, which makes the workflow quite painful and significantly complicates my data structures but its easier to read… So it really feels like a very hacky solution rather than a proper one…

I know that in theory this should be solvable using a Details / Property customization.

The problem is that I already tried to implement one and spent about two full days on it, and I never managed to get a reliable result for a TMap key. Sometimes the text just disappear, sometime the TMap is not even visible…

So at this point I’m not sure whether:

  • I misunderstood how the property system is supposed to be extended

  • or whether TMap keys based on a USTRUCT wrapping an FName simply cannot be made to behave like a native FNameProperty

What I’d really like to know is:

  • Has anyone actually succeeded in making a USTRUCT that wraps an FName display exactly like an FName when used as a TMap key in the Details panel?

And if it is possible:

  • Is this something that must be implemented separately for each struct type, or is there a generic way to support multiple “strong ID” types (for example several FGameId-like structs) so they all render as a single FName field?

Basically I’m completely open to the idea that I did it wrong, but after two days of attempts I need to know if I’m missing a specific hook, or if the engine just doesn’t support this use case and the behavior I’m seeing is expected.

Thanks for any guidance.

It is the default behavior, even when using internal structs like FGameplayTag. You must use the ForceInlineRow meta specifier.

Here is an example I tested with and without it:

Thanks a lot for the suggestion, ForceInlineRow does help.

Unfortunately, as soon as I tried switching the map value from FName to a USTRUCT, the problem came back.

  • TMap<FGameIdDialogueScene, FName> → looks good with ForceInlineRow

  • TMap<FGameIdDialogueScene, FMyStruct> → the nested struct UI returns, and authoring becomes painful again