// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Bindings/MVVMCompiledBindingLibraryCompiler.h" #include "MVVMBlueprintView.h" #include "MVVMBlueprintViewCompilerInterface.h" #include "MVVMBlueprintViewEvent.h" #include "MVVMBlueprintViewCondition.h" #include "MVVMBlueprintViewModelContext.h" #include "Templates/ValueOrError.h" #include "Types/MVVMFieldVariant.h" #include "UObject/StrongObjectPtr.h" #include "WidgetBlueprintCompiler.h" struct FMVVMBlueprintPinId; struct FMVVMBlueprintViewBinding; class FWidgetBlueprintCompilerContext; class UEdGraph; class UMVVMBlueprintView; class UMVVMBlueprintViewConversionFunction; class UMVVMBlueprintViewExtension; class UMVVMBlueprintViewEvent; class UMVVMViewClass; class UMVVMViewClassExtension; class UWidgetBlueprintGeneratedClass; namespace UE::MVVM { enum class EBindingMessageType : uint8; } namespace UE::MVVM::Private { struct FMVVMViewBlueprintCompiler { private: struct FCompilerBindingSource; struct FCompilerUserWidgetProperty; struct FCompilerViewModelSetter; struct FCompilerViewModelCreatorContext; struct FCompilerSourceViewModelDynamicCreatorContext; struct FCompilerNotifyFieldId; struct FGeneratedReadFieldPathContext; struct FGeneratedWriteFieldPathContext; struct FCompilerBinding; struct FCompilerEvent; struct FCompilerCondition; struct FCompilerExtension; public: FMVVMViewBlueprintCompiler(FWidgetBlueprintCompilerContext& InCreationContext, UMVVMBlueprintView* BlueprintView); FWidgetBlueprintCompilerContext& GetCompilerContext() { return WidgetBlueprintCompilerContext; } void AddExtension(UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CleanOldData(UWidgetBlueprintGeneratedClass* ClassToClean, UObject* OldCDO); /** Update the list of compiler generated variables to be created by the kismet compiler */ void GatherGeneratedVariables(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); /** Generate variable and public function in the Skeleton class and the generated class */ void CreateVariables(const FWidgetBlueprintCompilerContext::FCreateVariableContext& Context); /** Generate function that are hidden from the user (not on the Skeleton class). */ void CreateFunctions(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); /** Add all the field path and the bindings to the library compiler. */ bool PreCompile(UWidgetBlueprintGeneratedClass* Class); /** Compile the library and fill the view and viewclass */ bool Compile(UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); /** Get the list of generated function during the compilation process. */ TArray GetGeneratedFunctions() const; static void TestGenerateSetter(const UBlueprint* Context, FStringView ObjectName, FStringView FieldPath, FStringView FunctionName); private: bool AreStepsValid() const { return bIsGatherGeneratedVariablesStepValid && bIsCreateVariableStepValid && bIsCreateFunctionsStepValid && bIsPreCompileStepValid && bIsCompileStepValid; } // GatherGeneratedVariables void CreateWidgetMap(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); void CreateBindingList(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); void CreateEventList(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); void CreateConditionList(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); void CreateExtensionList(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); void CreateRequiredProperties(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context); // CreateVariables void CreatePublicFunctionsDeclaration(const FWidgetBlueprintCompilerContext::FCreateVariableContext& Context); // CreateFuntions void CategorizeBindings(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CategorizeEvents(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CategorizeConditions(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CreateWriteFieldContexts(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CreateViewModelSetters(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CreateIntermediateGraphFunctions(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); void CategorizeAsyncFunctions(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context); // PreCompile void FixCompilerBindingSelfSource(UWidgetBlueprintGeneratedClass* Class); void AddWarningForPropertyWithMVVMAndLegacyBinding(UWidgetBlueprintGeneratedClass* Class); void FixFieldPathContext(UWidgetBlueprintGeneratedClass* Class); void CreateReadFieldContexts(UWidgetBlueprintGeneratedClass* Class); void CreateCreatorContentFromBindingSource(UWidgetBlueprintGeneratedClass* Class); void PreCompileViewModelCreatorContexts(UWidgetBlueprintGeneratedClass* Class); void PreCompileBindings(UWidgetBlueprintGeneratedClass* Class); void PreCompileEvents(UWidgetBlueprintGeneratedClass* Class); void PreCompileConditions(UWidgetBlueprintGeneratedClass* Class); void PreCompileViewExtensions(UWidgetBlueprintGeneratedClass* Class); void PreCompileSourceDependencies(UWidgetBlueprintGeneratedClass* Class); // Compile void CompileSources(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CompileBindings(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CompileEvaluateSources(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CompileEvents(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CompileConditions(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void CompileViewExtensions(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); void SortSourceFields(const FCompiledBindingLibraryCompiler::FCompileResult& CompileResult, UWidgetBlueprintGeneratedClass* Class, UMVVMViewClass* ViewExtension); private: /** * List of all the sources needed by the view to register/execute the bindings. * They could be viewmodel, widget or any properties on the UserWidget. * They could also be a viewmodel in a long path. * It may not have an associated property (dynamicviewmodel). * It may only have OneTime binding. */ struct FCompilerBindingSource { enum class EType { ViewModel = 0, DynamicViewmodel = 1, Widget = 2, Self = 3, }; const UClass* AuthoritativeClass = nullptr; TArray> Dependencies; FName Name; EType Type; bool bIsOptional = false; }; TArray> NeededBindingSources; /** * Describe a Property that need to be added (if it doesn't already exist). * They can be a Widget, a viewmodel, or any object owned by the UserWidget. * They can be source or destination. */ struct FCompilerUserWidgetProperty : Compiler::FBlueprintViewUserWidgetProperty { enum class ECreationType { None, CreateIfDoesntExist, CreateOnlyIfDoesntExist, }; FString BlueprintSetter; ECreationType CreationType = ECreationType::None; bool bInstanced = false; bool bInstanceExposed = false; }; TArray NeededUserWidgetProperties; /** * Describe a ViewModel generated setter function. */ struct FCompilerViewModelSetter { const UClass* Class = nullptr; FName PropertyName; FString BlueprintSetter; FText DisplayName; UEdGraph* SetterGraph = nullptr; }; TArray ViewModelSettersToGenerate; /** * Describe the data to initialize the view's properties/viewmodels. */ struct FCompilerViewModelCreatorContext { FMVVMBlueprintViewModelContext ViewModelContext; TSharedPtr Source; TSharedPtr DynamicContext; FCompiledBindingLibraryCompiler::FFieldPathHandle ReadPropertyPathHandle; }; TArray ViewModelCreatorContexts; /** * Describe the data to initialize the view's properties/widget. */ struct FCompilerWidgetCreatorContext { TSharedPtr Source; bool bSelfReference = false; FCompiledBindingLibraryCompiler::FFieldPathHandle ReadPropertyPathHandle; }; TArray WidgetCreatorContexts; /** * Describe the data to initialize a viewmodel in a long path. * The viewmodel is not added in the BlueprintView but it needs to be dynamic added. */ struct FCompilerSourceViewModelDynamicCreatorContext { TSharedPtr Source; TSharedPtr ParentSource; // a dynamic always has a parent FFieldNotificationId NotificationId; FCompiledBindingLibraryCompiler::FFieldIdHandle NotificationIdLibraryCompilerHandle; }; TArray> SourceViewModelDynamicCreatorContexts; /** * The field id for a specific ReadFieldPathContext */ struct FCompilerNotifyFieldId { TArray BindingEditorKeys; TArray EventKeys; FFieldNotificationId NotificationId; TSharedPtr Source; TSharedPtr ViewModelDynamic; FCompiledBindingLibraryCompiler::FFieldIdHandle LibraryCompilerHandle; }; TArray> NotificationFields; /** * The source path we need to read from. * Can be any EMVVMBindingMode (OneTime, OneWay, ...) */ struct FGeneratedReadFieldPathContext { TArray> UsedByBindings; TArray> UsedByEvents; TArray> UsedByConditions; TSharedPtr Source; TArray GeneratedFields; // the string path converted into field TArray SkeletalGeneratedFields; // the field path converted with getter and setter EMVVMBlueprintFieldPathSource GeneratedFrom = EMVVMBlueprintFieldPathSource::None; bool bIsComponent = false; TSharedPtr NotificationField; FCompiledBindingLibraryCompiler::FFieldPathHandle LibraryCompilerHandle; }; TArray> GeneratedReadFieldPaths; /** * Destination path we need to write to. * Only if the bindings/events have a destination. * The info needs to be validate before we generate the functions list. */ struct FGeneratedWriteFieldPathContext { TArray> UsedByBindings; TArray> UsedByEvents; /** * Can be invalid if it's a widget with no Read Path. * It is the start of the path. */ TSharedPtr OptionalSource; TSharedPtr OptionalDependencySource; /** The string path converted into field. It always start from the UserWidget. */ TArray GeneratedFields; /** The field path converted with getter and setter. It always start from the UserWidget. */ TArray SkeletalGeneratedFields; EMVVMBlueprintFieldPathSource GeneratedFrom = EMVVMBlueprintFieldPathSource::None; bool bCanBeSetInNative = true; bool bUseByNativeBinding = false; FName GeneratedFunctionName; FCompiledBindingLibraryCompiler::FFieldPathHandle LibraryCompilerHandle; }; TArray> GeneratedWriteFieldPaths; /** * The list of all the valid bindings to iterate on. */ struct FCompilerBinding { struct FKey { int32 ViewBindingIndex = INDEX_NONE; bool bIsForwardBinding = false; FKey() = default; FKey(int32 InIndex, bool bInForward) : ViewBindingIndex(InIndex) , bIsForwardBinding(bInForward) {} bool operator==(const FKey& Other) const { return ViewBindingIndex == Other.ViewBindingIndex && bIsForwardBinding == Other.bIsForwardBinding; } }; enum class EType : int32 { Unknown = 0, // was not evaluated yet Invalid = -1, // evaluated and not valid Assignment = 1, //Destination=Source SimpleConversionFunction = 2, //Destination=Function(Source) ComplexConversionFunction = 3, //Destination=Function(SourceA, SourceB) //Function = 4, //Function(SourceA, SourceB) }; FKey Key; EType Type = EType::Unknown; bool bIsOneTimeBinding = false; TArray> ReadPaths; TSharedPtr WritePath; TWeakObjectPtr ConversionFunction; FCompiledBindingLibraryCompiler::FBindingHandle BindingHandle; FCompiledBindingLibraryCompiler::FFieldPathHandle ConversionFunctionHandle; UE::MVVM::Compiler::FCompilerBindingHandle CompilerBindingHandle; }; TArray> ValidBindings; /** * The list of all the valid events to iterate on. */ struct FCompilerEvent { TWeakObjectPtr Event; TArray> ReadPaths; TSharedPtr WritePath; TSharedPtr DelegateFieldPath; FName GeneratedGraphName; FName SourceName; // may not be in the NeededBindingSources FCompiledBindingLibraryCompiler::FFieldPathHandle DelegateFieldPathHandle; }; TArray> ValidEvents; /** * The list of all the valid conditions to iterate on. */ struct FCompilerCondition { TWeakObjectPtr Condition; TArray> ReadPaths; FName GeneratedGraphName; FName SourceName; // may not be in the NeededBindingSources FCompiledBindingLibraryCompiler::FFieldPathHandle DelegateFieldPathHandle; }; TArray> ValidConditions; /** * The list of all the valid extension to iterates on. */ struct FCompilerExtension { TWeakObjectPtr Extension; }; TArray> ValidExtensions; /** * List of public expose function */ TArray FunctionPermissionsToAdd; /** * List of generated function */ TArray GeneratedFunctions; private: TMap WidgetNameToWidgetPointerMap; FWidgetBlueprintCompilerContext& WidgetBlueprintCompilerContext; TObjectPtr BlueprintView = nullptr; FCompiledBindingLibraryCompiler BindingLibraryCompiler; bool bIsGatherGeneratedVariablesStepValid = true; bool bIsCreateVariableStepValid = true; bool bIsCreateFunctionsStepValid = true; bool bIsPreCompileStepValid = true; bool bIsCompileStepValid = true; private: void AddMessage(const FText& MessageText, Compiler::EMessageType MessageType) const; void AddMessages(TArrayView> Bindings, TArrayView> Events, const FText& MessageText, Compiler::EMessageType MessageType) const; void AddMessageForBinding(const TSharedPtr& Binding, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForBinding(const FMVVMBlueprintViewBinding& Binding, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForEvent(const TSharedPtr& Event, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForEvent(const UMVVMBlueprintViewEvent* Event, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForCondition(const TSharedPtr& Condition, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForCondition(const UMVVMBlueprintViewCondition* Condition, const FText& MessageText, Compiler::EMessageType MessageType, const FMVVMBlueprintPinId& PinId) const; void AddMessageForViewModel(const FMVVMBlueprintViewModelContext& ViewModel, const FText& Message, Compiler::EMessageType MessageType) const; void AddMessageForViewModel(const FText& ViewModelDisplayName, const FText& Message, Compiler::EMessageType MessageType) const; struct FGetFieldsResult { TSharedPtr OptionalSource; EMVVMBlueprintFieldPathSource GeneratedFrom = EMVVMBlueprintFieldPathSource::None; TArray GeneratedFields; }; struct FCreateFieldsResult { TSharedPtr OptionalSource; EMVVMBlueprintFieldPathSource GeneratedFrom = EMVVMBlueprintFieldPathSource::None; TArray GeneratedFields; TArray SkeletalGeneratedFields; bool bIsComponent; }; TValueOrError GetFields(const UWidgetBlueprintGeneratedClass* Class, const FMVVMBlueprintPropertyPath& PropertyPath) const; TValueOrError CreateFieldContext(const UWidgetBlueprintGeneratedClass* Class, const FMVVMBlueprintPropertyPath& PropertyPath, bool bForSourceReading) const; TValueOrError, FText> CreateNotifyFieldId(const UWidgetBlueprintGeneratedClass* Class, const TSharedPtr& ReadFieldContext); UMVVMViewClassExtension* CreateViewClassExtension(TSubclassOf ExtensionClass, UMVVMViewClass* ViewClass); static TArray AppendBaseField(const UClass* Class, FName PropertyName, TArray Properties); static bool IsPropertyPathValid(const UBlueprint* Context, TArrayView PropertyPath); static bool CanBeSetInNative(TArrayView PropertyPath); static TSharedRef MakeWriteFieldPath(EMVVMBlueprintFieldPathSource GeneratedFrom, TArray&& GeneratedFields, TArray&& SkeletalGeneratedFields); }; } //namespace