Skip to content

Value Converters

In Unreal Engine, value converters typically turn structures into values that a page can display or edit. For example, a converter can format a first and last name as one string. When a two-way binding submits input, the converter can parse the string and return a structure of the same type for whole-property writeback.

Supported Source Types

The Unreal Engine plugin's structure converter accepts reflected USTRUCT types as sources. Reverse conversion writes back the same USTRUCT type. This interface excludes plain C++ structs and UObject references as sources. Forward conversion can target String or another value type the page accepts.

For non-Unreal types, see Markup UI C++ Value Converters for definition and registration.

Define Converter

Data Model

Create PlayerProfile.h in your project's C++ module. Define FPlayerName with USTRUCT and expose the model's FullName with UPROPERTY(BlueprintReadWrite) so the binding can replace the whole structure.

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "PlayerProfile.generated.h"

USTRUCT(BlueprintType)
struct FPlayerName
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Profile")
    FString FirstName = TEXT("Ada");

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Profile")
    FString LastName = TEXT("Lovelace");
};

UCLASS()
class UPlayerProfile : public UObject
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Profile")
    FPlayerName FullName;
};

bool RegisterPlayerNameConverter();

Converter Class

In PlayerProfile.cpp, derive from TUnrealStructValueConverter<FPlayerName, ValueType::String>. Implement ConvertValue to produce display text and ConvertBackValue to build a new FPlayerName from the input.

#include "PlayerProfile.h"
#include "MarkupUISdk.h"
#include "StringUtilities.h"

class FPlayerNameConverter final : public MarkupUI::Binding::TUnrealStructValueConverter<
    FPlayerName, MarkupUI::ValueType::String>
{
    using Value = MarkupUI::Binding::Value;

protected:
    Value ConvertValue(const FPlayerName& Input, const Value&) const override
    {
        const FString Text = Input.FirstName + TEXT(", ") + Input.LastName;
        return MarkupUI::StringUtilities::WithUtf8View(Text, [](MarkupUI::StringView Utf8)
        {
            return Value::String(Utf8);
        });
    }

    TOptional<FPlayerName> ConvertBackValue(const Value& Input, const Value&) const override
    {
        MarkupUI::StringView Utf8;
        if (!Input.GetString(Utf8))
        {
            return {};
        }

        const FString Text = MarkupUI::StringUtilities::ToString(Utf8);
        FPlayerName Name;
        if (!Text.Split(TEXT(", "), &Name.FirstName, &Name.LastName) ||
            Name.FirstName.IsEmpty() || Name.LastName.IsEmpty() ||
            Name.FirstName.Contains(TEXT(",")) || Name.LastName.Contains(TEXT(",")))
        {
            return {};
        }

        return Name;
    }
};

This example expects first name, last name and returns an empty TOptional<FPlayerName> if parsing fails.

Register

Call RegisterPlayerNameConverter() once on the game thread. Check that registration succeeds before opening a page that uses it.

bool RegisterPlayerNameConverter()
{
    return MarkupUI::Binding::RegisterUnrealStructValueConverter<FPlayerNameConverter>(
        MARKUPUI_STR_VIEW("FormatPlayerName"));
}

Use It in HTML

Set a UPlayerProfile instance as the page's data context. Bind both the text and the input to FullName and use converter to select the registered name.

<p @text="{FullName, converter=FormatPlayerName}"></p>
<label>
    Name
    <input type="text"
           @value="{FullName, converter=FormatPlayerName, mode=twoWay, update=blur}">
</label>

The page initially displays Ada, Lovelace. Enter Grace, Hopper and leave the input. The binding replaces FullName with a new structure whose FirstName is Grace and LastName is Hopper. See Value Converters and Input and Two-Way Binding for binding options and submission timing.