// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "UObject/Object.h" #include "UObject/UnrealType.h" #include "BlueprintCompilerCppBackendUtils.h" #include "Blueprint/UserWidget.h" #include "Blueprint/WidgetBlueprintGeneratedClass.h" #include "Animation/WidgetAnimation.h" #include "Blueprint/WidgetTree.h" #include "Evaluation/MovieSceneSegment.h" #include "Evaluation/MovieSceneTrackImplementation.h" #include "Evaluation/MovieSceneEvalTemplate.h" #include "Compilation/MovieSceneCompiledDataManager.h" void FBackendHelperUMG::WidgetFunctionsInHeader(FEmitterLocalContext& Context) { if (Cast(Context.GetCurrentlyGeneratedClass())) { Context.Header.AddLine(FString::Printf(TEXT("virtual void %s(TArray& SlotNames) const override;"), GET_FUNCTION_NAME_STRING_CHECKED(UUserWidget, GetSlotNames))); Context.Header.AddLine(FString::Printf(TEXT("virtual void %s(FObjectPreSaveContext ObjectSaveContext) override;"), GET_FUNCTION_NAME_STRING_CHECKED_OneParam(UUserWidget, PreSave, FObjectPreSaveContext))); Context.Header.AddLine(TEXT("virtual void InitializeNativeClassData() override;")); } } void FBackendHelperUMG::AdditionalHeaderIncludeForWidget(FEmitterLocalContext& Context) { if (!Context.NativizationOptions.bExcludeMonolithicHeaders && Cast(Context.GetCurrentlyGeneratedClass())) { Context.Header.AddLine(TEXT("#include \"Runtime/UMG/Public/UMG.h\"")); } } void FBackendHelperUMG::CreateClassSubobjects(FEmitterLocalContext& Context, bool bCreate, bool bInitialize) { if (UWidgetBlueprintGeneratedClass* WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) { if (UWidgetTree* WidgetTree = WidgetClass->GetWidgetTreeArchetype()) { ensure(WidgetTree->GetOuter() == Context.GetCurrentlyGeneratedClass()); FEmitDefaultValueHelper::HandleClassSubobject(Context, WidgetTree, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); } for (UWidgetAnimation* Anim : WidgetClass->Animations) { ensure(Anim->GetOuter() == Context.GetCurrentlyGeneratedClass()); // We need the same regeneration like for cooking. See UMovieSceneSequence::Serialize UMovieSceneCompiledDataManager::GetPrecompiledData()->Compile(Anim); FString AnimationName = FEmitDefaultValueHelper::HandleClassSubobject(Context, Anim, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); if (bInitialize) { Context.AddLine(FString::Printf(TEXT("UMovieSceneCompiledDataManager::GetPrecompiledData()->Compile(%s);"), *AnimationName)); } } } } void FBackendHelperUMG::EmitWidgetInitializationFunctions(FEmitterLocalContext& Context) { if (UWidgetBlueprintGeneratedClass* WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) { Context.ResetPropertiesForInaccessibleStructs(); const FString CppClassName = FEmitHelper::GetCppName(WidgetClass); auto GenerateLocalProperty = [](FEmitterLocalContext& InContext, FProperty* InProperty, const uint8* DataPtr) -> FString { check(InProperty && DataPtr); const FString NativeName = InContext.GenerateUniqueLocalName(); const uint32 CppTemplateTypeFlags = EPropertyExportCPPFlags::CPPF_CustomTypeName | EPropertyExportCPPFlags::CPPF_BlueprintCppBackend | EPropertyExportCPPFlags::CPPF_NoConst | EPropertyExportCPPFlags::CPPF_NoRef; const FString Target = InContext.ExportCppDeclaration(InProperty, EExportedDeclaration::Local, CppTemplateTypeFlags, FEmitterLocalContext::EPropertyNameInDeclaration::Skip); InContext.AddLine(FString::Printf(TEXT("%s %s;"), *Target, *NativeName)); FEmitDefaultValueHelper::InnerGenerate(InContext, InProperty, NativeName, DataPtr, nullptr); return NativeName; }; { // GetSlotNames Context.AddLine(FString::Printf(TEXT("void %s::%s(TArray& SlotNames) const"), *CppClassName, GET_FUNCTION_NAME_STRING_CHECKED(UUserWidget, GetSlotNames))); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); const FString LocalNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("NamedSlots")), reinterpret_cast(&WidgetClass->NamedSlots)); Context.AddLine(FString::Printf(TEXT("SlotNames.Append(%s);"), *LocalNativeName)); Context.DecreaseIndent(); Context.AddLine(TEXT("}")); } { // InitializeNativeClassData Context.AddLine(FString::Printf(TEXT("void %s::InitializeNativeClassData()"), *CppClassName)); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); // Child widgets may actually use the widget tree from a parent class. // - @see UUserWidget::Initialize() UWidgetBlueprintGeneratedClass* WidgetTreeOwningClass = WidgetClass->FindWidgetTreeOwningClass(); // If we have a valid WidgetTree instance, emit code to initialize the widget using the owning class. if (UWidgetTree* WidgetTree = WidgetTreeOwningClass->GetWidgetTreeArchetype()) { FString WidgetTreeStr; if (WidgetClass == WidgetTreeOwningClass) { // This object was already created as a class-owned subobject and mapped to the 'WidgetTree' value. // - @see CreateClassSubobjects() WidgetTreeStr = Context.FindGloballyMappedObject(WidgetTree, UWidgetTree::StaticClass()); } else { // Emit code to assign the owning class to a local variable. const FString WidgetTreeOwnerClassStr = Context.GenerateUniqueLocalName(); Context.AddLine(FString::Printf(TEXT("UClass* %s = %s;"), *WidgetTreeOwnerClassStr, *Context.FindGloballyMappedObject(WidgetTreeOwningClass, UClass::StaticClass(), true))); // Emit code to locate and assign the owning class's WidgetTree instance to a local variable. This will have been created as part of the owning class's ctor, but note // that we have to look it up by name/outer because the converted class is a UDynamicClass and not a UWidgetBlueprintGeneratedClass, so there is no 'WidgetTree' member. WidgetTreeStr = Context.GenerateUniqueLocalName(); Context.AddLine(FString::Printf(TEXT("UWidgetTree* %s = CastChecked(StaticFindObjectFast(UWidgetTree::StaticClass(), %s, TEXT(\"WidgetTree\")));"), *WidgetTreeStr, *WidgetTreeOwnerClassStr)); } ensure(!WidgetTreeStr.IsEmpty()); const FString AnimationsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Animations")), reinterpret_cast(&WidgetTreeOwningClass->Animations)); const FString BindingsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Bindings")), reinterpret_cast(&WidgetTreeOwningClass->Bindings)); Context.AddLine(FString::Printf(TEXT("UWidgetBlueprintGeneratedClass::%s(this, GetClass(), %s, %s, %s);") , GET_FUNCTION_NAME_STRING_CHECKED(UWidgetBlueprintGeneratedClass, InitializeWidgetStatic) , *WidgetTreeStr , *AnimationsArrayNativeName , *BindingsArrayNativeName)); } Context.DecreaseIndent(); Context.AddLine(TEXT("}")); } // PreSave Context.AddLine(FString::Printf(TEXT("void %s::%s(FObjectPreSaveContext ObjectSaveContext)"), *CppClassName, GET_FUNCTION_NAME_STRING_CHECKED_OneParam(UUserWidget, PreSave, FObjectPreSaveContext))); Context.AddLine(TEXT("{")); Context.IncreaseIndent(); Context.AddLine(FString::Printf(TEXT("Super::%s(ObjectSaveContext);"), GET_FUNCTION_NAME_STRING_CHECKED_OneParam(UObject, PreSave, FObjectPreSaveContext))); Context.AddLine(TEXT("TArray LocalNamedSlots;")); Context.AddLine(FString::Printf(TEXT("%s(LocalNamedSlots);"), GET_FUNCTION_NAME_STRING_CHECKED(UUserWidget, GetSlotNames))); Context.AddLine(TEXT("RemoveObsoleteBindings(LocalNamedSlots);")); //RemoveObsoleteBindings is protected - no check Context.DecreaseIndent(); Context.AddLine(TEXT("}")); } } bool FBackendHelperUMG::SpecialStructureConstructorUMG(const UStruct* Struct, const uint8* ValuePtr, /*out*/ FString* OutResult) { check(ValuePtr || !OutResult); auto FrameNumberRangeBoundConstructorLambda = [](const TRangeBound& InRangeBound, const FFrameNumber& InRangeBoundValue) -> FString { if (InRangeBound.IsExclusive()) { return FString::Printf(TEXT("TRangeBound::Exclusive(%d)"), InRangeBoundValue.Value); } else if (InRangeBound.IsInclusive()) { return FString::Printf(TEXT("TRangeBound::Inclusive(%d)"), InRangeBoundValue.Value); } else { return FString::Printf(TEXT("TRangeBound::Open()")); } }; if (FSectionEvaluationData::StaticStruct() == Struct) { if (OutResult) { const FSectionEvaluationData* SectionEvaluationData = reinterpret_cast(ValuePtr); if (SectionEvaluationData->ForcedTime == TNumericLimits::Lowest()) { *OutResult = FString::Printf(TEXT("FSectionEvaluationData(%d, ESectionEvaluationFlags(0x%02x))") , SectionEvaluationData->ImplIndex , (uint8)SectionEvaluationData->Flags); } else { *OutResult = FString::Printf(TEXT("FSectionEvaluationData(%d, %d)") , SectionEvaluationData->ImplIndex , SectionEvaluationData->ForcedTime.Value); } } return true; } if (FMovieSceneSegment::StaticStruct() == Struct) { if (OutResult) { const FMovieSceneSegment* MovieSceneSegment = reinterpret_cast(ValuePtr); FString SegmentsInitializerList; for (const FSectionEvaluationData& SectionEvaluationData : MovieSceneSegment->Impls) { if (!SegmentsInitializerList.IsEmpty()) { SegmentsInitializerList += TEXT(", "); } FString SectionEvaluationDataStr; FBackendHelperUMG::SpecialStructureConstructorUMG(FSectionEvaluationData::StaticStruct() , reinterpret_cast(&SectionEvaluationData) , &SectionEvaluationDataStr); SegmentsInitializerList += SectionEvaluationDataStr; } const FString LowerBoundStr = FrameNumberRangeBoundConstructorLambda(MovieSceneSegment->Range.GetLowerBound(), MovieSceneSegment->Range.GetLowerBound().IsClosed() ? MovieSceneSegment->Range.GetLowerBoundValue() : FFrameNumber()); const FString UpperBoundStr = FrameNumberRangeBoundConstructorLambda(MovieSceneSegment->Range.GetUpperBound(), MovieSceneSegment->Range.GetUpperBound().IsClosed() ? MovieSceneSegment->Range.GetUpperBoundValue() : FFrameNumber()); *OutResult = FString::Printf(TEXT("FMovieSceneSegment(TRange(%s, %s), {%s})"), *LowerBoundStr, *UpperBoundStr, *SegmentsInitializerList); } return true; } if (FMovieSceneFrameRange::StaticStruct() == Struct) { if (OutResult) { const FMovieSceneFrameRange* MovieSceneFrameRange = reinterpret_cast(ValuePtr); const FString LowerBoundStr = FrameNumberRangeBoundConstructorLambda(MovieSceneFrameRange->Value.GetLowerBound(), MovieSceneFrameRange->Value.GetLowerBound().IsClosed() ? MovieSceneFrameRange->Value.GetLowerBoundValue() : FFrameNumber()); const FString UpperBoundStr = FrameNumberRangeBoundConstructorLambda(MovieSceneFrameRange->Value.GetUpperBound(), MovieSceneFrameRange->Value.GetUpperBound().IsClosed() ? MovieSceneFrameRange->Value.GetUpperBoundValue() : FFrameNumber()); *OutResult = FString::Printf(TEXT("FMovieSceneFrameRange(TRange(%s, %s))"), *LowerBoundStr, *UpperBoundStr); } return true; } return false; } bool FBackendHelperUMG::IsTInlineStruct(UScriptStruct* OuterStruct) { return (OuterStruct == FMovieSceneTrackImplementationPtr::StaticStruct()) || (OuterStruct == FMovieSceneEvalTemplatePtr::StaticStruct()); } UScriptStruct* FBackendHelperUMG::InlineValueStruct(UScriptStruct* OuterStruct, const uint8* ValuePtr) { if (OuterStruct == FMovieSceneTrackImplementationPtr::StaticStruct()) { const FMovieSceneTrackImplementation* MovieSceneTrackImplementation = reinterpret_cast(ValuePtr)->GetPtr(); if (MovieSceneTrackImplementation) { return &(MovieSceneTrackImplementation->GetScriptStruct()); } } if (OuterStruct == FMovieSceneEvalTemplatePtr::StaticStruct()) { const FMovieSceneEvalTemplate* MovieSceneEvalTemplate = reinterpret_cast(ValuePtr)->GetPtr(); if (MovieSceneEvalTemplate) { return &(MovieSceneEvalTemplate->GetScriptStruct()); } } return nullptr; } const uint8* FBackendHelperUMG::InlineValueData(UScriptStruct* OuterStruct, const uint8* ValuePtr) { if (ValuePtr) { if (OuterStruct == FMovieSceneTrackImplementationPtr::StaticStruct()) { return reinterpret_cast(reinterpret_cast(ValuePtr)->GetPtr()); } if (OuterStruct == FMovieSceneEvalTemplatePtr::StaticStruct()) { return reinterpret_cast(reinterpret_cast(ValuePtr)->GetPtr()); } } return nullptr; }