#include "FormatDataLibrary.h" #include "Editor.h" #include "EdGraphSchema_K2.h" #include "Internationalization/TextFormatter.h" #include "Layout/Geometry.h" #include "Widgets/Layout/Anchors.h" #include "Common.h" #include "Kismet/KismetTextLibrary.h" #include "UObject/UObjectIterator.h" void UlxFormatDataLibrary::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); ScanForConverters(); } void UlxFormatDataLibrary::ScanForConverters() { Converters.Empty(); for (TObjectIterator It; It; ++It) { UClass* Class = *It; for (TFieldIterator FuncIt(Class, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt) { UFunction* Function = *FuncIt; // Must have an AutoConvertedValue parameter. if (Function->FindPropertyByName(TEXT("AutoConvertedValue")) == nullptr) continue; // Must have a Name parameter that is a string. FProperty* NameProp = Function->FindPropertyByName(TEXT("Name")); if (NameProp == nullptr) continue; if (CastField(NameProp) == nullptr) continue; // Must return FFormatArgumentData. FStructProperty* ReturnProp = CastField(Function->GetReturnProperty()); if (ReturnProp == nullptr) continue; if (ReturnProp->Struct->GetFName() != TEXT("FormatArgumentData")) continue; // Must have exactly three properties: AutoConvertedValue, Name, and ReturnValue. int PropCount = 0; for (TFieldIterator PropIt(Function); PropIt; ++PropIt) PropCount++; if (PropCount != 3) continue; Converters.Add(Function); } } for (UFunction* Func : Converters) { UE_LOG(LogLuprexIntegration, Display, TEXT("FormatData converter: %s::%s"), *Func->GetOuterUClass()->GetName(), *Func->GetName()); } } UFunction* UlxFormatDataLibrary::GetConverterForPinType(const UEdGraphSchema_K2 *Schema, const FEdGraphPinType& PinType, bool AllowWild) { // Special case: wildcard pins are unconnected pins. if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard) && AllowWild) { return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank)); } // Scan the cached converter list for a matching type. UlxFormatDataLibrary* FormatDataLib = GEditor->GetEditorSubsystem(); for (UFunction* Function : FormatDataLib->Converters) { FProperty* ValueProperty = Function->FindPropertyByName(TEXT("AutoConvertedValue")); FEdGraphPinType ValuePinType; if (!Schema->ConvertPropertyToPinType(ValueProperty, ValuePinType)) continue; if (!Schema->ArePinTypesEquivalent(PinType, ValuePinType)) continue; return Function; } // A general handler for Enums. if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast(PinType.PinSubCategoryObject))) { return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataEnum)); } // A case for subclasses of 'Object' which are not exactly 'Object'. if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object) { return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataObject)); } return nullptr; } ELogVerbosity::Type UlxFormatDataLibrary::ConvertElxFormatLogVerbosity(ElxFormatLogVerbosity Verbosity) { switch (Verbosity) { case ElxFormatLogVerbosity::Error: return ELogVerbosity::Error; case ElxFormatLogVerbosity::Warning: return ELogVerbosity::Warning; case ElxFormatLogVerbosity::Display: return ELogVerbosity::Display; case ElxFormatLogVerbosity::Log: return ELogVerbosity::Log; case ElxFormatLogVerbosity::ThrottledDisplay: return ELogVerbosity::Display; case ElxFormatLogVerbosity::ThrottledLog: return ELogVerbosity::Log; case ElxFormatLogVerbosity::Verbose: return ELogVerbosity::Verbose; case ElxFormatLogVerbosity::VeryVerbose: return ELogVerbosity::VeryVerbose; case ElxFormatLogVerbosity::Fatal: return ELogVerbosity::Fatal; } } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_BoolToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataByte(uint8 AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt(int AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataInt64(int64 AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataFloat(float AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Float; Result.ArgumentName = Name; Result.ArgumentValueFloat = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataDouble(double AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Double; Result.ArgumentName = Name; Result.ArgumentValueDouble = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataText(FText AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataString(FString AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_StringToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataName(FName AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataKey(FKey AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_NameToText(AutoConvertedValue.GetFName()); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGender(ETextGender AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Gender; Result.ArgumentName = Name; Result.ArgumentValueGender = AutoConvertedValue; return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataObject(UObject *AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_ObjectToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector(const FVector &AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_VectorToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataVector2D(const FVector2D &AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_Vector2dToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataRotator(const FRotator &AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_RotatorToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = UKismetTextLibrary::Conv_TransformToText(AutoConvertedValue); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataGeometry(const FGeometry &AutoConvertedValue, const FString &Name) { FVector2D LocalSize = AutoConvertedValue.GetLocalSize(); FVector2D AbsPos = AutoConvertedValue.GetAbsolutePosition(); FVector2D AbsSize = AutoConvertedValue.GetAbsoluteSize(); FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText::Format( INVTEXT("Geom(Local={0}x{1} Abs={2}x{3} Pos={4},{5})"), FText::AsNumber(LocalSize.X), FText::AsNumber(LocalSize.Y), FText::AsNumber(AbsSize.X), FText::AsNumber(AbsSize.Y), FText::AsNumber(AbsPos.X), FText::AsNumber(AbsPos.Y)); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnchors(const FAnchors &AutoConvertedValue, const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText::Format( INVTEXT("Anchors(Min={0},{1} Max={2},{3})"), FText::AsNumber(AutoConvertedValue.Minimum.X), FText::AsNumber(AutoConvertedValue.Minimum.Y), FText::AsNumber(AutoConvertedValue.Maximum.X), FText::AsNumber(AutoConvertedValue.Maximum.Y)); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString &Name) { FFormatArgumentData Result; Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText(); return Result; } FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataEnum(uint8 Value, const FString &Name, const UObject *PinSubCategoryObject) { const UEnum *Enum = Cast(PinSubCategoryObject); FFormatArgumentData Result; if (Enum == nullptr) { Result.ArgumentValueType = EFormatArgumentType::Int; Result.ArgumentName = Name; Result.ArgumentValueInt = Value; } else { Result.ArgumentValueType = EFormatArgumentType::Text; Result.ArgumentName = Name; Result.ArgumentValue = FText::Format(INVTEXT("<{0}>"), Enum->GetDisplayNameTextByValue(Value)); } return Result; } void UlxFormatDataLibrary::FormatLogMessageInternal(UObject *Context, ElxFormatLogVerbosity Verbosity, const FString &InPattern, TArray InArgs) { // For throttled verbosity levels, suppress repeated messages with the // same format pattern. We key on the blueprint name + format pattern, // and allow at most one message per second per key. // if ((Verbosity == ElxFormatLogVerbosity::ThrottledDisplay) || (Verbosity == ElxFormatLogVerbosity::ThrottledLog)) { static TMap LastLogTime; double Now = FPlatformTime::Seconds(); FString Key = Context->GetClass()->GetName() + TEXT("::") + InPattern; double &Last = LastLogTime.FindOrAdd(Key, 0.0); if (Now - Last < 1.0) { return; } Last = Now; } // Generate the formatted string. // FText InPatternText(FText::FromString(InPattern)); FText Message = FTextFormatter::Format(MoveTemp(InPatternText), MoveTemp(InArgs), false, false); FString MessageString = Message.ToString(); // Get the blueprint name. // // Normally, the log function expects you to pass in a filename, and a log // category name. We use the blueprint name for both. // // Using the blueprint name as a log category name is not technically // correct. However, there is no correct way to create log categories // from inside of blueprints. Doing it this way at least produces a reasonable // message inside the log. What doesn't work correctly is the log message // suppression system. Ie, console commands like 'log verbose' // don't have any effect here. The design of the log message suppression // system is such that there just is no reasonable way to hook into it from // inside of blueprints. // FString BlueprintNameString = Context->GetClass()->GetName(); auto BlueprintNameAnsi = StringCast(*BlueprintNameString); FLogCategoryName BlueprintNameLogCategory(Context->GetClass()->GetFName()); // Output to Log // ELogVerbosity::Type VerbosityValue = ConvertElxFormatLogVerbosity(Verbosity); if (VerbosityValue <= ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) { FMsg::Logf(BlueprintNameAnsi.Get(), 0, BlueprintNameLogCategory, VerbosityValue, TEXT("%s"), *MessageString); } }