EM_Task/CoreUObject/Private/UObject/TextProperty.cpp

225 lines
7.8 KiB
C++
Raw Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "UObject/TextProperty.h"
#include "Internationalization/ITextData.h"
#include "UObject/PropertyPortFlags.h"
#include "UObject/Package.h"
#include "Internationalization/TextNamespaceUtil.h"
#include "Internationalization/TextPackageNamespaceUtil.h"
#include "Internationalization/StringTableRegistry.h"
#include "Internationalization/StringTableCore.h"
#include "Serialization/ArchiveUObjectFromStructuredArchive.h"
IMPLEMENT_FIELD(FTextProperty)
EConvertFromTypeResult FTextProperty::ConvertFromType(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, uint8* Data, UStruct* DefaultsStruct)
{
// Convert serialized string to text.
if (Tag.Type == NAME_StrProperty)
{
FString str;
Slot << str;
FText Text = FText::FromString(str);
Text.TextData->PersistText();
Text.Flags |= ETextFlag::ConvertedProperty;
SetPropertyValue_InContainer(Data, Text, Tag.ArrayIndex);
return EConvertFromTypeResult::Converted;
}
// Convert serialized name to text.
if (Tag.Type == NAME_NameProperty)
{
FName Name;
Slot << Name;
FText Text = FText::FromName(Name);
Text.Flags |= ETextFlag::ConvertedProperty;
SetPropertyValue_InContainer(Data, Text, Tag.ArrayIndex);
return EConvertFromTypeResult::Converted;
}
return EConvertFromTypeResult::UseSerializeItem;
}
bool FTextProperty::Identical_Implementation(const FText& ValueA, const FText& ValueB, uint32 PortFlags)
{
// A culture variant text is never equal to a culture invariant text
// A transient text is never equal to a non-transient text
// An empty text is never equal to a non-empty text
if (ValueA.IsCultureInvariant() != ValueB.IsCultureInvariant() || ValueA.IsTransient() != ValueB.IsTransient() || ValueA.IsEmpty() != ValueB.IsEmpty())
{
return false;
}
// If both texts are empty (see the above check), then they must be equal
if (ValueA.IsEmpty())
{
return true;
}
// If both texts share the same pointer, then they must be equal
if (ValueA.IdenticalTo(ValueB))
{
// Placeholder string table entries will have the same pointer, but should only be considered equal if they're using the same string table and key
if (ValueA.IsFromStringTable() && FTextInspector::GetSourceString(ValueA) == &FStringTableEntry::GetPlaceholderSourceString())
{
FName ValueAStringTableId;
FString ValueAStringTableEntryKey;
FTextInspector::GetTableIdAndKey(ValueA, ValueAStringTableId, ValueAStringTableEntryKey);
FName ValueBStringTableId;
FString ValueBStringTableEntryKey;
FTextInspector::GetTableIdAndKey(ValueB, ValueBStringTableId, ValueBStringTableEntryKey);
return ValueAStringTableId == ValueBStringTableId && ValueAStringTableEntryKey.Equals(ValueBStringTableEntryKey, ESearchCase::CaseSensitive);
}
// Otherwise they're equal
return true;
}
// We compare the display strings in editor (as we author in the native language)
// We compare the display string for culture invariant and transient texts as they don't have an identity
if (GIsEditor || ValueA.IsCultureInvariant() || ValueA.IsTransient())
{
return FTextInspector::GetDisplayString(ValueA).Equals(FTextInspector::GetDisplayString(ValueB), ESearchCase::CaseSensitive);
}
// If we got this far then the texts don't share the same pointer, which means that they can't share the same identity
return false;
}
bool FTextProperty::Identical(const void* A, const void* B, uint32 PortFlags) const
{
const TCppType ValueA = GetPropertyValue(A);
if (B)
{
const TCppType ValueB = GetPropertyValue(B);
return Identical_Implementation(ValueA, ValueB, PortFlags);
}
return FTextInspector::GetDisplayString(ValueA).IsEmpty();
}
void FTextProperty::SerializeItem(FStructuredArchive::FSlot Slot, void* Value, void const* Defaults) const
{
TCppType* TextPtr = GetPropertyValuePtr(Value);
Slot << *TextPtr;
}
void FTextProperty::ExportTextItem(FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const
{
const FText& TextValue = GetPropertyValue(PropertyValue);
if (PortFlags & PPF_ExportCpp)
{
ValueStr += GenerateCppCodeForTextValue(TextValue, FString());
}
else if (PortFlags & PPF_PropertyWindow)
{
if (PortFlags & PPF_Delimited)
{
ValueStr += TEXT("\"");
ValueStr += TextValue.ToString();
ValueStr += TEXT("\"");
}
else
{
ValueStr += TextValue.ToString();
}
}
else
{
FTextStringHelper::WriteToBuffer(ValueStr, TextValue, !!(PortFlags & PPF_Delimited));
}
}
const TCHAR* FTextProperty::ImportText_Internal(const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const
{
FText* TextPtr = GetPropertyValuePtr(Data);
FString TextNamespace;
if (Parent && HasAnyPropertyFlags(CPF_Config))
{
const bool bPerObject = UsesPerObjectConfig(Parent);
if (bPerObject)
{
FString PathNameString;
UPackage* ParentOutermost = Parent->GetOutermost();
if (ParentOutermost == GetTransientPackage())
{
PathNameString = Parent->GetName();
}
else
{
PathNameString = Parent->GetPathName(ParentOutermost);
}
TextNamespace = PathNameString + TEXT(" ") + Parent->GetClass()->GetName();
Parent->OverridePerObjectConfigSection(TextNamespace);
}
else
{
const bool bGlobalConfig = HasAnyPropertyFlags(CPF_GlobalConfig);
UClass* ConfigClass = bGlobalConfig ? GetOwnerClass() : Parent->GetClass();
TextNamespace = ConfigClass->GetPathName();
}
}
FString PackageNamespace;
#if USE_STABLE_LOCALIZATION_KEYS
if (GIsEditor && !(PortFlags & (PPF_DuplicateVerbatim | PPF_DuplicateForPIE)))
{
PackageNamespace = TextNamespaceUtil::EnsurePackageNamespace(Parent);
}
#endif // USE_STABLE_LOCALIZATION_KEYS
return FTextStringHelper::ReadFromBuffer(Buffer, *TextPtr, *TextNamespace, *PackageNamespace, !!(PortFlags & PPF_Delimited));
}
FString FTextProperty::GenerateCppCodeForTextValue(const FText& InValue, const FString& Indent)
{
FString CppCode;
if (InValue.IsEmpty())
{
CppCode += TEXT("FText::GetEmpty()");
}
else if (InValue.IsCultureInvariant())
{
const FString& StringValue = FTextInspector::GetDisplayString(InValue);
// Produces FText::AsCultureInvariant(TEXT("..."))
CppCode += TEXT("FText::AsCultureInvariant(\n");
CppCode += FStrProperty::ExportCppHardcodedText(StringValue, Indent + TEXT("\t\t"));
CppCode += TEXT("\t)");
}
else
{
FString ExportedText;
FTextStringHelper::WriteToBuffer(ExportedText, InValue);
if (FTextStringHelper::IsComplexText(*ExportedText))
{
// Produces FTextStringHelper::CreateFromBuffer(TEXT("..."))
CppCode += TEXT("FTextStringHelper::CreateFromBuffer(\n");
CppCode += FStrProperty::ExportCppHardcodedText(ExportedText, Indent + TEXT("\t\t"));
CppCode += Indent;
CppCode += TEXT("\t)");
}
else
{
// Produces FText::FromString(TEXT("..."))
CppCode += TEXT("FText::FromString(\n");
CppCode += FStrProperty::ExportCppHardcodedText(ExportedText, Indent + TEXT("\t\t"));
CppCode += TEXT("\t)");
}
}
return CppCode;
}
FString FTextProperty::GetCPPTypeForwardDeclaration() const
{
return FString();
}