2022-06-08 13:25:42 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MVVMViewBlueprintCompiler.h"
2024-02-08 13:38:56 -05:00
2022-06-08 13:25:42 -04:00
# include "Bindings/MVVMBindingHelper.h"
2022-10-06 20:08:12 -04:00
# include "Bindings/MVVMConversionFunctionHelper.h"
2022-06-08 13:25:42 -04:00
# include "Bindings/MVVMFieldPathHelper.h"
2024-02-08 13:38:56 -05:00
# include "BlueprintEditorSettings.h"
# include "Blueprint/WidgetTree.h"
2023-01-12 01:48:34 -05:00
# include "Components/Widget.h"
2024-03-26 02:39:40 -04:00
# include "Extensions/MVVMBlueprintViewExtension.h"
2023-02-17 15:28:46 -05:00
# include "HAL/IConsoleManager.h"
2024-02-08 13:38:56 -05:00
# include "Misc/NamePermissionList.h"
2022-06-08 13:25:42 -04:00
# include "MVVMBlueprintView.h"
2023-07-26 14:04:59 -04:00
# include "MVVMBlueprintViewConversionFunction.h"
2023-01-31 10:21:03 -05:00
# include "MVVMDeveloperProjectSettings.h"
2023-03-16 13:02:33 -04:00
# include "MVVMMessageLog.h"
2023-02-27 16:27:44 -05:00
# include "PropertyPermissionList.h"
2022-06-08 13:25:42 -04:00
# include "MVVMFunctionGraphHelper.h"
2023-06-16 11:26:16 -04:00
# include "MVVMWidgetBlueprintExtension_View.h"
2023-01-12 01:48:34 -05:00
# include "Templates/ValueOrError.h"
# include "Types/MVVMBindingName.h"
2023-02-17 15:28:46 -05:00
# include "UObject/LinkerLoad.h"
2023-10-05 18:27:37 -04:00
# include "UObject/UnrealType.h"
2023-02-09 12:14:47 -05:00
# include "View/MVVMViewClass.h"
2023-02-11 12:38:02 -05:00
# include "View/MVVMViewModelContextResolver.h"
2023-10-11 13:52:26 -04:00
# include "WidgetBlueprintEditorUtils.h"
2022-06-08 13:25:42 -04:00
2022-10-06 20:08:12 -04:00
# define LOCTEXT_NAMESPACE "MVVMViewBlueprintCompiler"
2024-03-26 02:40:51 -04:00
namespace UE : : MVVM : : Compiler
{
int32 FCompilerBindingHandle : : IdGenerator = 0 ;
}
2023-11-17 14:09:15 -05:00
/**
When compiling the skeletal class
CreateVariables ( )
Add the less amount of errors here to have a more responsive editor and have the variable available in the editor .
Create all the variables
Create the public functions
When compiling the full class
CreateVariables ( )
same as skeletal , it may compile the skeletal then the full class . This will be called twice with the same instance .
CreateFunctions ( )
Create the private functions for bindings and events .
Note The class is not linked
Once the full class is compiled , then add the bindings / events with
PreCompile ( )
Create the bindings and events data and send them to the library compiler .
Compile ( )
All bindings and events data are compiled . Create the view data with the compiled data .
*/
2022-06-08 13:25:42 -04:00
namespace UE : : MVVM : : Private
{
2023-11-17 14:09:15 -05:00
FAutoConsoleVariable CVarLogViewCompiledResult (
TEXT ( " MVVM.LogViewCompiledResult " ) ,
2023-03-22 13:07:37 -04:00
false ,
TEXT ( " After the view is compiled log the compiled bindings and sources. " )
) ;
2023-03-16 13:02:33 -04:00
2023-06-16 11:26:16 -04:00
# if UE_BUILD_DEVELOPMENT || UE_BUILD_DEBUG
FAutoConsoleCommand CVarTestGenerateSetter (
TEXT ( " MVVM.TestGenerateSetter " ) ,
TEXT ( " Generate a setter function base on the input string. " ) ,
FConsoleCommandWithArgsDelegate : : CreateLambda ( [ ] ( const TArray < FString > & Args )
{
if ( Args . Num ( ) < 3 )
{
return ;
}
2023-10-20 09:15:33 -04:00
FMVVMViewBlueprintCompiler : : TestGenerateSetter ( nullptr , Args [ 0 ] , Args [ 1 ] , Args [ 2 ] ) ;
2023-06-16 11:26:16 -04:00
} )
) ;
# endif
static const FText CouldNotCreateSourceFieldPathFormat = LOCTEXT ( " CouldNotCreateSourceFieldPath " , " Couldn't create the source field path '{0}'. {1} " ) ;
2023-11-17 14:09:15 -05:00
static const FText CouldNotCreateDestinationFieldPathFormat = LOCTEXT ( " CouldNotCreateDestinationFieldPath " , " Couldn't create the destination field path '{0}'. {1} " ) ;
2023-08-21 08:46:45 -04:00
static const FText PropertyPathIsInvalidFormat = LOCTEXT ( " PropertyPathIsInvalid " , " The property path '{0}' is invalid. " ) ;
2023-06-16 11:26:16 -04:00
2023-11-17 14:09:15 -05:00
void RenameObjectToTransientPackage ( UObject * ObjectToRename )
{
const ERenameFlags RenFlags = REN_DoNotDirty | REN_ForceNoResetLoaders | REN_DontCreateRedirectors ;
ObjectToRename - > Rename ( nullptr , GetTransientPackage ( ) , RenFlags ) ;
ObjectToRename - > SetFlags ( RF_Transient ) ;
ObjectToRename - > ClearFlags ( RF_Public | RF_Standalone | RF_ArchetypeObject ) ;
FLinkerLoad : : InvalidateExport ( ObjectToRename ) ;
}
2023-06-16 11:26:16 -04:00
FString PropertyPathToString ( const UClass * InSelfContext , const UMVVMBlueprintView * BlueprintView , const FMVVMBlueprintPropertyPath & PropertyPath )
2022-06-08 13:25:42 -04:00
{
2023-10-20 10:45:57 -04:00
if ( ! PropertyPath . IsValid ( ) )
2022-10-06 20:08:12 -04:00
{
return FString ( ) ;
}
TStringBuilder < 512 > Result ;
2023-10-20 10:45:57 -04:00
switch ( PropertyPath . GetSource ( BlueprintView - > GetOuterUMVVMWidgetBlueprintExtension_View ( ) - > GetOuterUWidgetBlueprint ( ) ) )
2022-10-06 20:08:12 -04:00
{
2023-10-20 10:45:57 -04:00
case EMVVMBlueprintFieldPathSource : : SelfContext :
Result < < InSelfContext - > ClassGeneratedBy - > GetName ( ) ;
break ;
case EMVVMBlueprintFieldPathSource : : Widget :
Result < < PropertyPath . GetWidgetName ( ) ;
break ;
case EMVVMBlueprintFieldPathSource : : ViewModel :
2022-10-06 20:08:12 -04:00
if ( const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) )
{
Result < < SourceViewModelContext - > GetViewModelName ( ) ;
}
else
{
Result < < TEXT ( " <none> " ) ;
}
2023-10-20 10:45:57 -04:00
break ;
default :
2022-10-06 20:08:12 -04:00
Result < < TEXT ( " <none> " ) ;
2023-10-20 10:45:57 -04:00
break ;
2022-10-06 20:08:12 -04:00
}
2023-06-16 11:26:16 -04:00
FString BasePropertyPath = PropertyPath . GetPropertyPath ( InSelfContext ) ;
2022-10-06 20:08:12 -04:00
if ( BasePropertyPath . Len ( ) )
{
Result < < TEXT ( ' . ' ) ;
Result < < MoveTemp ( BasePropertyPath ) ;
}
return Result . ToString ( ) ;
2022-06-08 13:25:42 -04:00
}
2023-06-16 11:26:16 -04:00
FText PropertyPathToText ( const UClass * InSelfContext , const UMVVMBlueprintView * BlueprintView , const FMVVMBlueprintPropertyPath & PropertyPath )
2022-10-06 20:08:12 -04:00
{
2023-06-16 11:26:16 -04:00
return FText : : FromString ( PropertyPathToString ( InSelfContext , BlueprintView , PropertyPath ) ) ;
2022-10-06 20:08:12 -04:00
}
FText GetViewModelIdText ( const FMVVMBlueprintPropertyPath & PropertyPath )
{
return FText : : FromString ( PropertyPath . GetViewModelId ( ) . ToString ( EGuidFormats : : DigitsWithHyphensInBraces ) ) ;
}
2023-11-17 14:09:15 -05:00
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > AddObjectFieldPath ( FCompiledBindingLibraryCompiler & BindingLibraryCompiler , const UWidgetBlueprintGeneratedClass * Class , FStringView ObjectPath , const UClass * ExpectedType )
2023-03-22 13:07:37 -04:00
{
// Generate a path to read the value at runtime
static const FText InvalidGetterFormat = LOCTEXT ( " ViewModelInvalidGetterWithReason " , " Viewmodel has an invalid Getter. {0} " ) ;
TValueOrError < TArray < FMVVMConstFieldVariant > , FText > GeneratedField = FieldPathHelper : : GenerateFieldPathList ( Class , ObjectPath , true ) ;
if ( GeneratedField . HasError ( ) )
{
return MakeError ( FText : : Format ( InvalidGetterFormat , GeneratedField . GetError ( ) ) ) ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = BindingLibraryCompiler . AddObjectFieldPath ( GeneratedField . GetValue ( ) , ExpectedType , true ) ;
if ( ReadFieldPathResult . HasError ( ) )
{
return MakeError ( FText : : Format ( InvalidGetterFormat , ReadFieldPathResult . GetError ( ) ) ) ;
}
return MakeValue ( ReadFieldPathResult . StealValue ( ) ) ;
}
2023-11-16 20:11:58 -05:00
2023-11-17 14:09:15 -05:00
FMVVMViewBlueprintCompiler : : FMVVMViewBlueprintCompiler ( FWidgetBlueprintCompilerContext & InCreationContext , UMVVMBlueprintView * InBlueprintView )
: WidgetBlueprintCompilerContext ( InCreationContext )
, BlueprintView ( InBlueprintView )
, BindingLibraryCompiler ( InCreationContext . WidgetBlueprint ( ) )
{
check ( BlueprintView . IsValid ( ) ) ;
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessage ( const FText & MessageText , Compiler : : EMessageType MessageType ) const
2023-11-17 14:09:15 -05:00
{
switch ( MessageType )
{
2024-03-26 02:39:40 -04:00
case Compiler : : EMessageType : : Info :
2023-11-17 14:09:15 -05:00
WidgetBlueprintCompilerContext . MessageLog . Note ( * MessageText . ToString ( ) ) ;
break ;
2024-03-26 02:39:40 -04:00
case Compiler : : EMessageType : : Warning :
2023-11-17 14:09:15 -05:00
WidgetBlueprintCompilerContext . MessageLog . Warning ( * MessageText . ToString ( ) ) ;
break ;
2024-03-26 02:39:40 -04:00
case Compiler : : EMessageType : : Error :
2023-11-17 14:09:15 -05:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * MessageText . ToString ( ) ) ;
break ;
default :
break ;
}
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessages ( TArrayView < TWeakPtr < FCompilerBinding > > Bindings , TArrayView < TWeakPtr < FCompilerEvent > > Events , const FText & MessageText , Compiler : : EMessageType MessageType ) const
2023-11-17 14:09:15 -05:00
{
for ( TWeakPtr < FCompilerBinding > & Binding : Bindings )
{
2023-12-19 11:31:33 -05:00
AddMessageForBinding ( Binding . Pin ( ) , MessageText , MessageType , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
}
for ( TWeakPtr < FCompilerEvent > & Event : Events )
{
2023-12-19 11:31:33 -05:00
AddMessageForEvent ( Event . Pin ( ) , MessageText , MessageType , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
}
if ( Bindings . Num ( ) = = 0 & & Events . Num ( ) = = 0 )
{
AddMessage ( MessageText , MessageType ) ;
}
}
2023-12-19 11:31:33 -05:00
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForBinding ( const TSharedPtr < FCompilerBinding > & Binding , const FText & MessageText , Compiler : : EMessageType MessageType , const FMVVMBlueprintPinId & PinId ) const
2023-11-17 14:09:15 -05:00
{
const FMVVMBlueprintViewBinding * BindingPtr = Binding ? BlueprintView - > GetBindingAt ( Binding - > Key . ViewBindingIndex ) : nullptr ;
if ( BindingPtr )
{
2023-12-19 11:31:33 -05:00
AddMessageForBinding ( * BindingPtr , MessageText , MessageType , PinId ) ;
2023-11-17 14:09:15 -05:00
}
else
{
AddMessage ( MessageText , MessageType ) ;
}
}
2023-12-19 11:31:33 -05:00
FText GetJoinArgumentNames ( const FMVVMBlueprintPinId & PinId )
{
TArray < FText > JoinedNames ;
JoinedNames . Reserve ( PinId . GetNames ( ) . Num ( ) ) ;
for ( FName Name : PinId . GetNames ( ) )
{
JoinedNames . Add ( FText : : FromName ( Name ) ) ;
}
return FText : : Join ( LOCTEXT ( " NameSeperator " , " . " ) , JoinedNames ) ;
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForBinding ( const FMVVMBlueprintViewBinding & Binding , const FText & MessageText , Compiler : : EMessageType MessageType , const FMVVMBlueprintPinId & PinId ) const
2022-10-06 20:08:12 -04:00
{
const FText BindingName = FText : : FromString ( Binding . GetDisplayNameString ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ) ;
FText FormattedError ;
2023-12-19 11:31:33 -05:00
if ( PinId . IsValid ( ) )
2022-10-06 20:08:12 -04:00
{
2023-12-19 11:31:33 -05:00
FormattedError = FText : : Format ( LOCTEXT ( " BindingFormatWithArgument " , " Binding '{0}': Argument '{1}' - {2} " ) , BindingName , UE : : MVVM : : Private : : GetJoinArgumentNames ( PinId ) , MessageText ) ;
2022-10-06 20:08:12 -04:00
}
else
{
2023-03-17 16:33:42 -04:00
FormattedError = FText : : Format ( LOCTEXT ( " BindingFormat " , " Binding '{0}': {1} " ) , BindingName , MessageText ) ;
2022-10-06 20:08:12 -04:00
}
2023-11-17 14:09:15 -05:00
AddMessage ( FormattedError , MessageType ) ;
2022-10-06 20:08:12 -04:00
2023-11-17 14:09:15 -05:00
static EBindingMessageType BindingMessageTypes [ ] = { EBindingMessageType : : Info , EBindingMessageType : : Warning , EBindingMessageType : : Error } ;
FBindingMessage NewMessage = { FormattedError , BindingMessageTypes [ static_cast < int32 > ( MessageType ) ] } ;
2023-03-17 16:33:42 -04:00
BlueprintView - > AddMessageToBinding ( Binding . BindingId , NewMessage ) ;
2022-10-06 20:08:12 -04:00
}
2023-11-17 14:09:15 -05:00
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForEvent ( const TSharedPtr < FCompilerEvent > & Event , const FText & MessageText , Compiler : : EMessageType MessageType , const FMVVMBlueprintPinId & PinId ) const
2023-11-17 14:09:15 -05:00
{
const UMVVMBlueprintViewEvent * EventPtr = Event ? Event - > Event . Get ( ) : nullptr ;
if ( EventPtr )
{
2023-12-19 11:31:33 -05:00
AddMessageForEvent ( EventPtr , MessageText , MessageType , PinId ) ;
2023-11-17 14:09:15 -05:00
}
else
{
AddMessage ( MessageText , MessageType ) ;
}
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForEvent ( const UMVVMBlueprintViewEvent * Event , const FText & MessageText , Compiler : : EMessageType MessageType , const FMVVMBlueprintPinId & PinId ) const
2023-08-21 08:46:45 -04:00
{
const FText EventName = Event - > GetDisplayName ( true ) ;
FText FormattedError ;
2023-12-19 11:31:33 -05:00
if ( PinId . IsValid ( ) )
2023-08-21 08:46:45 -04:00
{
2023-12-19 11:31:33 -05:00
FormattedError = FText : : Format ( LOCTEXT ( " EventFormatWithArgument " , " Event '{0}': Argument '{1}' - {2} " ) , EventName , UE : : MVVM : : Private : : GetJoinArgumentNames ( PinId ) , MessageText ) ;
2023-08-21 08:46:45 -04:00
}
else
{
FormattedError = FText : : Format ( LOCTEXT ( " EventFormat " , " Event '{0}': {1} " ) , EventName , MessageText ) ;
}
2023-11-17 14:09:15 -05:00
AddMessage ( FormattedError , MessageType ) ;
2023-08-21 08:46:45 -04:00
2023-11-17 14:09:15 -05:00
static UMVVMBlueprintViewEvent : : EMessageType BindingMessageTypes [ ] = { UMVVMBlueprintViewEvent : : EMessageType : : Info , UMVVMBlueprintViewEvent : : EMessageType : : Warning , UMVVMBlueprintViewEvent : : EMessageType : : Error } ;
UMVVMBlueprintViewEvent : : FMessage NewMessage = { FormattedError , BindingMessageTypes [ static_cast < int32 > ( MessageType ) ] } ;
2023-08-21 08:46:45 -04:00
Event - > AddCompilationToBinding ( NewMessage ) ;
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForViewModel ( const FMVVMBlueprintViewModelContext & ViewModel , const FText & Message , Compiler : : EMessageType MessageType ) const
2022-10-06 20:08:12 -04:00
{
const FText FormattedError = FText : : Format ( LOCTEXT ( " ViewModelFormat " , " Viewodel '{0}': {1} " ) , ViewModel . GetDisplayName ( ) , Message ) ;
2023-11-17 14:09:15 -05:00
AddMessage ( FormattedError , MessageType ) ;
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForViewModel ( const FText & ViewModelDisplayName , const FText & Message , Compiler : : EMessageType MessageType ) const
2023-11-17 14:09:15 -05:00
{
const FText FormattedError = FText : : Format ( LOCTEXT ( " ViewModelFormat " , " Viewodel '{0}': {1} " ) , ViewModelDisplayName , Message ) ;
AddMessage ( FormattedError , MessageType ) ;
2022-10-06 20:08:12 -04:00
}
2022-06-08 13:25:42 -04:00
2023-08-21 08:46:45 -04:00
2022-06-08 13:25:42 -04:00
void FMVVMViewBlueprintCompiler : : AddExtension ( UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
{
WidgetBlueprintCompilerContext . AddExtension ( Class , ViewExtension ) ;
}
void FMVVMViewBlueprintCompiler : : CleanOldData ( UWidgetBlueprintGeneratedClass * ClassToClean , UObject * OldCDO )
{
// Clean old View
if ( ! WidgetBlueprintCompilerContext . Blueprint - > bIsRegeneratingOnLoad & & WidgetBlueprintCompilerContext . bIsFullCompile )
{
TArray < UObject * > Children ;
const bool bIncludeNestedObjects = false ;
ForEachObjectWithOuter ( ClassToClean , [ & Children ] ( UObject * Child )
{
if ( Cast < UMVVMViewClass > ( Child ) )
{
Children . Add ( Child ) ;
}
} , bIncludeNestedObjects ) ;
for ( UObject * Child : Children )
{
RenameObjectToTransientPackage ( Child ) ;
}
2024-01-19 11:53:42 -05:00
// Clean up the stale viewmodel instances.
TMap < FGuid , TWeakObjectPtr < UObject > > & SavedViewModelInstances = BlueprintView - > GetOuterUMVVMWidgetBlueprintExtension_View ( ) - > TemporaryViewModelInstances ;
TArray < FGuid > DeleteViewModelInstances ;
for ( auto It = SavedViewModelInstances . CreateIterator ( ) ; It ; + + It )
{
uint32 ViewModelIndex = ViewModelCreatorContexts . IndexOfByPredicate ( [ It ] ( const FCompilerViewModelCreatorContext & SourceCreatorContext )
{
return SourceCreatorContext . ViewModelContext . GetViewModelId ( ) = = It - > Key ;
} ) ;
TWeakObjectPtr < UObject > StaticViewModelInstance = nullptr ;
if ( ViewModelIndex ! = INDEX_NONE )
{
const FMVVMBlueprintViewModelContext & ViewModelContext = ViewModelCreatorContexts [ ViewModelIndex ] . ViewModelContext ;
if ( It - > Value . IsValid ( ) )
{
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance
& & ViewModelContext . bExposeInstanceInEditor )
{
StaticViewModelInstance = It - > Value ;
}
else
{
RenameObjectToTransientPackage ( It - > Value . Get ( ) ) ;
}
}
}
if ( ! StaticViewModelInstance . IsValid ( ) )
{
DeleteViewModelInstances . Add ( It - > Key ) ;
SavedViewModelInstances [ It - > Key ] = nullptr ;
}
}
for ( FGuid Guid : DeleteViewModelInstances )
{
SavedViewModelInstances . Remove ( Guid ) ;
}
2022-06-08 13:25:42 -04:00
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateVariables ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
2022-06-08 13:25:42 -04:00
{
if ( ! BlueprintView )
{
return ;
}
2023-11-17 14:09:15 -05:00
if ( ! AreStepsValid ( ) )
2022-06-08 13:25:42 -04:00
{
return ;
}
if ( Context . GetCompileType ( ) = = EKismetCompileType : : SkeletonOnly )
{
2023-11-17 14:09:15 -05:00
CreateWidgetMap ( Context ) ;
CreateBindingList ( Context ) ;
CreateEventList ( Context ) ;
CreateRequiredProperties ( Context ) ;
CreatePublicFunctionsDeclaration ( Context ) ;
2023-07-26 14:04:59 -04:00
}
2022-06-08 13:25:42 -04:00
2024-02-08 13:38:56 -05:00
// Create variable
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
auto CreateVariable = [ & Context ] ( const FCompilerUserWidgetProperty & UserWidgetProperty ) - > FProperty *
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
FEdGraphPinType NewPropertyPinType ( UEdGraphSchema_K2 : : PC_Object , NAME_None , UserWidgetProperty . AuthoritativeClass , EPinContainerType : : None , false , FEdGraphTerminalType ( ) ) ;
FProperty * NewProperty = Context . CreateVariable ( UserWidgetProperty . Name , NewPropertyPinType ) ;
if ( NewProperty ! = nullptr )
2024-01-19 11:53:42 -05:00
{
2024-02-08 13:38:56 -05:00
NewProperty - > SetPropertyFlags ( CPF_BlueprintVisible | CPF_RepSkip ) ;
2024-01-19 11:53:42 -05:00
2024-02-08 13:38:56 -05:00
const bool bIsInstanceExposed = UserWidgetProperty . bInstanced & & UserWidgetProperty . bInstanceExposed ;
if ( bIsInstanceExposed )
2024-01-19 11:53:42 -05:00
{
2024-02-08 13:38:56 -05:00
NewProperty - > SetPropertyFlags ( CPF_Edit | CPF_ExportObject | CPF_PersistentInstance | CPF_InstancedReference | CPF_NonNullable | CPF_NoClear ) ;
if ( UserWidgetProperty . AuthoritativeClass - > HasAnyClassFlags ( CLASS_HasInstancedReference ) )
{
NewProperty - > SetPropertyFlags ( CPF_ContainsInstancedReference ) ;
}
}
else
{
NewProperty - > SetPropertyFlags ( CPF_Transient | CPF_DuplicateTransient ) ;
2024-01-19 11:53:42 -05:00
}
2024-02-08 13:38:56 -05:00
if ( UserWidgetProperty . bExposeOnSpawn )
{
NewProperty - > SetPropertyFlags ( CPF_ExposeOnSpawn ) ;
}
else if ( ! bIsInstanceExposed )
{
NewProperty - > SetPropertyFlags ( CPF_DisableEditOnInstance ) ;
}
2024-01-19 11:53:42 -05:00
2024-02-08 13:38:56 -05:00
if ( UserWidgetProperty . bReadOnly )
{
NewProperty - > SetPropertyFlags ( CPF_BlueprintReadOnly ) ;
}
2022-06-08 13:25:42 -04:00
# if WITH_EDITOR
2024-02-08 13:38:56 -05:00
if ( ! UserWidgetProperty . BlueprintSetter . IsEmpty ( ) )
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_PropertySetFunction , * UserWidgetProperty . BlueprintSetter ) ;
}
if ( ! UserWidgetProperty . DisplayName . IsEmpty ( ) )
{
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_DisplayName , * UserWidgetProperty . DisplayName . ToString ( ) ) ;
}
if ( ! UserWidgetProperty . CategoryName . IsEmpty ( ) )
{
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_FunctionCategory , * UserWidgetProperty . CategoryName ) ;
}
if ( UserWidgetProperty . bExposeOnSpawn )
{
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_ExposeOnSpawn , TEXT ( " true " ) ) ;
}
if ( UserWidgetProperty . bPrivate )
{
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_Private , TEXT ( " true " ) ) ;
}
if ( bIsInstanceExposed )
{
NewProperty - > SetMetaData ( FName ( " EditInline " ) , TEXT ( " true " ) ) ;
}
# endif
}
return NewProperty ;
} ;
for ( FCompilerUserWidgetProperty & UserWidgetProperty : NeededUserWidgetProperties )
{
check ( UserWidgetProperty . AuthoritativeClass ) ;
check ( ! UserWidgetProperty . Name . IsNone ( ) ) ;
UserWidgetProperty . Property = nullptr ; // Skeletal set the property, Full needs the new property
FMVVMConstFieldVariant UserWidgetPropertyField = BindingHelper : : FindFieldByName ( Context . GetGeneratedClass ( ) , FMVVMBindingName ( UserWidgetProperty . Name ) ) ;
// The class is not linked yet. It may not be available yet.
if ( UserWidgetPropertyField . IsEmpty ( ) )
{
for ( FField * Field = Context . GetGeneratedClass ( ) - > ChildProperties ; Field ! = nullptr ; Field = Field - > Next )
{
if ( Field - > GetFName ( ) = = UserWidgetProperty . Name )
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
if ( CastField < FProperty > ( Field ) )
{
UserWidgetPropertyField = FMVVMFieldVariant ( CastField < FProperty > ( Field ) ) ;
}
else
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FieldIsNotProperty " , " The field for source '{0}' exists but is not a property. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
}
break ;
2022-06-08 13:25:42 -04:00
}
2024-02-08 13:38:56 -05:00
}
for ( UField * Field = Context . GetGeneratedClass ( ) - > Children ; Field ! = nullptr ; Field = Field - > Next )
{
if ( Field - > GetFName ( ) = = UserWidgetProperty . Name )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FieldIsNotProperty " , " The field for source '{0}' exists but is not a property. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
2024-02-08 13:38:56 -05:00
break ;
2022-06-08 13:25:42 -04:00
}
2023-10-11 13:52:26 -04:00
}
}
2024-02-08 13:38:56 -05:00
if ( UserWidgetPropertyField . IsEmpty ( ) )
2023-10-11 13:52:26 -04:00
{
2024-02-08 13:38:56 -05:00
UClass * ParentClass = Context . GetGeneratedClass ( ) - > GetSuperClass ( ) ;
if ( const FProperty * Property = ParentClass - > FindPropertyByName ( UserWidgetProperty . Name ) )
2023-10-11 13:52:26 -04:00
{
2024-02-08 13:38:56 -05:00
UserWidgetPropertyField = FMVVMFieldVariant ( Property ) ;
2022-06-08 13:25:42 -04:00
}
}
2024-02-08 13:38:56 -05:00
// Will always create viewmodel properties.
// Will never create properties for animation or other Self.Object
// Will create properties for widget when they are not already created.
if ( UserWidgetProperty . CreationType = = FCompilerUserWidgetProperty : : ECreationType : : CreateOnlyIfDoesntExist & & ! UserWidgetPropertyField . IsEmpty ( ) )
2023-10-11 13:52:26 -04:00
{
2024-02-08 13:38:56 -05:00
// Viewmodel property cannot already exist. It will creates issue with initialization and with View::SetViewModel.
const UClass * OwnerClass = Cast < UClass > ( UserWidgetPropertyField . GetOwner ( ) ) ;
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " PropertyAlreadyExistInParent " , " There is already a property named '{0}' in scope '{1}'. " ) , UserWidgetProperty . DisplayName , ( OwnerClass ? OwnerClass - > GetDisplayNameText ( ) : FText : : GetEmpty ( ) ) ) . ToString ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
2024-02-08 13:38:56 -05:00
if ( ! UserWidgetPropertyField . IsEmpty ( ) )
2023-11-17 14:09:15 -05:00
{
2024-02-08 13:38:56 -05:00
if ( UserWidgetPropertyField . IsFunction ( ) )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FunctionCanBeSource " , " Function can't be source. '{0}'. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
if ( ! BindingHelper : : IsValidForSourceBinding ( UserWidgetPropertyField ) )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FieldNotAccessibleAtRuntime " , " The field for source '{0}' exists but is not accessible at runtime. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
ensure ( UserWidgetPropertyField . IsProperty ( ) ) ;
const FProperty * Property = UserWidgetPropertyField . IsProperty ( ) ? UserWidgetPropertyField . GetProperty ( ) : nullptr ;
const FObjectProperty * ObjectProperty = CastField < const FObjectProperty > ( Property ) ;
const bool bIsCompatible = ObjectProperty & & UserWidgetProperty . AuthoritativeClass - > IsChildOf ( ObjectProperty - > PropertyClass ) ;
if ( ! bIsCompatible )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " PropertyExistsAndNotCompatible " , " There is already a property named '{0}' that is not compatible with the source of the same name. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
const bool bIsBindWidget = FWidgetBlueprintEditorUtils : : IsBindWidgetProperty ( ObjectProperty ) ;
if ( Context . GetGeneratedClass ( ) ! = ObjectProperty - > GetOwnerStruct ( ) & & ! bIsBindWidget )
{
// Widget needs to be BindWidget to be reused as a property.
const UClass * OwnerClass = Cast < UClass > ( ObjectProperty - > GetOwnerStruct ( ) ) ;
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " WidgetPropertyAlreadyExist " , " There is already a property named '{0}' in scope '{1}' for the widget. Are you missing a BindWidget?. " ) , UserWidgetProperty . DisplayName , ( OwnerClass ? OwnerClass - > GetDisplayNameText ( ) : FText : : GetEmpty ( ) ) ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
2023-11-17 14:09:15 -05:00
}
2024-02-08 13:38:56 -05:00
// Can we reused the property or we need to create a new one.
bool bCreateVariable = UserWidgetProperty . CreationType = = FCompilerUserWidgetProperty : : ECreationType : : CreateOnlyIfDoesntExist
| | ( UserWidgetProperty . CreationType = = FCompilerUserWidgetProperty : : ECreationType : : CreateIfDoesntExist & & UserWidgetPropertyField . IsEmpty ( ) ) ;
if ( bCreateVariable )
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
UserWidgetProperty . Property = CreateVariable ( UserWidgetProperty ) ;
}
else if ( UserWidgetPropertyField . IsProperty ( ) )
{
UserWidgetProperty . Property = UserWidgetPropertyField . GetProperty ( ) ;
2023-10-11 13:52:26 -04:00
}
2024-02-08 13:38:56 -05:00
if ( UserWidgetProperty . Property = = nullptr )
2023-10-11 13:52:26 -04:00
{
2024-02-08 13:38:56 -05:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " VariableCouldNotBeCreated " , " The variable for '{0}' could not be created. " ) , UserWidgetProperty . DisplayName ) . ToString ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
}
2024-02-08 13:38:56 -05:00
}
2022-06-08 13:25:42 -04:00
2024-02-08 13:38:56 -05:00
// Public function permissions
{
FPathPermissionList & FunctionPermissions = GetMutableDefault < UBlueprintEditorSettings > ( ) - > GetFunctionPermissions ( ) ;
FName ContextName ;
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
TStringBuilder < 512 > ContextNameStr ;
Context . GetGeneratedClass ( ) - > GetPathName ( nullptr , ContextNameStr ) ;
ContextName = ContextNameStr . ToString ( ) ;
2022-06-08 13:25:42 -04:00
}
2024-02-08 13:38:56 -05:00
FunctionPermissions . UnregisterOwner ( ContextName ) ;
const bool bHasFiltering = GetMutableDefault < UBlueprintEditorSettings > ( ) - > GetFunctionPermissions ( ) . HasFiltering ( ) ;
if ( bHasFiltering )
2022-06-08 13:25:42 -04:00
{
2024-02-08 13:38:56 -05:00
for ( FName FunctionName : FunctionPermissionsToAdd )
{
TStringBuilder < 512 > FunctionPath ;
FunctionPath < < ContextName ;
FunctionPath < < TEXT ( ' : ' ) ;
FunctionPath < < FunctionName ;
FunctionPermissions . AddAllowListItem ( ContextName , FunctionPath ) ;
}
2022-06-08 13:25:42 -04:00
}
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateWidgetMap ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
2022-06-08 13:25:42 -04:00
{
// The widget tree is not created yet for SKEL class.
//Context.GetGeneratedClass()->GetWidgetTreeArchetype()
WidgetNameToWidgetPointerMap . Reset ( ) ;
TArray < UWidget * > Widgets ;
UWidgetBlueprint * WidgetBPToScan = Context . GetWidgetBlueprint ( ) ;
while ( WidgetBPToScan ! = nullptr )
{
Widgets = WidgetBPToScan - > GetAllSourceWidgets ( ) ;
if ( Widgets . Num ( ) ! = 0 )
{
break ;
}
WidgetBPToScan = WidgetBPToScan - > ParentClass & & WidgetBPToScan - > ParentClass - > ClassGeneratedBy ? Cast < UWidgetBlueprint > ( WidgetBPToScan - > ParentClass - > ClassGeneratedBy ) : nullptr ;
}
for ( UWidget * Widget : Widgets )
{
WidgetNameToWidgetPointerMap . Add ( Widget - > GetFName ( ) , Widget ) ;
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateBindingList ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
ensure ( Context . GetCompileType ( ) = = EKismetCompileType : : SkeletonOnly ) ;
if ( Context . GetCompileType ( ) ! = EKismetCompileType : : SkeletonOnly )
{
return ;
}
ValidBindings . Reset ( ) ;
// Build the list of bindings that we should compile.
for ( int32 Index = 0 ; Index < BlueprintView - > GetNumBindings ( ) ; + + Index )
{
FMVVMBlueprintViewBinding * BindingPtr = BlueprintView - > GetBindingAt ( Index ) ;
if ( BindingPtr = = nullptr )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " BindingInvalidIndex " , " Internal error: Tried to fetch binding for invalid index {0}. " ) , Index ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
FMVVMBlueprintViewBinding & Binding = * BindingPtr ;
if ( ! Binding . bCompile )
{
continue ;
}
const bool bHasConversionFunction = Binding . Conversion . GetConversionFunction ( true ) | | Binding . Conversion . GetConversionFunction ( false ) ;
if ( bHasConversionFunction & & Binding . BindingType = = EMVVMBindingMode : : TwoWay )
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " TwoWayBindingsWithConversion " , " Two-way bindings are not allowed to use conversion functions. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
continue ;
}
if ( IsForwardBinding ( Binding . BindingType ) )
{
TSharedRef < FCompilerBinding > ValidBinding = MakeShared < FCompilerBinding > ( ) ;
ValidBinding - > Key . ViewBindingIndex = Index ;
ValidBinding - > Key . bIsForwardBinding = true ;
ValidBinding - > bIsOneTimeBinding = IsOneTimeBinding ( Binding . BindingType ) ;
ValidBindings . Add ( ValidBinding ) ;
}
if ( IsBackwardBinding ( Binding . BindingType ) )
{
TSharedRef < FCompilerBinding > ValidBinding = MakeShared < FCompilerBinding > ( ) ;
ValidBinding - > Key . ViewBindingIndex = Index ;
ValidBinding - > Key . bIsForwardBinding = false ;
ValidBinding - > bIsOneTimeBinding = IsOneTimeBinding ( Binding . BindingType ) ;
ValidBindings . Add ( ValidBinding ) ;
}
}
}
void FMVVMViewBlueprintCompiler : : CreateEventList ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
{
for ( UMVVMBlueprintViewEvent * EventPtr : BlueprintView - > GetEvents ( ) )
{
if ( EventPtr = = nullptr )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " EventInvalid " , " Internal error: An event is invalid. " ) . ToString ( ) ) ;
bIsCreateVariableStepValid = false ;
continue ;
}
if ( ! EventPtr - > bCompile )
{
continue ;
}
if ( ! EventPtr - > GetEventPath ( ) . HasPaths ( ) )
{
2024-03-26 02:39:40 -04:00
AddMessageForEvent ( EventPtr , LOCTEXT ( " EventInvalidEventPath " , " The event path is invalid. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
continue ;
}
TSharedRef < FCompilerEvent > ValidEvent = MakeShared < FCompilerEvent > ( ) ;
ValidEvent - > Event = EventPtr ;
ValidEvents . Add ( ValidEvent ) ;
}
}
void FMVVMViewBlueprintCompiler : : CreateRequiredProperties ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
{
ensure ( Context . GetCompileType ( ) = = EKismetCompileType : : SkeletonOnly ) ;
if ( Context . GetCompileType ( ) ! = EKismetCompileType : : SkeletonOnly )
{
return ;
}
NeededBindingSources . Reset ( ) ;
NeededUserWidgetProperties . Reset ( ) ;
ViewModelCreatorContexts . Reset ( ) ;
ViewModelSettersToGenerate . Reset ( ) ;
2022-06-08 13:25:42 -04:00
2022-10-06 20:08:12 -04:00
TSet < FGuid > ViewModelGuids ;
2022-06-08 13:25:42 -04:00
for ( const FMVVMBlueprintViewModelContext & ViewModelContext : BlueprintView - > GetViewModels ( ) )
{
if ( ! ViewModelContext . GetViewModelId ( ) . IsValid ( ) )
{
2024-03-26 02:39:40 -04:00
AddMessageForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidGuid " , " GUID is invalid. " ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
2022-10-06 20:08:12 -04:00
if ( ViewModelGuids . Contains ( ViewModelContext . GetViewModelId ( ) ) )
2022-06-08 13:25:42 -04:00
{
2024-03-26 02:39:40 -04:00
AddMessageForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelAlreadyAdded " , " Identical viewmodel has already been added. " ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
2022-10-06 20:08:12 -04:00
ViewModelGuids . Add ( ViewModelContext . GetViewModelId ( ) ) ;
2022-06-08 13:25:42 -04:00
2022-06-29 19:00:59 -04:00
if ( ViewModelContext . GetViewModelClass ( ) = = nullptr | | ! ViewModelContext . IsValid ( ) )
2022-06-08 13:25:42 -04:00
{
2024-03-26 02:39:40 -04:00
AddMessageForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidClass " , " Invalid class. " ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
2023-11-17 14:09:15 -05:00
FName PropertyName = ViewModelContext . GetViewModelName ( ) ;
2023-03-10 23:20:09 -05:00
const bool bCreateSetterFunction = GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter
& & ( ViewModelContext . bCreateSetterFunction | | ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual ) ;
2023-11-17 14:09:15 -05:00
FString SetterFunctionName ;
if ( bCreateSetterFunction )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
SetterFunctionName = TEXT ( " Set " ) + ViewModelContext . GetViewModelName ( ) . ToString ( ) ;
FCompilerViewModelSetter & ViewModelSetter = ViewModelSettersToGenerate . AddDefaulted_GetRef ( ) ;
ViewModelSetter . Class = ViewModelContext . GetViewModelClass ( ) ;
ViewModelSetter . PropertyName = PropertyName ;
ViewModelSetter . BlueprintSetter = SetterFunctionName ;
ViewModelSetter . DisplayName = ViewModelContext . GetDisplayName ( ) ;
}
TSharedRef < FCompilerBindingSource > SourceContext = MakeShared < FCompilerBindingSource > ( ) ;
{
SourceContext - > AuthoritativeClass = ViewModelContext . GetViewModelClass ( ) ;
SourceContext - > Name = PropertyName ;
SourceContext - > Type = FCompilerBindingSource : : EType : : ViewModel ;
SourceContext - > bIsOptional = ViewModelContext . bOptional ;
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
SourceContext - > bIsOptional = true ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance )
{
SourceContext - > bIsOptional = false ;
2023-11-16 20:11:58 -05:00
}
2023-11-17 14:09:15 -05:00
NeededBindingSources . Add ( SourceContext ) ;
}
{
2023-12-01 10:43:39 -05:00
bool bIsPublicReadable = ViewModelContext . InstancedViewModel = = nullptr & & ViewModelContext . bCreateGetterFunction ;
2023-11-17 14:09:15 -05:00
FCompilerUserWidgetProperty & SourceVariable = NeededUserWidgetProperties . AddDefaulted_GetRef ( ) ;
SourceVariable . AuthoritativeClass = ViewModelContext . GetViewModelClass ( ) ;
SourceVariable . Name = PropertyName ;
SourceVariable . DisplayName = ViewModelContext . GetDisplayName ( ) ;
SourceVariable . CategoryName = TEXT ( " Viewmodel " ) ;
SourceVariable . BlueprintSetter = SetterFunctionName ;
SourceVariable . CreationType = FCompilerUserWidgetProperty : : ECreationType : : CreateOnlyIfDoesntExist ;
SourceVariable . bExposeOnSpawn = bCreateSetterFunction ;
2023-12-01 10:43:39 -05:00
SourceVariable . bPrivate = ! bIsPublicReadable ;
2023-11-17 14:09:15 -05:00
SourceVariable . bReadOnly = ! bCreateSetterFunction ;
2024-01-19 11:53:42 -05:00
SourceVariable . bInstanced = ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance ;
SourceVariable . bInstanceExposed = ViewModelContext . bExposeInstanceInEditor ;
2023-11-17 14:09:15 -05:00
}
{
FCompilerViewModelCreatorContext & CreatorContext = ViewModelCreatorContexts . AddDefaulted_GetRef ( ) ;
CreatorContext . ViewModelContext = ViewModelContext ;
CreatorContext . Source = SourceContext ;
}
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
TSet < FName > WidgetSourcesCreated ;
TSet < FName > WidgetUserPropertyCreated ;
bool bSelfBindingSourceCreated = NeededBindingSources . ContainsByPredicate ( [ ] ( const TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Type = = FCompilerBindingSource : : EType : : Self ;
} ) ;
2022-10-12 14:35:36 -04:00
const FName DefaultWidgetCategory = Context . GetWidgetBlueprint ( ) - > GetFName ( ) ;
2023-11-17 14:09:15 -05:00
auto GenerateCompilerContext = [ Self = this , DefaultWidgetCategory , Class = Context . GetGeneratedClass ( ) , & ViewModelGuids , & WidgetSourcesCreated , & WidgetUserPropertyCreated , & bSelfBindingSourceCreated ] ( bool bInCreateSource , const FMVVMBlueprintPropertyPath & PropertyPath ) - > TValueOrError < void , FText >
2023-08-21 08:46:45 -04:00
{
2023-10-20 10:45:57 -04:00
switch ( PropertyPath . GetSource ( Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) )
{
case EMVVMBlueprintFieldPathSource : : SelfContext :
2023-11-17 14:09:15 -05:00
{
if ( ! bSelfBindingSourceCreated )
{
bSelfBindingSourceCreated = true ;
TSharedRef < FCompilerBindingSource > SourceContext = MakeShared < FCompilerBindingSource > ( ) ;
SourceContext - > AuthoritativeClass = nullptr ;
SourceContext - > Name = Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) - > GetFName ( ) ;
SourceContext - > Type = FCompilerBindingSource : : EType : : Self ;
SourceContext - > bIsOptional = false ;
Self - > NeededBindingSources . Add ( SourceContext ) ;
}
2023-10-20 10:45:57 -04:00
return MakeValue ( ) ;
2023-11-17 14:09:15 -05:00
}
2023-10-20 10:45:57 -04:00
case EMVVMBlueprintFieldPathSource : : Widget :
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
// Only do this once
bool bNewCreateSource = ! WidgetSourcesCreated . Contains ( PropertyPath . GetWidgetName ( ) ) & & bInCreateSource ;
bool bNewAddWidgetProperty = ! WidgetUserPropertyCreated . Contains ( PropertyPath . GetWidgetName ( ) ) ;
if ( bNewCreateSource | | bNewAddWidgetProperty )
2023-08-21 08:46:45 -04:00
{
UWidget * * WidgetPtr = Self - > WidgetNameToWidgetPointerMap . Find ( PropertyPath . GetWidgetName ( ) ) ;
if ( WidgetPtr = = nullptr | | * WidgetPtr = = nullptr )
{
return MakeError ( FText : : Format ( LOCTEXT ( " InvalidWidgetFormat " , " Could not find the targeted widget: {0} " ) , FText : : FromName ( PropertyPath . GetWidgetName ( ) ) ) ) ;
}
UWidget * Widget = * WidgetPtr ;
2023-11-17 14:09:15 -05:00
if ( bNewCreateSource )
{
FName PropertyName = PropertyPath . GetWidgetName ( ) ;
{
TSharedRef < FCompilerBindingSource > * FoundCompilerSource = Self - > NeededBindingSources . FindByPredicate ( [ PropertyName ] ( const TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Name = = PropertyName ;
} ) ;
if ( FoundCompilerSource ! = nullptr )
{
// It should be in the TSet<FName> WidgetSources
return MakeError ( FText : : Format ( LOCTEXT ( " ExistingWidgetSourceFormat " , " Internal error. A widget source already exist: {0} " ) , FText : : FromName ( PropertyPath . GetWidgetName ( ) ) ) ) ;
}
}
TSharedRef < FCompilerBindingSource > SourceContext = MakeShared < FCompilerBindingSource > ( ) ;
SourceContext - > AuthoritativeClass = Widget - > GetClass ( ) ;
SourceContext - > Name = PropertyName ;
SourceContext - > Type = FCompilerBindingSource : : EType : : Widget ;
SourceContext - > bIsOptional = false ;
Self - > NeededBindingSources . Add ( SourceContext ) ;
WidgetSourcesCreated . Add ( Widget - > GetFName ( ) ) ;
}
if ( bNewAddWidgetProperty )
{
FCompilerUserWidgetProperty & SourceVariable = Self - > NeededUserWidgetProperties . AddDefaulted_GetRef ( ) ;
SourceVariable . AuthoritativeClass = Widget - > GetClass ( ) ;
SourceVariable . Name = PropertyPath . GetWidgetName ( ) ;
SourceVariable . DisplayName = FText : : FromString ( Widget - > GetDisplayLabel ( ) ) ;
SourceVariable . CategoryName = TEXT ( " Widget " ) ;
SourceVariable . CreationType = FCompilerUserWidgetProperty : : ECreationType : : CreateIfDoesntExist ;
SourceVariable . bExposeOnSpawn = false ;
2024-02-12 10:07:57 -05:00
SourceVariable . bPrivate = true ;
2023-11-17 15:09:55 -05:00
SourceVariable . bPrivate = false ;
2023-11-17 14:09:15 -05:00
SourceVariable . bReadOnly = true ;
2024-01-19 11:53:42 -05:00
SourceVariable . bInstanced = false ;
SourceVariable . bInstanceExposed = false ;
2023-11-17 14:09:15 -05:00
WidgetUserPropertyCreated . Add ( Widget - > GetFName ( ) ) ;
}
2023-08-21 08:46:45 -04:00
}
2023-10-20 10:45:57 -04:00
break ;
2023-08-21 08:46:45 -04:00
}
2023-10-20 10:45:57 -04:00
case EMVVMBlueprintFieldPathSource : : ViewModel :
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
const FMVVMBlueprintViewModelContext * SourceViewModelContext = Self - > BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) ;
2023-08-21 08:46:45 -04:00
if ( SourceViewModelContext = = nullptr )
{
return MakeError ( FText : : Format ( LOCTEXT ( " BindingViewModelNotFound " , " Could not find viewmodel with GUID {0}. " ) , GetViewModelIdText ( PropertyPath ) ) ) ;
}
if ( ! ViewModelGuids . Contains ( SourceViewModelContext - > GetViewModelId ( ) ) )
{
return MakeError ( FText : : Format ( LOCTEXT ( " BindingViewModelInvalid " , " Viewmodel {0} {1} was invalid. " ) , SourceViewModelContext - > GetDisplayName ( ) , GetViewModelIdText ( PropertyPath ) ) ) ;
}
2023-10-20 10:45:57 -04:00
break ;
2023-08-21 08:46:45 -04:00
}
2023-10-20 10:45:57 -04:00
default :
2023-08-21 08:46:45 -04:00
return MakeError ( LOCTEXT ( " SourcePathNotSet " , " A source path is required, but not set. " ) ) ;
}
return MakeValue ( ) ;
} ;
2023-11-17 14:09:15 -05:00
// Find the start property for each path.
//The full path will be tested later. We want to build the list of property needed before generating the graphs.
for ( const TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
const FMVVMBlueprintViewBinding & Binding = * ( BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ) ;
2023-11-16 23:04:14 -05:00
2023-12-19 11:31:33 -05:00
auto RunGenerateCompilerSourceContext = [ Self = this , & GenerateCompilerContext , & Binding ] ( bool bCreateSource , const FMVVMBlueprintPropertyPath & PropertyPath , const FMVVMBlueprintPinId & PinId )
2023-11-16 20:11:58 -05:00
{
2023-11-17 14:09:15 -05:00
TValueOrError < void , FText > SourceContextResult = GenerateCompilerContext ( bCreateSource , PropertyPath ) ;
if ( SourceContextResult . HasError ( ) )
2023-11-16 20:11:58 -05:00
{
2024-03-26 02:39:40 -04:00
Self - > AddMessageForBinding ( Binding , SourceContextResult . StealError ( ) , Compiler : : EMessageType : : Error , PinId ) ;
2023-11-17 14:09:15 -05:00
Self - > bIsCreateVariableStepValid = false ;
}
} ;
UMVVMBlueprintViewConversionFunction * ConversionFunction = Binding . Conversion . GetConversionFunction ( ValidBinding - > Key . bIsForwardBinding ) ;
if ( ConversionFunction )
{
// validate the sources
for ( const FMVVMBlueprintPin & Pin : ConversionFunction - > GetPins ( ) )
{
if ( Pin . UsedPathAsValue ( ) )
{
2023-12-19 11:31:33 -05:00
if ( ! Pin . IsValid ( ) )
2023-07-26 14:04:59 -04:00
{
2023-11-17 14:09:15 -05:00
AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " InvalidFunctionArgumentPathName " , " The conversion function {0} has an invalid argument. " ) , FText : : FromString ( Binding . GetDisplayNameString ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
bIsCreateVariableStepValid = false ;
2023-08-04 10:49:38 -04:00
continue ;
2023-07-26 14:04:59 -04:00
}
2023-12-19 11:31:33 -05:00
RunGenerateCompilerSourceContext ( true , Pin . GetPath ( ) , Pin . GetId ( ) ) ;
2022-10-06 20:08:12 -04:00
}
}
2023-11-17 14:09:15 -05:00
// validate the destination
const FMVVMBlueprintPropertyPath & BindingDestinationPath = ValidBinding - > Key . bIsForwardBinding ? Binding . DestinationPath : Binding . SourcePath ;
2023-12-19 11:31:33 -05:00
RunGenerateCompilerSourceContext ( false , BindingDestinationPath , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
}
else
{
// if we aren't using a conversion function, validate the source and destination paths
2023-12-19 11:31:33 -05:00
RunGenerateCompilerSourceContext ( ValidBinding - > Key . bIsForwardBinding , Binding . SourcePath , FMVVMBlueprintPinId ( ) ) ;
RunGenerateCompilerSourceContext ( ! ValidBinding - > Key . bIsForwardBinding , Binding . DestinationPath , FMVVMBlueprintPinId ( ) ) ;
2023-08-21 08:46:45 -04:00
}
}
// Find the event property.
//All inputs must also exist (for the BP to compile).
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FCompilerEvent > & ValidEvent : ValidEvents )
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
UMVVMBlueprintViewEvent * EventPtr = ValidEvent - > Event . Get ( ) ;
check ( EventPtr ) ;
2023-12-19 11:31:33 -05:00
auto RunGenerateCompilerSourceContext = [ Self = this , & GenerateCompilerContext , EventPtr ] ( bool bCreateSource , const FMVVMBlueprintPropertyPath & PropertyPath , const FMVVMBlueprintPinId & PinId )
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
TValueOrError < void , FText > SourceContextResult = GenerateCompilerContext ( bCreateSource , PropertyPath ) ;
2023-08-21 08:46:45 -04:00
if ( SourceContextResult . HasError ( ) )
{
2024-03-26 02:39:40 -04:00
Self - > AddMessageForEvent ( EventPtr , SourceContextResult . StealError ( ) , Compiler : : EMessageType : : Error , PinId ) ;
2023-11-17 14:09:15 -05:00
Self - > bIsCreateVariableStepValid = false ;
2023-08-21 08:46:45 -04:00
}
} ;
2023-12-19 11:31:33 -05:00
RunGenerateCompilerSourceContext ( false , EventPtr - > GetEventPath ( ) , FMVVMBlueprintPinId ( ) ) ;
RunGenerateCompilerSourceContext ( false , EventPtr - > GetDestinationPath ( ) , FMVVMBlueprintPinId ( ) ) ;
2023-08-21 08:46:45 -04:00
for ( const FMVVMBlueprintPin & Pin : EventPtr - > GetPins ( ) )
{
if ( Pin . UsedPathAsValue ( ) )
{
2023-12-19 11:31:33 -05:00
if ( ! Pin . IsValid ( ) )
2023-11-17 14:09:15 -05:00
{
2023-12-19 11:31:33 -05:00
AddMessageForEvent ( EventPtr
, LOCTEXT ( " InvalidEventArgumentPathName " , " The event has an invalid argument. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
) ;
2023-11-17 14:09:15 -05:00
bIsCreateVariableStepValid = false ;
continue ;
}
2024-02-15 15:40:59 -05:00
RunGenerateCompilerSourceContext ( true , Pin . GetPath ( ) , Pin . GetId ( ) ) ;
2022-10-06 20:08:12 -04:00
}
2022-06-08 13:25:42 -04:00
}
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreatePublicFunctionsDeclaration ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context )
2022-06-08 13:25:42 -04:00
{
2022-08-24 13:14:51 -04:00
// Clean all previous intermediate function graph. It should stay alive. The graph lives on the Blueprint not on the class and it's used to generate the UFunction.
{
for ( UEdGraph * OldGraph : BlueprintView - > TemporaryGraph )
{
if ( OldGraph )
{
RenameObjectToTransientPackage ( OldGraph ) ;
}
}
BlueprintView - > TemporaryGraph . Reset ( ) ;
}
2023-07-26 14:04:59 -04:00
if ( GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter )
2023-03-10 23:20:09 -05:00
{
2023-11-17 14:09:15 -05:00
for ( FCompilerViewModelSetter & Setter : ViewModelSettersToGenerate )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
ensure ( Setter . SetterGraph = = nullptr ) ;
Setter . SetterGraph = UE : : MVVM : : FunctionGraphHelper : : CreateIntermediateFunctionGraph (
WidgetBlueprintCompilerContext
, Setter . BlueprintSetter
, ( FUNC_BlueprintCallable | FUNC_Public | FUNC_Final )
, TEXT ( " Viewmodel " )
, false ) ;
if ( Setter . SetterGraph = = nullptr | | Setter . SetterGraph - > GetFName ( ) ! = FName ( * Setter . BlueprintSetter ) )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " SetterNameAlreadyExists " , " The setter name {0} already exists and could not be autogenerated. " ) ,
FText : : FromString ( Setter . BlueprintSetter )
) . ToString ( )
) ;
bIsCreateVariableStepValid = false ;
continue ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
2024-02-08 13:38:56 -05:00
BlueprintView - > TemporaryGraph . Add ( Setter . SetterGraph ) ;
FunctionPermissionsToAdd . Add ( Setter . SetterGraph - > GetFName ( ) ) ;
2023-11-17 14:09:15 -05:00
UE : : MVVM : : FunctionGraphHelper : : AddFunctionArgument ( Setter . SetterGraph , const_cast < UClass * > ( Setter . Class ) , " Viewmodel " ) ;
2023-07-26 14:04:59 -04:00
}
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateFunctions ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
2023-07-26 14:04:59 -04:00
{
2023-11-17 14:09:15 -05:00
if ( ! AreStepsValid ( ) )
2023-06-16 11:26:16 -04:00
{
return ;
}
2023-11-17 14:09:15 -05:00
SourceViewModelDynamicCreatorContexts . Reset ( ) ;
2023-06-16 11:26:16 -04:00
2023-11-17 14:09:15 -05:00
CategorizeBindings ( Context ) ;
CategorizeEvents ( Context ) ;
CreateWriteFieldContexts ( Context ) ;
CreateViewModelSetters ( Context ) ;
CreateIntermediateGraphFunctions ( Context ) ;
}
void FMVVMViewBlueprintCompiler : : CategorizeBindings ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
{
// Find the type of the bindings
for ( TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
{
FMVVMBlueprintViewBinding & Binding = * BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ;
check ( ValidBinding - > Type = = FCompilerBinding : : EType : : Unknown ) ;
ValidBinding - > Type = FCompilerBinding : : EType : : Invalid ;
UMVVMBlueprintViewConversionFunction * ConversionFunction = Binding . Conversion . GetConversionFunction ( ValidBinding - > Key . bIsForwardBinding ) ;
if ( ConversionFunction )
{
UClass * NewClass = WidgetBlueprintCompilerContext . NewClass ;
if ( ! ConversionFunction - > IsValid ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) )
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " InvalidConversionFunction " , " The conversion function is invalid. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
// Make sure the graph is up to date
UEdGraph * WrapperGraph = ConversionFunction - > GetOrCreateIntermediateWrapperGraph ( WidgetBlueprintCompilerContext ) ;
if ( WrapperGraph = = nullptr )
{
2023-12-19 11:31:33 -05:00
AddMessageForBinding ( Binding , LOCTEXT ( " InvalidConversionFunctionGraph " , " The conversion function graph could not be generated. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
2023-12-01 09:37:20 -05:00
ConversionFunction - > UpdatePinValues ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ;
if ( ConversionFunction - > HasOrphanedPin ( ) )
{
2023-12-19 11:31:33 -05:00
AddMessageForBinding ( Binding , LOCTEXT ( " InvalidConversionFunctionGraphOrphaned " , " The conversion function has an orphaned pin. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
) ;
2023-12-01 09:37:20 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
2023-11-17 14:09:15 -05:00
if ( ConversionFunction - > GetPins ( ) . Num ( ) = = 0 )
{
AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " InvalidNumberOfFunctionPin " , " The conversion function {0} has no source. " ) , FText : : FromString ( Binding . GetDisplayNameString ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
bIsCreateFunctionsStepValid = false ;
continue ;
}
if ( ConversionFunction - > NeedsWrapperGraph ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) )
{
ValidBinding - > Type = FCompilerBinding : : EType : : ComplexConversionFunction ;
}
else
{
ValidBinding - > Type = FCompilerBinding : : EType : : SimpleConversionFunction ;
}
ValidBinding - > ConversionFunction = ConversionFunction ;
// Because the editor use the destination to order the bindings, the destination path can be "valid". Pointing only to the WidgetName.
//const FMVVMBlueprintPropertyPath& BindingSourcePath = ValidBinding->Key.bIsForwardBinding ? Binding.SourcePath : Binding.DestinationPath;
//if (BindingSourcePath.IsValid())
//{
2023-12-19 11:31:33 -05:00
// AddMessageForBinding(Binding, LOCTEXT("ShouldNotHaveSourceWarning", "Internal Error. The binding should not have a source."), EMessageType::Warning, FMVVMBlueprintPinId());
2023-11-17 14:09:15 -05:00
//}
}
else
{
ValidBinding - > Type = FCompilerBinding : : EType : : Assignment ;
}
}
}
void FMVVMViewBlueprintCompiler : : CategorizeEvents ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
{
for ( TSharedRef < FCompilerEvent > & Event : ValidEvents )
{
Event - > Type = FCompilerEvent : : EType : : Invalid ;
UMVVMBlueprintViewEvent * EventPtr = Event - > Event . Get ( ) ;
UEdGraph * WrapperGraph = EventPtr - > GetOrCreateWrapperGraph ( ) ;
if ( WrapperGraph = = nullptr )
{
2024-03-26 02:39:40 -04:00
AddMessageForEvent ( EventPtr , LOCTEXT ( " InvalidEventGraph " , " The event could not be generated. " ) , Compiler : : EMessageType : : Warning , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
2023-12-01 09:37:20 -05:00
EventPtr - > UpdatePinValues ( ) ;
if ( EventPtr - > HasOrphanedPin ( ) )
{
2024-03-26 02:39:40 -04:00
AddMessageForEvent ( EventPtr , LOCTEXT ( " InvalidEventGraphOrphaned " , " The event has an orphaned pin. " ) , Compiler : : EMessageType : : Warning , FMVVMBlueprintPinId ( ) ) ;
2023-12-01 09:37:20 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
2023-11-17 14:09:15 -05:00
Event - > Type = FCompilerEvent : : EType : : Valid ;
}
}
void FMVVMViewBlueprintCompiler : : CreateWriteFieldContexts ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
{
2023-06-16 11:26:16 -04:00
// Use the Skeleton class. The class bind and not all functions are generated yet
UWidgetBlueprintGeneratedClass * NewSkeletonClass = Cast < UWidgetBlueprintGeneratedClass > ( WidgetBlueprintCompilerContext . Blueprint - > SkeletonGeneratedClass ) ;
if ( NewSkeletonClass = = nullptr )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " InvalidNewClass " , " Internal error. The skeleton class is not valid. " ) . ToString ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
2023-06-16 11:26:16 -04:00
return ;
}
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
2023-06-16 11:26:16 -04:00
{
2023-11-17 14:09:15 -05:00
const FMVVMBlueprintViewBinding & Binding = * BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ;
2023-11-16 20:11:58 -05:00
2023-11-17 14:09:15 -05:00
if ( ValidBinding - > Type = = FCompilerBinding : : EType : : Assignment
| | ValidBinding - > Type = = FCompilerBinding : : EType : : SimpleConversionFunction
| | ValidBinding - > Type = = FCompilerBinding : : EType : : ComplexConversionFunction )
2023-11-16 20:11:58 -05:00
{
2023-11-17 14:09:15 -05:00
const FMVVMBlueprintPropertyPath & DestinationPropertyPath = ValidBinding - > Key . bIsForwardBinding ? Binding . DestinationPath : Binding . SourcePath ;
TValueOrError < FCreateFieldsResult , FText > FieldContextResult = CreateFieldContext ( NewSkeletonClass , DestinationPropertyPath , false ) ;
if ( FieldContextResult . HasError ( ) )
2023-11-16 20:11:58 -05:00
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , FieldContextResult . StealError ( ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
// Test if it already exist
2024-03-25 19:44:50 -04:00
TSharedPtr < FGeneratedWriteFieldPathContext > WriteFieldPath ;
2023-11-17 14:09:15 -05:00
{
2024-03-25 19:44:50 -04:00
const TArray < UE : : MVVM : : FMVVMConstFieldVariant > & SkeletalGeneratedFieldsResult = FieldContextResult . GetValue ( ) . SkeletalGeneratedFields ;
2023-11-17 14:09:15 -05:00
TSharedRef < FGeneratedWriteFieldPathContext > * Found = GeneratedWriteFieldPaths . FindByPredicate ( [ & SkeletalGeneratedFieldsResult ] ( const TSharedRef < FGeneratedWriteFieldPathContext > & Other )
{
return Other - > SkeletalGeneratedFields = = SkeletalGeneratedFieldsResult ;
} ) ;
if ( Found )
{
2024-03-25 19:44:50 -04:00
WriteFieldPath = * Found ;
// Backward binding are reactive and are not trigger on initialization.
//It is valid for a backward binding to reuse the same write field path.
if ( ValidBinding - > Key . bIsForwardBinding )
{
AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " PropertyPathAlreadyUsed " , " The property path '{0}' is already used by another binding. " ) , PropertyPathToText ( NewSkeletonClass , BlueprintView . Get ( ) , DestinationPropertyPath ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2024-03-25 19:44:50 -04:00
, FMVVMBlueprintPinId ( )
) ;
}
}
else
{
WriteFieldPath = MakeWriteFieldPath (
DestinationPropertyPath . GetSource ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) )
, MoveTemp ( FieldContextResult . GetValue ( ) . GeneratedFields )
, MoveTemp ( FieldContextResult . GetValue ( ) . SkeletalGeneratedFields ) ) ;
GeneratedWriteFieldPaths . Add ( WriteFieldPath . ToSharedRef ( ) ) ;
2023-11-17 14:09:15 -05:00
}
}
WriteFieldPath - > UsedByBindings . AddUnique ( ValidBinding ) ;
WriteFieldPath - > bUseByNativeBinding = true ; // the setter function can be in BP but the destination will be set in native
// Assign the Destination to the binding
ValidBinding - > WritePath = WriteFieldPath ;
}
}
//The destination for event must also exist (for the BP to compile).
for ( TSharedRef < FCompilerEvent > & ValidEvent : ValidEvents )
{
UMVVMBlueprintViewEvent * EventPtr = ValidEvent - > Event . Get ( ) ;
check ( EventPtr ) ;
if ( ValidEvent - > Type = = FCompilerEvent : : EType : : Valid )
{
TValueOrError < FCreateFieldsResult , FText > FieldContextResult = CreateFieldContext ( NewSkeletonClass , EventPtr - > GetDestinationPath ( ) , false ) ;
if ( FieldContextResult . HasError ( ) )
{
AddMessageForEvent ( EventPtr
, FText : : Format ( Private : : PropertyPathIsInvalidFormat , PropertyPathToText ( NewSkeletonClass , BlueprintView . Get ( ) , EventPtr - > GetDestinationPath ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-06-16 11:26:16 -04:00
) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
2023-06-16 11:26:16 -04:00
}
2023-11-17 14:09:15 -05:00
// Test if it already exist
2024-03-25 19:44:50 -04:00
TSharedPtr < FGeneratedWriteFieldPathContext > WriteFieldPath ;
2023-06-16 11:26:16 -04:00
{
2024-03-25 19:44:50 -04:00
const TArray < UE : : MVVM : : FMVVMConstFieldVariant > & SkeletalGeneratedFieldsResult = FieldContextResult . GetValue ( ) . SkeletalGeneratedFields ;
2023-11-17 14:09:15 -05:00
TSharedRef < FGeneratedWriteFieldPathContext > * Found = GeneratedWriteFieldPaths . FindByPredicate ( [ & SkeletalGeneratedFieldsResult ] ( const TSharedRef < FGeneratedWriteFieldPathContext > & Other ) { return Other - > SkeletalGeneratedFields = = SkeletalGeneratedFieldsResult ; } ) ;
if ( Found )
{
2024-03-25 19:44:50 -04:00
WriteFieldPath = ( * Found ) ;
2023-11-17 14:09:15 -05:00
}
else
{
2024-03-25 19:44:50 -04:00
WriteFieldPath = MakeWriteFieldPath (
EventPtr - > GetDestinationPath ( ) . GetSource ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) )
, MoveTemp ( FieldContextResult . GetValue ( ) . GeneratedFields )
, MoveTemp ( FieldContextResult . GetValue ( ) . SkeletalGeneratedFields ) ) ;
GeneratedWriteFieldPaths . Add ( WriteFieldPath . ToSharedRef ( ) ) ;
2023-11-17 14:09:15 -05:00
}
2023-06-16 11:26:16 -04:00
}
2024-03-25 19:44:50 -04:00
WriteFieldPath - > UsedByEvents . AddUnique ( ValidEvent ) ;
ValidEvent - > WritePath = WriteFieldPath ;
2023-06-16 11:26:16 -04:00
}
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateViewModelSetters ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
if ( GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter )
{
for ( FCompilerViewModelSetter & Setter : ViewModelSettersToGenerate )
{
if ( ensure ( Setter . SetterGraph ! = nullptr ) )
{
if ( ! UE : : MVVM : : FunctionGraphHelper : : GenerateViewModelSetter ( WidgetBlueprintCompilerContext , Setter . SetterGraph , Setter . PropertyName ) )
{
2024-03-26 02:39:40 -04:00
AddMessageForViewModel ( Setter . DisplayName , LOCTEXT ( " SetterFunctionCouldNotBeGenerated " , " The setter function could not be generated. " ) , Compiler : : EMessageType : : Warning ) ;
2023-11-17 14:09:15 -05:00
bIsCreateFunctionsStepValid = false ;
continue ;
}
}
}
}
}
void FMVVMViewBlueprintCompiler : : CreateIntermediateGraphFunctions ( const FWidgetBlueprintCompilerContext : : FCreateFunctionContext & Context )
{
// Add function to set destination if needed
for ( TSharedRef < FGeneratedWriteFieldPathContext > & GeneratedDestination : GeneratedWriteFieldPaths )
{
// If the destination can't be set in cpp, we need to generate a BP function to set the value.
if ( ! GeneratedDestination - > bCanBeSetInNative & & GeneratedDestination - > bUseByNativeBinding )
{
auto AddErrorMessage = [ Self = this , & GeneratedDestination ] ( const FText & ErrorMsg )
{
2024-03-26 02:39:40 -04:00
Self - > AddMessages ( GeneratedDestination - > UsedByBindings , GeneratedDestination - > UsedByEvents , ErrorMsg , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
} ;
const FProperty * SetterProperty = nullptr ;
if ( ensure ( GeneratedDestination - > SkeletalGeneratedFields . Num ( ) > 0 & & GeneratedDestination - > SkeletalGeneratedFields . Last ( ) . IsProperty ( ) ) )
{
SetterProperty = GeneratedDestination - > SkeletalGeneratedFields . Last ( ) . GetProperty ( ) ;
}
if ( SetterProperty = = nullptr )
{
AddErrorMessage ( FText : : Format ( LOCTEXT ( " CantGetSetter " , " Internal Error. The setter function was not created. {0} " ) , : : UE : : MVVM : : FieldPathHelper : : ToText ( GeneratedDestination - > GeneratedFields ) ) ) ;
bIsCreateFunctionsStepValid = false ;
continue ;
}
// create a setter function to be called from native. For now we follow the convention of Setter(Conversion(Getter))
UEdGraph * GeneratedSetterGraph = UE : : MVVM : : FunctionGraphHelper : : CreateIntermediateFunctionGraph ( WidgetBlueprintCompilerContext , FString : : Printf ( TEXT ( " __Setter_%s " ) , * SetterProperty - > GetName ( ) ) , EFunctionFlags : : FUNC_None , TEXT ( " AutogeneratedSetter " ) , false ) ;
if ( GeneratedSetterGraph = = nullptr )
{
AddErrorMessage ( FText : : Format ( LOCTEXT ( " CantCreateSetter " , " Internal Error. The setter function was not created. {0} " ) , : : UE : : MVVM : : FieldPathHelper : : ToText ( GeneratedDestination - > GeneratedFields ) ) ) ;
bIsCreateFunctionsStepValid = false ;
continue ;
}
UE : : MVVM : : FunctionGraphHelper : : AddFunctionArgument ( GeneratedSetterGraph , SetterProperty , " NewValue " ) ;
// set GeneratedSetterFunction. Use the SkeletalSetterPath here to use the setter will be generated when the function is generated.
if ( ! UE : : MVVM : : FunctionGraphHelper : : GenerateIntermediateSetter ( WidgetBlueprintCompilerContext , GeneratedSetterGraph , GeneratedDestination - > SkeletalGeneratedFields ) )
{
AddErrorMessage ( FText : : Format ( LOCTEXT ( " CantGeneratedSetter " , " Internal Error. The setter function was not generated. {0} " ) , : : UE : : MVVM : : FieldPathHelper : : ToText ( GeneratedDestination - > GeneratedFields ) ) ) ;
bIsCreateFunctionsStepValid = false ;
continue ;
}
// the new path can only be set later, once the function is compiled.
GeneratedDestination - > GeneratedFunctionName = GeneratedSetterGraph - > GetFName ( ) ;
}
}
// Add Generated Conversion functions to the blueprint
for ( const TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
{
FMVVMBlueprintViewBinding & Binding = * BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ;
if ( ValidBinding - > Type = = FCompilerBinding : : EType : : ComplexConversionFunction )
{
UMVVMBlueprintViewConversionFunction * ConversionFunction = Binding . Conversion . GetConversionFunction ( ValidBinding - > Key . bIsForwardBinding ) ;
check ( ConversionFunction ) ;
UEdGraph * WrapperGraph = ConversionFunction - > GetOrCreateIntermediateWrapperGraph ( WidgetBlueprintCompilerContext ) ;
if ( ensure ( WrapperGraph ) & & ConversionFunction - > IsWrapperGraphTransient ( ) )
{
bool bAlreadyContained = WidgetBlueprintCompilerContext . Blueprint - > FunctionGraphs . Contains ( WrapperGraph ) ;
if ( ensure ( ! bAlreadyContained ) )
{
Context . AddGeneratedFunctionGraph ( WrapperGraph ) ;
}
}
}
}
// Add Generated event to the blueprint
for ( TSharedRef < FCompilerEvent > & Event : ValidEvents )
{
UMVVMBlueprintViewEvent * EventPtr = Event - > Event . Get ( ) ;
if ( Event - > Type = = FCompilerEvent : : EType : : Valid )
{
UEdGraph * WrapperGraph = EventPtr - > GetOrCreateWrapperGraph ( ) ;
ensure ( WrapperGraph ) ;
bool bAlreadyContained = WidgetBlueprintCompilerContext . Blueprint - > FunctionGraphs . Contains ( WrapperGraph ) ;
if ( ensure ( ! bAlreadyContained ) )
{
Context . AddGeneratedFunctionGraph ( WrapperGraph ) ;
}
}
}
}
bool FMVVMViewBlueprintCompiler : : PreCompile ( UWidgetBlueprintGeneratedClass * Class )
{
if ( ! AreStepsValid ( ) )
2022-06-08 13:25:42 -04:00
{
return false ;
}
2023-11-17 14:09:15 -05:00
FixCompilerBindingSelfSource ( Class ) ;
AddWarningForPropertyWithMVVMAndLegacyBinding ( Class ) ;
2022-07-13 13:52:41 -04:00
2023-11-17 14:09:15 -05:00
GeneratedReadFieldPaths . Reset ( ) ;
CreateReadFieldContexts ( Class ) ;
CreateCreatorContentFromBindingSource ( Class ) ;
2022-06-08 13:25:42 -04:00
2024-02-15 15:40:59 -05:00
// NB. The dynamic sources are created.
FixFieldPathContext ( Class ) ;
2023-11-17 14:09:15 -05:00
if ( ! AreStepsValid ( ) )
{
return false ;
}
PreCompileViewModelCreatorContexts ( Class ) ;
PreCompileBindings ( Class ) ;
PreCompileEvents ( Class ) ;
2024-03-26 02:39:40 -04:00
PreCompileViewExtensions ( Class ) ;
2024-02-15 15:40:59 -05:00
PreCompileSourceDependencies ( Class ) ;
2023-11-17 14:09:15 -05:00
return AreStepsValid ( ) ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
bool FMVVMViewBlueprintCompiler : : Compile ( UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
if ( ! AreStepsValid ( ) )
2022-06-08 13:25:42 -04:00
{
return false ;
}
2023-08-24 17:34:17 -04:00
TValueOrError < FCompiledBindingLibraryCompiler : : FCompileResult , FText > CompileResult = BindingLibraryCompiler . Compile ( BlueprintView - > GetCompiledBindingLibraryId ( ) ) ;
2022-06-08 13:25:42 -04:00
if ( CompileResult . HasError ( ) )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " BindingCompilationFailed " , " The binding compilation failed. {1} " ) , CompileResult . GetError ( ) ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
return false ;
}
2023-12-01 11:13:35 -05:00
CompileSources ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2023-11-17 14:09:15 -05:00
CompileBindings ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2023-12-01 11:13:35 -05:00
CompileEvaluateSources ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2023-11-17 14:09:15 -05:00
CompileEvents ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2024-03-26 02:39:40 -04:00
CompileViewExtensions ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2023-12-01 11:13:35 -05:00
SortSourceFields ( CompileResult . GetValue ( ) , Class , ViewExtension ) ;
2022-06-08 13:25:42 -04:00
2023-07-31 14:43:55 -04:00
{
ViewExtension - > bInitializeSourcesOnConstruct = BlueprintView - > GetSettings ( ) - > bInitializeSourcesOnConstruct ;
ViewExtension - > bInitializeBindingsOnConstruct = ViewExtension - > bInitializeSourcesOnConstruct ? BlueprintView - > GetSettings ( ) - > bInitializeBindingsOnConstruct : false ;
2023-12-01 11:13:35 -05:00
ViewExtension - > bInitializeEventsOnConstruct = BlueprintView - > GetSettings ( ) - > bInitializeEventsOnConstruct ;
}
{
ViewExtension - > OptionalSources = 0 ;
for ( int32 Index = 0 ; Index < ViewExtension - > Sources . Num ( ) ; + + Index )
{
const FMVVMViewClass_Source & Source = ViewExtension - > Sources [ Index ] ;
if ( Source . IsOptional ( ) )
{
FMVVMViewClass_SourceKey ClassSourceKey = FMVVMViewClass_SourceKey ( Index ) ;
ViewExtension - > OptionalSources | = ClassSourceKey . GetBit ( ) ;
}
}
2023-07-31 14:43:55 -04:00
}
2023-11-17 14:09:15 -05:00
bool bResult = AreStepsValid ( ) ;
2022-06-08 13:25:42 -04:00
if ( bResult )
{
ViewExtension - > BindingLibrary = MoveTemp ( CompileResult . GetValue ( ) . Library ) ;
2023-03-16 13:02:33 -04:00
2023-03-22 13:07:37 -04:00
# if UE_WITH_MVVM_DEBUGGING
2023-11-17 14:09:15 -05:00
if ( CVarLogViewCompiledResult - > GetBool ( ) )
2023-03-16 13:02:33 -04:00
{
2023-12-01 11:13:35 -05:00
UMVVMViewClass : : FToStringArgs ToStringArgs = UMVVMViewClass : : FToStringArgs : : All ( ) ;
ToStringArgs . Source . bUseDisplayName = false ;
ToStringArgs . Binding . bUseDisplayName = false ;
ToStringArgs . Evaluate . bUseDisplayName = false ;
ToStringArgs . Event . bUseDisplayName = false ;
UE_LOG ( LogMVVM , Log , TEXT ( " %s " ) , * ViewExtension - > ToString ( ToStringArgs ) ) ;
2023-03-16 13:02:33 -04:00
}
2023-03-22 13:07:37 -04:00
# endif
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
return AreStepsValid ( ) ;
2022-06-08 13:25:42 -04:00
}
2024-02-15 15:40:59 -05:00
void FMVVMViewBlueprintCompiler : : FixFieldPathContext ( UWidgetBlueprintGeneratedClass * Class )
2022-07-13 13:52:41 -04:00
{
2024-02-15 15:40:59 -05:00
TSharedRef < FCompilerBindingSource > * SelfSourcePtr = NeededBindingSources . FindByPredicate ( [ ] ( const TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Type = = FCompilerBindingSource : : EType : : Self ;
} ) ;
auto FindProperty = [ ] ( FMVVMConstFieldVariant Field ) - > const FProperty *
{
if ( Field . IsProperty ( ) )
{
return Field . GetProperty ( ) ;
}
else if ( Field . IsFunction ( ) & & Field . GetFunction ( ) )
{
return BindingHelper : : GetReturnProperty ( Field . GetFunction ( ) ) ;
}
return nullptr ;
} ;
auto FindDynamicCreatorContext = [ Self = this , & FindProperty ] ( TArray < UE : : MVVM : : FMVVMConstFieldVariant > & GeneratedFields , int32 Index , TSharedPtr < FCompilerBindingSource > & InOutSource ) - > bool
{
bool bBreak = true ;
UE : : MVVM : : FMVVMConstFieldVariant NextField = GeneratedFields [ Index ] ;
if ( const FObjectProperty * FieldObjectProperty = CastField < FObjectProperty > ( FindProperty ( NextField ) ) )
{
const UClass * NextFieldObjectPtrClass = FieldObjectProperty - > PropertyClass ;
for ( const TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > & DynamicCreatorContext : Self - > SourceViewModelDynamicCreatorContexts )
{
if ( DynamicCreatorContext - > ParentSource = = InOutSource
& & DynamicCreatorContext - > NotificationId . GetFieldName ( ) = = NextField . GetName ( )
& & DynamicCreatorContext - > Source - > AuthoritativeClass = = NextFieldObjectPtrClass )
{
InOutSource = DynamicCreatorContext - > Source ;
bBreak = false ;
break ;
}
}
}
return bBreak ;
} ;
//// Sanity check. Set Source to FGeneratedReadFieldPathContext
//{
// for (TSharedRef<FGeneratedReadFieldPathContext>& GeneratedRead : GeneratedReadFieldPaths)
// {
// // ReadFieldPath should already be valid. Only confirm it here.
// TSharedPtr<FCompilerBindingSource> NewSource = SelfSourcePtr ? *SelfSourcePtr : TSharedPtr<FCompilerBindingSource>();
// if (GeneratedRead->GeneratedFields.Num() > 0)
// {
// UE::MVVM::FMVVMConstFieldVariant NextField = GeneratedRead->GeneratedFields[0];
// if (const FObjectProperty* FieldObjectProperty = CastField<FObjectProperty>(FindProperty(NextField)))
// {
// UClass* NextFieldObjectPtrClass = FieldObjectProperty->PropertyClass;
// TSharedRef<FCompilerBindingSource>* BindingSource = NeededBindingSources.FindByPredicate(
// [NextField, NextFieldObjectPtrClass](TSharedRef<FCompilerBindingSource>& Other)
// {
// return Other->Name == NextField.GetName()
// && NextFieldObjectPtrClass == Other->AuthoritativeClass;
// }
// );
// if (BindingSource)
// {
// NewSource = *BindingSource;
// for (int32 GeneratedFieldIndex = 1; GeneratedFieldIndex < GeneratedRead->GeneratedFields.Num() - 1; ++GeneratedFieldIndex)
// {
// if (FindDynamicCreatorContext(GeneratedRead->GeneratedFields, GeneratedFieldIndex, NewSource))
// {
// break;
// }
// }
// }
// }
// }
// if (GeneratedRead->Source != NewSource)
// {
// // We only do this test as a sanity check and because it's the same algo for FGeneratedWriteFieldPathContext
// AddMessage(FText::Format(LOCTEXT("InternalErrorNotSameSource", "Internal error. The read path {0} source do not matches with the previous calculated source."), ::UE::MVVM::FieldPathHelper::ToText(GeneratedRead->GeneratedFields))
// , EMessageType::Info
// );
// }
// }
//}
// Set OptionalSource and OptionalDependencySource to FGeneratedWriteFieldPathContext
for ( TSharedRef < FGeneratedWriteFieldPathContext > & GeneratedDestination : GeneratedWriteFieldPaths )
{
//ViewmodelA.ViewmodelB = ViewmodelC.Value -> ViewmodelA needs to init before ViewmodelA_ViewmodelB, ViewmodelA before ViewmodelC, ViewmodelC before ViewmodelA_ViewmodelB
// OptionSource = ViewmodelA
// OptionalDependencySource = ViewmodelA_ViewmodelB. We do not have the info for ViewmodelC (only in the binding) and there could be more than one read path. Set the flag and add the dependency later.
//ViewmodelA.ViewmodelB.Value = ViewmodelC.Value -> ViewmodelA needs to init before ViewmodelA_ViewmodelB, ViewmodelA before ViewmodelC, ViewmodelA_ViewmodelB before ViewmodelC
// OptionSource = ViewmodelA_ViewmodelB
// OptionalDependencySource = null. We set a value, not a source.
if ( GeneratedDestination - > GeneratedFields . Num ( ) > 0 )
{
UE : : MVVM : : FMVVMConstFieldVariant NextField = GeneratedDestination - > GeneratedFields [ 0 ] ;
if ( const FObjectProperty * FieldObjectProperty = CastField < FObjectProperty > ( FindProperty ( NextField ) ) )
{
const UClass * NextFieldObjectPtrClass = FieldObjectProperty - > PropertyClass ;
TSharedRef < FCompilerBindingSource > * BindingSource = NeededBindingSources . FindByPredicate (
[ NextField , NextFieldObjectPtrClass ] ( TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Name = = NextField . GetName ( )
& & NextFieldObjectPtrClass = = Other - > AuthoritativeClass ;
}
) ;
if ( BindingSource )
{
if ( GeneratedDestination - > GeneratedFields . Num ( ) > 1 )
{
GeneratedDestination - > OptionalSource = * BindingSource ;
int32 GeneratedFieldIndex = 1 ;
for ( ; GeneratedFieldIndex < GeneratedDestination - > GeneratedFields . Num ( ) - 1 ; + + GeneratedFieldIndex )
{
if ( FindDynamicCreatorContext ( GeneratedDestination - > GeneratedFields , GeneratedFieldIndex , GeneratedDestination - > OptionalSource ) )
{
break ;
}
}
if ( GeneratedFieldIndex = = GeneratedDestination - > GeneratedFields . Num ( ) - 1 )
{
FindDynamicCreatorContext ( GeneratedDestination - > GeneratedFields , GeneratedFieldIndex , GeneratedDestination - > OptionalDependencySource ) ;
}
}
else
{
GeneratedDestination - > OptionalSource = SelfSourcePtr ? * SelfSourcePtr : TSharedPtr < FCompilerBindingSource > ( ) ;
GeneratedDestination - > OptionalDependencySource = * BindingSource ;
}
}
}
}
}
// If a graph was generated for the FGeneratedWriteFieldPathContext, then use it instead for the SkeletalGeneratedFields
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FGeneratedWriteFieldPathContext > & GeneratedDestination : GeneratedWriteFieldPaths )
2022-07-13 13:52:41 -04:00
{
2023-11-17 14:09:15 -05:00
if ( ! GeneratedDestination - > GeneratedFunctionName . IsNone ( ) )
2022-07-13 13:52:41 -04:00
{
2023-11-17 14:09:15 -05:00
UFunction * GeneratedFunction = Class - > FindFunctionByName ( GeneratedDestination - > GeneratedFunctionName ) ;
if ( GeneratedFunction = = nullptr )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " CantFindGeneratedBindingSetterFunction " , " Internal Error. The setter function was not generated. " ) . ToString ( ) ) ;
bIsPreCompileStepValid = false ;
continue ;
}
GeneratedDestination - > GeneratedFields . Reset ( ) ;
GeneratedDestination - > GeneratedFields . Add ( UE : : MVVM : : FMVVMConstFieldVariant ( GeneratedFunction ) ) ;
// note the function is not added on the skeletal class
GeneratedDestination - > SkeletalGeneratedFields . Reset ( ) ;
GeneratedDestination - > SkeletalGeneratedFields . Add ( UE : : MVVM : : FMVVMConstFieldVariant ( GeneratedFunction ) ) ;
2022-07-13 13:52:41 -04:00
}
2023-11-17 14:09:15 -05:00
}
}
2022-07-13 13:52:41 -04:00
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateReadFieldContexts ( UWidgetBlueprintGeneratedClass * Class )
{
auto AlreadyExist = [ Self = this ] ( const TArray < UE : : MVVM : : FMVVMConstFieldVariant > & SkeletalGeneratedFields ) - > TSharedPtr < FGeneratedReadFieldPathContext >
2022-07-13 13:52:41 -04:00
{
2023-11-17 14:09:15 -05:00
// Test if it already exist
TSharedRef < FGeneratedReadFieldPathContext > * Found = Self - > GeneratedReadFieldPaths . FindByPredicate ( [ & SkeletalGeneratedFields ] ( const TSharedRef < FGeneratedReadFieldPathContext > & Other ) { return Other - > SkeletalGeneratedFields = = SkeletalGeneratedFields ; } ) ;
return Found ! = nullptr ? * Found : TSharedPtr < FGeneratedReadFieldPathContext > ( ) ;
2023-11-16 20:11:58 -05:00
} ;
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
{
const FMVVMBlueprintViewBinding & Binding = * BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ;
2023-11-16 23:04:14 -05:00
2023-12-19 11:31:33 -05:00
auto CreateBindingSourceContext = [ Self = this , Class , & ValidBinding , & Binding ] ( const FMVVMBlueprintPropertyPath & PropertyPath , const FMVVMBlueprintPinId & PinId ) - > TValueOrError < FCreateFieldsResult , void >
2023-11-17 14:09:15 -05:00
{
TValueOrError < FCreateFieldsResult , FText > FieldContextResult = Self - > CreateFieldContext ( Class , PropertyPath , true ) ;
if ( FieldContextResult . HasError ( ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( Private : : PropertyPathIsInvalidFormat , PropertyPathToText ( Class , Self - > BlueprintView . Get ( ) , PropertyPath ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, PinId
2023-11-17 14:09:15 -05:00
) ;
Self - > bIsPreCompileStepValid = false ;
return MakeError ( ) ;
}
return MakeValue ( FieldContextResult . StealValue ( ) ) ;
} ;
2023-12-19 11:31:33 -05:00
auto CreateFieldId = [ Self = this , Class , & Binding , bIsOneTimeBinding = ValidBinding - > bIsOneTimeBinding ] ( const FMVVMBlueprintPropertyPath & PropertyPath , TSharedPtr < FGeneratedReadFieldPathContext > & ReadFieldContext , const FMVVMBlueprintPinId & PinId ) - > TValueOrError < void , void >
2023-11-17 14:09:15 -05:00
{
if ( ! ReadFieldContext - > NotificationField . IsValid ( ) )
{
TValueOrError < TSharedPtr < FCompilerNotifyFieldId > , FText > CreateFieldResult = Self - > CreateNotifyFieldId ( Class , ReadFieldContext , Binding ) ;
if ( CreateFieldResult . HasError ( ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " CreateNotifyFieldIdFailedInvalidSelfContext " , " The property path '{0}' is invalid. {1} " ) , PropertyPathToText ( Class , Self - > BlueprintView . Get ( ) , PropertyPath ) , CreateFieldResult . StealError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, PinId
2023-11-17 14:09:15 -05:00
) ;
return MakeError ( ) ;
}
if ( CreateFieldResult . GetValue ( ) )
{
// Sanity check
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
// if there is a FieldId associated with the read property
if ( CreateFieldResult . GetValue ( ) - > Source )
2023-07-26 14:04:59 -04:00
{
2023-11-17 14:09:15 -05:00
EMVVMBlueprintFieldPathSource PathSource = PropertyPath . GetSource ( Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ;
bool bValidViewModel = CreateFieldResult . GetValue ( ) - > Source - > Type = = FCompilerBindingSource : : EType : : ViewModel & & PathSource = = EMVVMBlueprintFieldPathSource : : ViewModel ;
bool bValidWidget = CreateFieldResult . GetValue ( ) - > Source - > Type = = FCompilerBindingSource : : EType : : Widget & & PathSource = = EMVVMBlueprintFieldPathSource : : Widget ;
bool bDynamic = CreateFieldResult . GetValue ( ) - > Source - > Type = = FCompilerBindingSource : : EType : : DynamicViewmodel ;
bool bSelf = CreateFieldResult . GetValue ( ) - > Source - > Type = = FCompilerBindingSource : : EType : : Self & & PathSource = = EMVVMBlueprintFieldPathSource : : SelfContext ;
if ( ! ( bValidViewModel | | bValidWidget | | bDynamic | | bSelf ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " CreateNotifyFieldIdFailedInvalidInvalidContext " , " Internal error. The property path '{0}' is invalid. The context is invalid. " ) , PropertyPathToText ( Class , Self - > BlueprintView . Get ( ) , PropertyPath ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, PinId
2023-11-16 23:04:14 -05:00
) ;
2023-11-17 14:09:15 -05:00
return MakeError ( ) ;
2023-11-16 23:04:14 -05:00
}
2023-07-26 14:04:59 -04:00
}
}
2023-11-16 20:11:58 -05:00
2023-11-17 14:09:15 -05:00
ReadFieldContext - > NotificationField = CreateFieldResult . GetValue ( ) ;
2024-02-15 15:40:59 -05:00
ReadFieldContext - > Source = ReadFieldContext - > NotificationField - > Source ;
2023-11-16 23:04:14 -05:00
}
2022-10-06 20:08:12 -04:00
}
2023-11-17 14:09:15 -05:00
return MakeValue ( ) ;
2022-10-06 20:08:12 -04:00
} ;
2023-11-17 14:09:15 -05:00
if ( ValidBinding - > Type = = FCompilerBinding : : EType : : Assignment )
2023-03-17 16:33:42 -04:00
{
2023-11-17 14:09:15 -05:00
const FMVVMBlueprintPropertyPath & BindingSourcePath = ValidBinding - > Key . bIsForwardBinding ? Binding . SourcePath : Binding . DestinationPath ;
2023-12-19 11:31:33 -05:00
TValueOrError < FCreateFieldsResult , void > CreateSourceResult = CreateBindingSourceContext ( BindingSourcePath , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
if ( CreateSourceResult . HasError ( ) )
2023-03-17 16:33:42 -04:00
{
2023-11-16 23:04:14 -05:00
continue ;
}
2023-11-17 14:09:15 -05:00
TSharedPtr < FGeneratedReadFieldPathContext > Found = AlreadyExist ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
if ( ! Found )
{
Found = MakeShared < FGeneratedReadFieldPathContext > ( ) ;
2024-02-15 15:40:59 -05:00
Found - > Source = MoveTemp ( CreateSourceResult . GetValue ( ) . OptionalSource ) ;
2023-11-17 14:09:15 -05:00
Found - > GeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . GeneratedFields ) ;
Found - > SkeletalGeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
2023-12-19 11:31:33 -05:00
if ( CreateFieldId ( BindingSourcePath , Found , FMVVMBlueprintPinId ( ) ) . HasError ( ) )
2023-11-17 14:09:15 -05:00
{
bIsPreCompileStepValid = false ;
continue ;
}
GeneratedReadFieldPaths . Add ( Found . ToSharedRef ( ) ) ;
}
Found - > UsedByBindings . AddUnique ( ValidBinding ) ;
ValidBinding - > ReadPaths . Add ( Found . ToSharedRef ( ) ) ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
else if ( ValidBinding - > Type = = FCompilerBinding : : EType : : SimpleConversionFunction
| | ValidBinding - > Type = = FCompilerBinding : : EType : : ComplexConversionFunction )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
UMVVMBlueprintViewConversionFunction * ConversionFunction = Binding . Conversion . GetConversionFunction ( ValidBinding - > Key . bIsForwardBinding ) ;
if ( ensure ( ConversionFunction ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
for ( const FMVVMBlueprintPin & Pin : ConversionFunction - > GetPins ( ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
if ( Pin . UsedPathAsValue ( ) )
2023-11-16 23:04:14 -05:00
{
2023-12-19 11:31:33 -05:00
TValueOrError < FCreateFieldsResult , void > CreateSourceResult = CreateBindingSourceContext ( Pin . GetPath ( ) , Pin . GetId ( ) ) ;
2023-11-17 14:09:15 -05:00
if ( CreateSourceResult . HasError ( ) )
{
continue ;
}
TSharedPtr < FGeneratedReadFieldPathContext > Found = AlreadyExist ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
if ( ! Found )
{
Found = MakeShared < FGeneratedReadFieldPathContext > ( ) ;
2024-02-15 15:40:59 -05:00
Found - > Source = MoveTemp ( CreateSourceResult . GetValue ( ) . OptionalSource ) ;
2023-11-17 14:09:15 -05:00
Found - > GeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . GeneratedFields ) ;
Found - > SkeletalGeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
2023-12-19 11:31:33 -05:00
if ( CreateFieldId ( Pin . GetPath ( ) , Found , Pin . GetId ( ) ) . HasError ( ) )
2023-11-17 14:09:15 -05:00
{
bIsPreCompileStepValid = false ;
continue ;
}
GeneratedReadFieldPaths . Add ( Found . ToSharedRef ( ) ) ;
}
Found - > UsedByBindings . AddUnique ( TWeakPtr < FCompilerBinding > ( ValidBinding ) ) ;
ValidBinding - > ReadPaths . Add ( Found . ToSharedRef ( ) ) ;
2023-11-16 23:04:14 -05:00
}
}
}
}
}
2023-11-17 14:09:15 -05:00
//The pins for event
for ( TSharedRef < FCompilerEvent > & ValidEvent : ValidEvents )
{
UMVVMBlueprintViewEvent * EventPtr = ValidEvent - > Event . Get ( ) ;
check ( EventPtr ) ;
if ( ValidEvent - > Type = = FCompilerEvent : : EType : : Valid )
{
for ( const FMVVMBlueprintPin & Pin : EventPtr - > GetPins ( ) )
{
if ( Pin . UsedPathAsValue ( ) )
{
TValueOrError < FCreateFieldsResult , FText > CreateSourceResult = CreateFieldContext ( Class , Pin . GetPath ( ) , true ) ;
if ( CreateSourceResult . HasError ( ) )
{
AddMessageForEvent ( ValidEvent
, FText : : Format ( Private : : PropertyPathIsInvalidFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , Pin . GetPath ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, Pin . GetId ( )
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
}
TSharedPtr < FGeneratedReadFieldPathContext > Found = AlreadyExist ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
if ( ! Found )
{
Found = MakeShared < FGeneratedReadFieldPathContext > ( ) ;
2024-02-15 15:40:59 -05:00
Found - > Source = MoveTemp ( CreateSourceResult . GetValue ( ) . OptionalSource ) ;
2023-11-17 14:09:15 -05:00
Found - > GeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . GeneratedFields ) ;
Found - > SkeletalGeneratedFields = MoveTemp ( CreateSourceResult . GetValue ( ) . SkeletalGeneratedFields ) ;
GeneratedReadFieldPaths . Add ( Found . ToSharedRef ( ) ) ;
}
Found - > UsedByEvents . AddUnique ( ValidEvent ) ;
ValidEvent - > ReadPaths . Add ( Found . ToSharedRef ( ) ) ;
}
}
}
}
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CreateCreatorContentFromBindingSource ( UWidgetBlueprintGeneratedClass * Class )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
// Add all the needed sources that are not viewmodel (so not in the SourceCreators)
for ( const TSharedRef < FCompilerBindingSource > & Source : NeededBindingSources )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
{
const bool bSourceIsViewModel = ( Source - > Type = = FCompilerBindingSource : : EType : : ViewModel | | Source - > Type = = FCompilerBindingSource : : EType : : DynamicViewmodel ) ;
const FCompilerViewModelCreatorContext * FoundSourceCreator = ViewModelCreatorContexts . FindByPredicate ( [ Source ] ( const FCompilerViewModelCreatorContext & Other ) { return Other . Source = = Source ; } ) ;
if ( bSourceIsViewModel & & FoundSourceCreator = = nullptr )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " ViewmodelSourceNotAdded " , " Internal error. A viewmodel was not added to the compiled list. " ) . ToString ( ) ) ;
bIsPreCompileStepValid = false ;
}
}
const bool bSourceIsWidget = Source - > Type = = FCompilerBindingSource : : EType : : Widget | | Source - > Type = = FCompilerBindingSource : : EType : : Self ;
if ( bSourceIsWidget )
{
FCompilerWidgetCreatorContext & CompiledSourceCreator = WidgetCreatorContexts . AddDefaulted_GetRef ( ) ;
CompiledSourceCreator . Source = Source ;
CompiledSourceCreator . bSelfReference = Source - > Type = = FCompilerBindingSource : : EType : : Self ;
}
}
}
void FMVVMViewBlueprintCompiler : : FixCompilerBindingSelfSource ( UWidgetBlueprintGeneratedClass * Class )
{
int32 Counter = 0 ;
for ( TSharedRef < FCompilerBindingSource > & Source : NeededBindingSources )
{
if ( Source - > Type = = FCompilerBindingSource : : EType : : Self )
{
ensure ( Source - > AuthoritativeClass = = nullptr ) ;
Source - > AuthoritativeClass = Class ;
+ + Counter ;
}
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
if ( Counter > 1 )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
AddMessage ( LOCTEXT ( " MoreThanSeldContext " , " Internal error. There is more than self context. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-11-17 14:09:15 -05:00
) ;
}
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : AddWarningForPropertyWithMVVMAndLegacyBinding ( UWidgetBlueprintGeneratedClass * Class )
{
const TArray < FDelegateRuntimeBinding > & LegacyBindings = Class - > Bindings ;
for ( const TSharedRef < FGeneratedWriteFieldPathContext > & WriteFieldPath : GeneratedWriteFieldPaths )
{
FName MVVMObjectName ;
FName MVVMFieldName ;
if ( WriteFieldPath - > GeneratedFrom = = EMVVMBlueprintFieldPathSource : : SelfContext )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
MVVMObjectName = Class - > ClassGeneratedBy ? Class - > ClassGeneratedBy - > GetFName ( ) : FName ( ) ;
MVVMFieldName = WriteFieldPath - > SkeletalGeneratedFields . Num ( ) > 0 ? WriteFieldPath - > SkeletalGeneratedFields [ 0 ] . GetName ( ) : FName ( ) ;
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
for ( int32 Index = 0 ; Index < WriteFieldPath - > SkeletalGeneratedFields . Num ( ) ; + + Index )
{
const UE : : MVVM : : FMVVMConstFieldVariant & Field = WriteFieldPath - > SkeletalGeneratedFields [ Index ] ;
const FObjectPropertyBase * ObjectProperty = Field . IsProperty ( ) ? CastField < const FObjectPropertyBase > ( Field . GetProperty ( ) ) : nullptr ;
if ( ObjectProperty & & ObjectProperty - > PropertyClass - > IsChildOf ( UWidget : : StaticClass ( ) ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
MVVMObjectName = ObjectProperty - > GetFName ( ) ;
MVVMFieldName = WriteFieldPath - > SkeletalGeneratedFields . IsValidIndex ( Index + 1 ) ? WriteFieldPath - > SkeletalGeneratedFields [ Index + 1 ] . GetName ( ) : FName ( ) ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
for ( const FDelegateRuntimeBinding & LegacyBinding : LegacyBindings )
{
if ( LegacyBinding . ObjectName = = MVVMObjectName & & LegacyBinding . PropertyName = = MVVMFieldName )
{
if ( WriteFieldPath - > UsedByBindings . Num ( ) )
{
AddMessages ( WriteFieldPath - > UsedByBindings
, TArrayView < TWeakPtr < FCompilerEvent > > ( )
, LOCTEXT ( " BindingConflictWithLegacy " , " The binding is set on a property with legacy binding. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-11-17 14:09:15 -05:00
) ;
}
}
}
}
}
void FMVVMViewBlueprintCompiler : : PreCompileViewModelCreatorContexts ( UWidgetBlueprintGeneratedClass * Class )
{
for ( FCompilerViewModelCreatorContext & SourceCreatorContext : ViewModelCreatorContexts )
{
const FMVVMBlueprintViewModelContext & ViewModelContext = SourceCreatorContext . ViewModelContext ;
checkf ( ViewModelContext . GetViewModelClass ( ) , TEXT ( " The viewmodel class is invalid. It was checked in CreateSourceList " ) ) ;
if ( ViewModelContext . GetViewModelClass ( ) - > HasAllClassFlags ( CLASS_Deprecated ) )
{
AddMessageForViewModel ( ViewModelContext
, FText : : Format ( LOCTEXT ( " ViewModelTypeDeprecated " , " Viewmodel class '{0}' is deprecated and should not be used. Please update it in the View Models panel. " ) , ViewModelContext . GetViewModelClass ( ) - > GetDisplayNameText ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-11-17 14:09:15 -05:00
) ;
}
if ( ! ViewModelContext . GetViewModelClass ( ) - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelInvalidInterface " , " The class doesn't implement the interface NotifyFieldValueChanged. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
// Test the creation mode. Dynamic source are always using Path.
if ( SourceCreatorContext . DynamicContext = = nullptr )
{
2023-11-16 23:04:14 -05:00
if ( ! GetAllowedContextCreationType ( ViewModelContext . GetViewModelClass ( ) ) . Contains ( ViewModelContext . CreationType ) )
{
2023-11-17 14:09:15 -05:00
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelContextCreationTypeInvalid " , " It has an invalidate creation type. You can change it in the View Models panel. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
2023-11-16 23:04:14 -05:00
continue ;
}
2023-11-16 20:11:58 -05:00
}
2023-11-17 14:09:15 -05:00
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual )
{
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance )
{
if ( ViewModelContext . GetViewModelClass ( ) - > HasAllClassFlags ( CLASS_Abstract ) )
{
AddMessageForViewModel ( ViewModelContext
, FText : : Format ( LOCTEXT ( " ViewModelTypeAbstract " , " Viewmodel class '{0}' is abstract and can't be created. You can change it in the View Models panel. " ) , ViewModelContext . GetViewModelClass ( ) - > GetDisplayNameText ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
2024-01-19 11:53:42 -05:00
UObject * CDOInstance = Class - > GetDefaultObject ( true ) ;
// If requested, create an instance of the viewmodel to be edited through the details panel
if ( ViewModelContext . bExposeInstanceInEditor )
{
FObjectPropertyBase * ViewModelProperty = FindFProperty < FObjectPropertyBase > ( Class , ViewModelContext . GetViewModelName ( ) ) ;
if ( ! ViewModelProperty - > GetObjectPropertyValue_InContainer ( CDOInstance ) )
{
UObject * StaticInstance = NewObject < UObject > ( CDOInstance , ViewModelContext . GetViewModelClass ( ) , NAME_None , RF_Transactional | RF_Public ) ;
BlueprintView - > GetOuterUMVVMWidgetBlueprintExtension_View ( ) - > TemporaryViewModelInstances . Add ( ViewModelContext . GetViewModelId ( ) , StaticInstance ) ;
if ( ensure ( StaticInstance - > GetClass ( ) - > IsChildOf ( ViewModelProperty - > PropertyClass ) ) )
{
ViewModelProperty - > SetObjectPropertyValue_InContainer ( CDOInstance , StaticInstance ) ;
}
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = AddObjectFieldPath ( BindingLibraryCompiler , Class , ViewModelContext . GetViewModelName ( ) . ToString ( ) , ViewModelContext . GetViewModelClass ( ) ) ;
if ( ReadFieldPathResult . HasError ( ) )
{
AddMessageForViewModel ( ViewModelContext
, ReadFieldPathResult . GetError ( )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2024-01-19 11:53:42 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
SourceCreatorContext . ReadPropertyPathHandle = ReadFieldPathResult . StealValue ( ) ;
}
else
{
FObjectPropertyBase * ViewModelProperty = FindFProperty < FObjectPropertyBase > ( Class , ViewModelContext . GetViewModelName ( ) ) ;
ViewModelProperty - > SetObjectPropertyValue_InContainer ( CDOInstance , nullptr ) ;
}
2023-11-17 14:09:15 -05:00
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : PropertyPath )
{
if ( ViewModelContext . ViewModelPropertyPath . IsEmpty ( ) )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelInvalidGetter " , " Viewmodel has an invalid Getter. You can select a new one in the View Models panel. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = true ;
continue ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = AddObjectFieldPath ( BindingLibraryCompiler , Class , ViewModelContext . ViewModelPropertyPath , ViewModelContext . GetViewModelClass ( ) ) ;
if ( ReadFieldPathResult . HasError ( ) )
{
AddMessageForViewModel ( ViewModelContext
, ReadFieldPathResult . GetError ( )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
SourceCreatorContext . ReadPropertyPathHandle = ReadFieldPathResult . StealValue ( ) ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : GlobalViewModelCollection )
{
if ( ViewModelContext . GlobalViewModelIdentifier . IsNone ( ) )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelInvalidGlobalIdentifier " , " Viewmodel doesn't have a valid Global identifier. You can specify a new one in the Viewmodels panel. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Resolver )
{
if ( ! ViewModelContext . Resolver )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelInvalidResolver " , " Viewmodel doesn't have a valid Resolver. You can specify a new one in the Viewmodels panel. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
}
else
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelInvalidCreationType " , " Viewmodel doesn't have a valid creation type. You can select one in the Viewmodels panel. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
2023-11-16 20:11:58 -05:00
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
for ( FCompilerWidgetCreatorContext & WidgetCreator : WidgetCreatorContexts )
{
if ( ! WidgetCreator . Source - > AuthoritativeClass - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) )
{
AddMessage ( FText : : Format ( LOCTEXT ( " WidgetInvalidInterface " , " The widget {0} class doesn't implement the interface NotifyFieldValueChanged. " ) , FText : : FromName ( WidgetCreator . Source - > Name ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
if ( ! WidgetCreator . bSelfReference )
{
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = AddObjectFieldPath ( BindingLibraryCompiler , Class , WidgetCreator . Source - > Name . ToString ( ) , WidgetCreator . Source - > AuthoritativeClass ) ;
if ( ReadFieldPathResult . HasError ( ) )
{
AddMessage ( FText : : Format ( LOCTEXT ( " WidgetAddObjectFieldPathFailFormat " , " The widget {0} creator failed. {1} " )
, FText : : FromName ( WidgetCreator . Source - > Name )
, ReadFieldPathResult . GetError ( )
)
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
WidgetCreator . ReadPropertyPathHandle = ReadFieldPathResult . StealValue ( ) ;
}
}
2022-07-13 13:52:41 -04:00
}
2023-12-01 11:13:35 -05:00
void FMVVMViewBlueprintCompiler : : CompileSources ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
2022-06-08 13:25:42 -04:00
{
2023-12-01 11:13:35 -05:00
struct FSortData
{
FSortData ( ) = default ;
2024-02-15 15:40:59 -05:00
TSharedPtr < FCompilerBindingSource > Source ;
TArray < FName , TInlineAllocator < 8 > > Errors ;
2023-12-01 11:13:35 -05:00
int32 SortIndex = - 1 ;
2024-02-15 15:40:59 -05:00
bool bCalculating = false ;
2023-12-01 11:13:35 -05:00
2024-02-15 15:40:59 -05:00
int32 CalculateSortIndex ( FName RequestedBy , TMap < FName , FSortData > & Map )
2023-12-01 11:13:35 -05:00
{
if ( SortIndex < 0 )
{
2024-02-15 15:40:59 -05:00
if ( bCalculating )
2023-12-01 11:13:35 -05:00
{
SortIndex = 0 ;
2024-02-15 15:40:59 -05:00
Errors . Add ( RequestedBy ) ;
2023-12-01 11:13:35 -05:00
}
else
{
2024-02-15 15:40:59 -05:00
bCalculating = true ;
if ( Source - > Dependencies . Num ( ) = = 0 )
{
SortIndex = 0 ;
}
else
{
for ( TWeakPtr < FCompilerBindingSource > WeakDependency : Source - > Dependencies )
{
TSharedPtr < FCompilerBindingSource > Dependency = WeakDependency . Pin ( ) ;
if ( ensure ( Dependency ) )
{
SortIndex = FMath : : Max ( Map [ Dependency - > Name ] . CalculateSortIndex ( Source - > Name , Map ) + 1 , SortIndex ) ;
}
}
}
bCalculating = false ;
2023-12-01 11:13:35 -05:00
}
}
2024-02-15 15:40:59 -05:00
return SortIndex ;
2023-12-01 11:13:35 -05:00
}
} ;
TMap < FName , FSortData > SortDatas ;
2024-02-15 15:40:59 -05:00
SortDatas . Reserve ( ViewModelCreatorContexts . Num ( ) + WidgetCreatorContexts . Num ( ) ) ;
2023-12-01 11:13:35 -05:00
TArray < FMVVMViewClass_Source > UnsortedSourceCreators ;
2024-02-15 15:40:59 -05:00
UnsortedSourceCreators . Reserve ( ViewModelCreatorContexts . Num ( ) + WidgetCreatorContexts . Num ( ) ) ;
2023-12-01 11:13:35 -05:00
2023-11-17 14:09:15 -05:00
for ( FCompilerViewModelCreatorContext & SourceCreatorContext : ViewModelCreatorContexts )
2022-06-08 13:25:42 -04:00
{
2023-03-22 13:07:37 -04:00
const FMVVMBlueprintViewModelContext & ViewModelContext = SourceCreatorContext . ViewModelContext ;
2023-12-01 11:13:35 -05:00
FMVVMViewClass_Source CompiledSourceCreator ;
2023-03-22 13:07:37 -04:00
ensure ( ViewModelContext . GetViewModelClass ( ) & & ViewModelContext . GetViewModelClass ( ) - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) ) ;
CompiledSourceCreator . ExpectedSourceType = ViewModelContext . GetViewModelClass ( ) ;
CompiledSourceCreator . PropertyName = ViewModelContext . GetViewModelName ( ) ;
2023-11-17 14:09:15 -05:00
bool bCanBeSet = ViewModelContext . bCreateSetterFunction ;
bool bCanBeEvaluated = SourceCreatorContext . DynamicContext . IsValid ( ) ;
bool bIsOptional = SourceCreatorContext . Source - > bIsOptional ;
2023-03-22 13:07:37 -04:00
bool bCreateInstance = false ;
2023-11-17 14:09:15 -05:00
bool bIsUserWidgetProperty = ! SourceCreatorContext . DynamicContext . IsValid ( ) ;
2023-03-22 13:07:37 -04:00
2023-11-17 14:09:15 -05:00
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
bCanBeSet = true ;
ensure ( bIsOptional = = true ) ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance )
{
bCreateInstance = true ;
2024-01-19 11:53:42 -05:00
if ( ViewModelContext . bExposeInstanceInEditor )
{
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( SourceCreatorContext . ReadPropertyPathHandle ) ;
if ( CompiledFieldPath = = nullptr )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelInvalidInstanceNotFound " , " The path for the created viewmodel instance was not found. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2024-01-19 11:53:42 -05:00
) ;
bIsCompileStepValid = false ;
continue ;
}
CompiledSourceCreator . FieldPath = * CompiledFieldPath ;
}
ensure ( bIsOptional = = false ) ;
2023-11-17 14:09:15 -05:00
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : PropertyPath )
{
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( SourceCreatorContext . ReadPropertyPathHandle ) ;
if ( CompiledFieldPath = = nullptr )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelInvalidInitializationBindingNotGenerated " , " The viewmodel initialization binding was not generated. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsCompileStepValid = false ;
continue ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
CompiledSourceCreator . FieldPath = * CompiledFieldPath ;
ensure ( bIsOptional = = ViewModelContext . bOptional ) ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : GlobalViewModelCollection )
{
ensure ( ! ViewModelContext . GlobalViewModelIdentifier . IsNone ( ) ) ;
FMVVMViewModelContext GlobalViewModelInstance ;
GlobalViewModelInstance . ContextClass = ViewModelContext . GetViewModelClass ( ) ;
GlobalViewModelInstance . ContextName = ViewModelContext . GlobalViewModelIdentifier ;
if ( ! GlobalViewModelInstance . IsValid ( ) )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelGlobalContextIdentifier " , " The context for viewmodel could not be created. Change the identifier. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-11-17 14:09:15 -05:00
) ;
2022-06-08 13:25:42 -04:00
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
CompiledSourceCreator . GlobalViewModelInstance = MoveTemp ( GlobalViewModelInstance ) ;
ensure ( bIsOptional = = ViewModelContext . bOptional ) ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Resolver )
{
UMVVMViewModelContextResolver * Resolver = DuplicateObject ( ViewModelContext . Resolver . Get ( ) , ViewExtension ) ;
if ( ! Resolver )
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewmodelFailedResolverDuplicate " , " Internal error. The resolver could not be dupliated. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsCompileStepValid = false ;
continue ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
CompiledSourceCreator . Resolver = Resolver ;
ensure ( bIsOptional = = ViewModelContext . bOptional ) ;
}
else
{
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelWithoutValidCreationType " , " The viewmodel doesn't have a valid creation type. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsCompileStepValid = false ;
continue ;
}
if ( SourceCreatorContext . DynamicContext )
{
2024-02-15 15:40:59 -05:00
if ( ! SourceCreatorContext . DynamicContext - > ParentSource )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
AddMessageForViewModel ( ViewModelContext
, LOCTEXT ( " ViewModelWithoutValidDyanmicParentSource " , " The viewmodel doesn't have a valid parent source. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsCompileStepValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
}
2023-03-22 13:07:37 -04:00
CompiledSourceCreator . Flags = 0 ;
2023-12-01 11:13:35 -05:00
CompiledSourceCreator . Flags | = bCreateInstance ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : TypeCreateInstance : 0 ;
CompiledSourceCreator . Flags | = bIsUserWidgetProperty ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : IsUserWidgetProperty : 0 ;
CompiledSourceCreator . Flags | = bIsUserWidgetProperty ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : SetUserWidgetProperty : 0 ;
CompiledSourceCreator . Flags | = bIsOptional ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : IsOptional : 0 ;
CompiledSourceCreator . Flags | = bCanBeSet ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : CanBeSet : 0 ;
CompiledSourceCreator . Flags | = bCanBeEvaluated ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : CanBeEvaluated : 0 ;
CompiledSourceCreator . Flags | = ( uint16 ) FMVVMViewClass_Source : : EFlags : : IsViewModel ;
2024-01-19 11:53:42 -05:00
CompiledSourceCreator . Flags | = ViewModelContext . bExposeInstanceInEditor ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : IsViewModelInstanceExposed : 0 ;
2023-11-17 14:09:15 -05:00
2024-02-15 15:40:59 -05:00
{
FSortData SortData ;
SortData . Source = SourceCreatorContext . Source . ToSharedRef ( ) ;
SortDatas . Add ( CompiledSourceCreator . GetName ( ) , SortData ) ;
ensure ( SortData . Source - > Name = = CompiledSourceCreator . GetName ( ) ) ;
}
2023-11-17 14:09:15 -05:00
UnsortedSourceCreators . Add ( MoveTemp ( CompiledSourceCreator ) ) ;
}
// The other sources needed by the view
for ( FCompilerWidgetCreatorContext & WidgetCreator : WidgetCreatorContexts )
{
2023-12-01 11:13:35 -05:00
FMVVMViewClass_Source CompiledSourceCreator ;
2024-02-15 15:40:59 -05:00
ensure ( WidgetCreator . Source - > AuthoritativeClass & & WidgetCreator . Source - > AuthoritativeClass - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) ) ;
2023-11-17 14:09:15 -05:00
CompiledSourceCreator . ExpectedSourceType = const_cast < UClass * > ( WidgetCreator . Source - > AuthoritativeClass ) ;
CompiledSourceCreator . PropertyName = WidgetCreator . Source - > Name ;
CompiledSourceCreator . Flags = 0 ;
2023-12-01 11:13:35 -05:00
CompiledSourceCreator . Flags | = WidgetCreator . bSelfReference ? ( uint16 ) FMVVMViewClass_Source : : EFlags : : SelfReference : 0 ;
2023-11-17 14:09:15 -05:00
if ( ! WidgetCreator . bSelfReference )
{
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( WidgetCreator . ReadPropertyPathHandle ) ;
if ( CompiledFieldPath = = nullptr )
{
AddMessage ( FText : : Format ( LOCTEXT ( " WidgetInvalidInitializationBindingNotGenerated " , " The widget {0} initialization binding was not generated. " ) , FText : : FromName ( WidgetCreator . Source - > Name ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-11-17 14:09:15 -05:00
) ;
bIsCompileStepValid = false ;
continue ;
}
CompiledSourceCreator . FieldPath = * CompiledFieldPath ;
}
2023-03-22 13:07:37 -04:00
2024-02-15 15:40:59 -05:00
{
FSortData SortData ;
SortData . Source = WidgetCreator . Source ;
SortDatas . Add ( CompiledSourceCreator . GetName ( ) , SortData ) ;
}
2023-09-28 14:01:37 -04:00
UnsortedSourceCreators . Add ( MoveTemp ( CompiledSourceCreator ) ) ;
}
2023-12-01 11:13:35 -05:00
// Sanity check
{
// Test if all NeededBindingSources in inside the UnsortedSourceCreators
for ( const TSharedRef < FCompilerBindingSource > & BindingSource : NeededBindingSources )
{
const FMVVMViewClass_Source * FoundClassSource = UnsortedSourceCreators . FindByPredicate ( [ ToFindName = BindingSource - > Name ] ( const FMVVMViewClass_Source & Other ) { return Other . GetName ( ) = = ToFindName ; } ) ;
if ( FoundClassSource = = nullptr )
{
2024-02-15 15:40:59 -05:00
AddMessage ( FText : : Format ( LOCTEXT ( " CompileSources_MissingSources " , " Internal error. The source {0} was not compiled. " ) , FText : : FromName ( BindingSource - > Name ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Warning
2023-12-01 11:13:35 -05:00
) ;
}
}
ensure ( SortDatas . Num ( ) = = UnsortedSourceCreators . Num ( ) ) ;
}
2024-02-15 15:40:59 -05:00
// Sort the source creators by priority and then by name
2023-09-28 14:01:37 -04:00
if ( UnsortedSourceCreators . Num ( ) > 1 )
{
2024-02-15 15:40:59 -05:00
// Calculate dependencies
2023-09-28 14:01:37 -04:00
for ( auto & SortDataPair : SortDatas )
{
2024-02-15 15:40:59 -05:00
SortDataPair . Value . CalculateSortIndex ( SortDataPair . Key , SortDatas ) ;
2023-09-28 14:01:37 -04:00
}
2024-02-15 15:40:59 -05:00
// Report errors
for ( const auto & SortDataPair : SortDatas )
{
for ( FName OtherSource : SortDataPair . Value . Errors )
{
AddMessage (
FText : : Format ( LOCTEXT ( " CompileSources_Dependencies " , " The source {0} circularly depends on the source {1} " ) , FText : : FromName ( SortDataPair . Key ) , FText : : FromName ( OtherSource ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Info
2024-02-15 15:40:59 -05:00
) ;
}
}
// Sort the sources
2023-12-01 11:13:35 -05:00
UnsortedSourceCreators . Sort ( [ & SortDatas ] ( const FMVVMViewClass_Source & A , const FMVVMViewClass_Source & B )
2023-09-28 14:01:37 -04:00
{
2023-12-01 11:13:35 -05:00
int32 ASortIndex = SortDatas [ A . GetName ( ) ] . SortIndex ;
int32 BSortIndex = SortDatas [ B . GetName ( ) ] . SortIndex ;
2023-09-28 14:01:37 -04:00
if ( ASortIndex = = BSortIndex )
{
2023-12-01 11:13:35 -05:00
return A . GetName ( ) . LexicalLess ( B . GetName ( ) ) ;
2023-09-28 14:01:37 -04:00
}
return ASortIndex < BSortIndex ;
} ) ;
}
2023-12-01 11:13:35 -05:00
// Add the sorted viewmodel array to the ViewExtension
ViewExtension - > Sources = MoveTemp ( UnsortedSourceCreators ) ;
if ( ViewExtension - > Sources . Num ( ) > 64 )
{
// The view use a uint64 bitfield to filter which viewmodel/source is valid/initialized.
//You can use the MVVM.LogViewCompiledResult command to display the compile result and see the name of the sources.
2024-03-26 02:39:40 -04:00
AddMessage ( LOCTEXT ( " TooManySources " , " There is too many sources for the view. Try spliting your widget into other widgets. " ) , Compiler : : EMessageType : : Error ) ;
2023-12-01 11:13:35 -05:00
}
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : PreCompileBindings ( UWidgetBlueprintGeneratedClass * Class )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
auto TestExecutionMode = [ Self = this ] ( const FMVVMBlueprintViewBinding & Binding ) - > bool
2023-08-04 10:49:38 -04:00
{
if ( Binding . bOverrideExecutionMode )
{
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsExecutionModeAllowed ( Binding . OverrideExecutionMode ) )
{
2023-12-19 11:31:33 -05:00
Self - > AddMessageForBinding ( Binding
, LOCTEXT ( " NotAllowedExecutionMode " , " The binding has a restricted execution mode. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
) ;
2023-08-04 10:49:38 -04:00
return false ;
}
}
return true ;
} ;
2023-11-17 14:09:15 -05:00
auto AddFieldIds = [ Self = this , Class ] ( FCompilerBinding & ValidBinding , const FMVVMBlueprintViewBinding & Binding ) - > bool
2023-08-04 10:49:38 -04:00
{
2023-11-17 14:09:15 -05:00
// Add FieldId
bool bHasFieldId = false ;
if ( ! ValidBinding . bIsOneTimeBinding )
2023-08-04 10:49:38 -04:00
{
2023-11-17 14:09:15 -05:00
for ( const TSharedPtr < FGeneratedReadFieldPathContext > & ReadPath : ValidBinding . ReadPaths )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
if ( ReadPath - > NotificationField )
{
if ( ReadPath - > NotificationField - > LibraryCompilerHandle . IsValid ( ) )
{
bHasFieldId = true ;
}
else if ( ReadPath - > NotificationField - > Source ) // it is maybe OneTIme
{
const UClass * SourceContextClass = ReadPath - > NotificationField - > Source - > AuthoritativeClass ;
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldIdHandle , FText > FieldIdResult = Self - > BindingLibraryCompiler . AddFieldId ( SourceContextClass , ReadPath - > NotificationField - > NotificationId . GetFieldName ( ) ) ;
if ( FieldIdResult . HasError ( ) | | ! FieldIdResult . GetValue ( ) . IsValid ( ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " CouldNotCreateFieldId " , " Could not create Field. {0} " ) , FieldIdResult . GetError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
ReadPath - > NotificationField - > LibraryCompilerHandle = FieldIdResult . StealValue ( ) ;
bHasFieldId = ReadPath - > NotificationField - > LibraryCompilerHandle . IsValid ( ) ;
}
}
2023-11-16 23:04:14 -05:00
}
}
2023-11-17 14:09:15 -05:00
// Test correct numbers of FieldIds
bool bRequiresValidFieldId = ! ValidBinding . bIsOneTimeBinding ;
if ( bRequiresValidFieldId & & ! bHasFieldId )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
Self - > AddMessageForBinding ( Binding
, LOCTEXT ( " CouldNotCreateSourceFields " , " There is no field to bind to. The binding must be a OneTime binding. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
return true ;
2023-08-04 10:49:38 -04:00
} ;
2023-11-17 14:09:15 -05:00
auto AddReadPaths = [ Self = this , Class ] ( FCompilerBinding & ValidBinding , const FMVVMBlueprintViewBinding & Binding ) - > bool
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
bool bHasValidReadHandle = false ;
if ( ValidBinding . Type ! = FCompilerBinding : : EType : : ComplexConversionFunction )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
for ( const TSharedPtr < FGeneratedReadFieldPathContext > & ReadPath : ValidBinding . ReadPaths )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
if ( ReadPath - > LibraryCompilerHandle . IsValid ( ) )
{
bHasValidReadHandle = true ;
}
else
{
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = Self - > BindingLibraryCompiler . AddFieldPath ( ReadPath - > SkeletalGeneratedFields , true ) ;
if ( FieldPathResult . HasError ( ) | | ! FieldPathResult . GetValue ( ) . IsValid ( ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( Private : : CouldNotCreateSourceFieldPathFormat , : : UE : : MVVM : : FieldPathHelper : : ToText ( ReadPath - > GeneratedFields ) , FieldPathResult . GetError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
2022-06-08 13:25:42 -04:00
2023-11-17 14:09:15 -05:00
ReadPath - > LibraryCompilerHandle = FieldPathResult . StealValue ( ) ;
bHasValidReadHandle = ReadPath - > LibraryCompilerHandle . IsValid ( ) ;
}
2023-11-16 23:04:14 -05:00
}
2022-08-24 13:14:51 -04:00
}
2023-11-17 14:09:15 -05:00
// Test read
bool bRequiresReadHandle = ValidBinding . Type ! = FCompilerBinding : : EType : : ComplexConversionFunction ;
if ( bRequiresReadHandle & & ! bHasValidReadHandle )
2023-03-22 13:07:37 -04:00
{
2023-11-17 14:09:15 -05:00
Self - > AddMessageForBinding ( Binding
, LOCTEXT ( " CouldNotCreateSourceReadIsPresent " , " Internal error. There should be no property to read from. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-16 20:11:58 -05:00
) ;
2023-11-17 14:09:15 -05:00
return false ;
2023-11-16 20:11:58 -05:00
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
return true ;
2023-11-16 23:04:14 -05:00
} ;
2023-11-17 14:09:15 -05:00
auto AddWritePaths = [ Self = this , Class ] ( FCompilerBinding & ValidBinding , const FMVVMBlueprintViewBinding & Binding ) - > bool
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
if ( ValidBinding . WritePath - > LibraryCompilerHandle . IsValid ( ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
return true ;
}
if ( ! ValidBinding . WritePath )
{
Self - > AddMessageForBinding ( Binding
, LOCTEXT ( " InvalidWritePath " , " Internal Error. The write path is invalid. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = Self - > BindingLibraryCompiler . AddFieldPath ( ValidBinding . WritePath - > SkeletalGeneratedFields , false ) ;
if ( FieldPathResult . HasError ( ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( Private : : CouldNotCreateDestinationFieldPathFormat , : : UE : : MVVM : : FieldPathHelper : : ToText ( ValidBinding . WritePath - > GeneratedFields ) , FieldPathResult . GetError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
ValidBinding . WritePath - > LibraryCompilerHandle = FieldPathResult . StealValue ( ) ;
// test write
return true ;
} ;
auto AddConversionFunction = [ Self = this , Class ] ( FCompilerBinding & ValidBinding , const FMVVMBlueprintViewBinding & Binding ) - > bool
{
const UFunction * ConversionFunction = nullptr ;
{
UMVVMBlueprintViewConversionFunction * ViewConversionFunction = ValidBinding . ConversionFunction . Get ( ) ;
if ( ViewConversionFunction )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
ConversionFunction = ViewConversionFunction - > GetCompiledFunction ( Class ) ;
if ( ConversionFunction = = nullptr )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " ConversionFunctionNotFound " , " The conversion function '{0}' could not be found. " ) , FText : : FromName ( ViewConversionFunction - > GetCompiledFunctionName ( Class ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
2023-12-22 09:43:14 -05:00
FMVVMBlueprintFunctionReference FunctionOrWrapperFunction = ViewConversionFunction - > GetConversionFunction ( ) ;
if ( FunctionOrWrapperFunction . GetType ( ) = = EMVVMBlueprintFunctionReferenceType : : Function )
2023-11-17 14:09:15 -05:00
{
2023-12-22 09:43:14 -05:00
const UFunction * WrapperFunction = FunctionOrWrapperFunction . GetFunction ( Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ;
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsConversionFunctionAllowed ( Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , WrapperFunction ) )
2023-11-17 14:09:15 -05:00
{
Self - > AddMessageForBinding ( Binding
2024-01-12 10:07:58 -05:00
, FText : : Format ( LOCTEXT ( " ConversionFunctionNotAllow " , " The conversion function {0} is not allowed. " ) , FText : : FromName ( WrapperFunction ? WrapperFunction - > GetFName ( ) : FName ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2024-01-12 10:07:58 -05:00
, FMVVMBlueprintPinId ( )
) ;
return false ;
}
}
else if ( FunctionOrWrapperFunction . GetType ( ) = = EMVVMBlueprintFunctionReferenceType : : Node )
{
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsConversionFunctionAllowed ( Self - > WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , FunctionOrWrapperFunction . GetNode ( ) ) )
{
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " ConversionFunctionNotAllow " , " The conversion function {0} is not allowed. " ) , FText : : FromName ( FunctionOrWrapperFunction . GetNode ( ) . Get ( ) ? FunctionOrWrapperFunction . GetNode ( ) - > GetFName ( ) : FName ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
}
}
else
{
2023-12-19 11:31:33 -05:00
Self - > AddMessageForBinding ( Binding
, LOCTEXT ( " ConversionFunctionNodeNotAllow " , " The conversion function node is not allowed. " )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
) ;
2023-11-17 14:09:15 -05:00
return false ;
}
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
}
if ( ConversionFunction ! = nullptr )
{
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = Self - > BindingLibraryCompiler . AddConversionFunctionFieldPath ( Class , ConversionFunction ) ;
if ( FieldPathResult . HasError ( ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
Self - > AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " CouldNotCreateConversionFunctionFieldPath " , " Couldn't create the conversion function field path '{0}'. {1} " )
, FText : : FromString ( ConversionFunction - > GetPathName ( ) )
, FieldPathResult . GetError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
return false ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
ValidBinding . ConversionFunctionHandle = FieldPathResult . StealValue ( ) ;
}
// Sanity check
{
const bool bShouldHaveConversionFunction = ValidBinding . Type = = FCompilerBinding : : EType : : ComplexConversionFunction | | ValidBinding . Type = = FCompilerBinding : : EType : : SimpleConversionFunction ;
if ( ( ConversionFunction ! = nullptr ) ! = bShouldHaveConversionFunction )
2023-11-16 23:04:14 -05:00
{
2024-03-26 02:39:40 -04:00
Self - > AddMessageForBinding ( Binding , LOCTEXT ( " ConversionFunctionShouldExist " , " Internal error. The conversion function should exist. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
return false ;
}
const bool bShouldHaveComplexConversionFunction = ValidBinding . Type = = FCompilerBinding : : EType : : ComplexConversionFunction ;
if ( bShouldHaveComplexConversionFunction ! = BindingHelper : : IsValidForComplexRuntimeConversion ( ConversionFunction ) )
{
2024-03-26 02:39:40 -04:00
Self - > AddMessageForBinding ( Binding , LOCTEXT ( " ConversionFunctionIsNotComplex " , " Internal Error. The complex conversion function does not respect the prerequisite. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-16 23:04:14 -05:00
return false ;
}
}
2023-11-17 14:09:15 -05:00
return true ;
} ;
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
{
const FMVVMBlueprintViewBinding & Binding = * ( BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ) ;
if ( ValidBinding - > Type ! = FCompilerBinding : : EType : : Assignment
& & ValidBinding - > Type ! = FCompilerBinding : : EType : : ComplexConversionFunction
& & ValidBinding - > Type ! = FCompilerBinding : : EType : : SimpleConversionFunction )
2023-11-16 23:04:14 -05:00
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " UnsupportedBindingType " , " The binding is invalid. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-11-16 23:04:14 -05:00
continue ;
}
2023-11-17 14:09:15 -05:00
if ( ! TestExecutionMode ( Binding ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-11-16 23:04:14 -05:00
continue ;
}
2023-11-17 14:09:15 -05:00
if ( ! AddFieldIds ( ValidBinding . Get ( ) , Binding )
| | ! AddReadPaths ( ValidBinding . Get ( ) , Binding )
| | ! AddWritePaths ( ValidBinding . Get ( ) , Binding )
| | ! AddConversionFunction ( ValidBinding . Get ( ) , Binding ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
continue ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
// Generate the binding
TValueOrError < FCompiledBindingLibraryCompiler : : FBindingHandle , FText > BindingResult = ValidBinding - > Type = = FCompilerBinding : : EType : : ComplexConversionFunction
? BindingLibraryCompiler . AddComplexBinding ( ValidBinding - > WritePath - > LibraryCompilerHandle , ValidBinding - > ConversionFunctionHandle )
: BindingLibraryCompiler . AddBinding ( ValidBinding - > ReadPaths [ 0 ] - > LibraryCompilerHandle , ValidBinding - > WritePath - > LibraryCompilerHandle , ValidBinding - > ConversionFunctionHandle ) ;
if ( BindingResult . HasError ( ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
AddMessageForBinding ( Binding
, FText : : Format ( LOCTEXT ( " CouldNotCreateBinding " , " Could not create binding. {0} " ) , BindingResult . StealError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( )
2023-11-17 14:09:15 -05:00
) ;
bIsPreCompileStepValid = false ;
continue ;
}
ValidBinding - > BindingHandle = BindingResult . StealValue ( ) ;
2024-03-26 02:39:40 -04:00
ValidBinding - > CompilerBindingHandle = Compiler : : FCompilerBindingHandle : : MakeHandle ( ) ;
2023-11-17 14:09:15 -05:00
}
// Add bindings for the dynamic viewmodels
for ( TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > & ViewModelDynamic : SourceViewModelDynamicCreatorContexts )
{
if ( ensure ( ViewModelDynamic - > Source . IsValid ( ) & & ViewModelDynamic - > ParentSource . IsValid ( ) ) )
{
if ( ! ViewModelDynamic - > NotificationId . IsValid ( ) )
{
2024-03-26 02:39:40 -04:00
AddMessage ( FText : : Format ( LOCTEXT ( " InvalidNotificationFieldId " , " {0} doesn't have a field Id. " ) , FText : : FromName ( ViewModelDynamic - > Source - > Name ) ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
continue ;
}
const UClass * SourceContextClass = ViewModelDynamic - > ParentSource - > AuthoritativeClass ;
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldIdHandle , FText > FieldIdResult = BindingLibraryCompiler . AddFieldId ( SourceContextClass , ViewModelDynamic - > NotificationId . GetFieldName ( ) ) ;
if ( FieldIdResult . HasError ( ) | | ! FieldIdResult . GetValue ( ) . IsValid ( ) )
{
2024-03-26 02:39:40 -04:00
AddMessage ( FText : : Format ( LOCTEXT ( " CouldNotCreateFieldIdForDynamic " , " Could not create Field for {0}. {1} " ) , FText : : FromName ( ViewModelDynamic - > Source - > Name ) , FieldIdResult . GetError ( ) ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
continue ;
}
ViewModelDynamic - > NotificationIdLibraryCompilerHandle = FieldIdResult . GetValue ( ) ;
}
}
}
void FMVVMViewBlueprintCompiler : : CompileBindings ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
{
static IConsoleVariable * CVarDefaultExecutionMode = IConsoleManager : : Get ( ) . FindConsoleVariable ( TEXT ( " MVVM.DefaultExecutionMode " ) ) ;
ensure ( CVarDefaultExecutionMode ) ;
if ( ! CVarDefaultExecutionMode )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " CantFindDefaultExecutioMode " , " The default execution mode cannot be found. " ) . ToString ( ) ) ;
return ;
}
2023-12-01 11:13:35 -05:00
// Sort the array to have a predictable list
2023-11-17 14:09:15 -05:00
{
2023-12-01 11:13:35 -05:00
UMVVMBlueprintView * LocalBlueprintView = BlueprintView . Get ( ) ;
ValidBindings . StableSort ( [ LocalBlueprintView ] ( const TSharedRef < FCompilerBinding > & A , const TSharedRef < FCompilerBinding > & B )
{
const FMVVMBlueprintViewBinding & BindingA = * ( LocalBlueprintView - > GetBindingAt ( A - > Key . ViewBindingIndex ) ) ;
const FMVVMBlueprintViewBinding & BindingB = * ( LocalBlueprintView - > GetBindingAt ( B - > Key . ViewBindingIndex ) ) ;
return BindingA . BindingId < BindingB . BindingId ;
} ) ;
}
2023-11-17 14:09:15 -05:00
2023-12-01 11:13:35 -05:00
ensure ( ViewExtension - > Bindings . Num ( ) = = 0 ) ;
ViewExtension - > Bindings . Reset ( ValidBindings . Num ( ) ) ;
2023-11-17 14:09:15 -05:00
for ( int32 ValidBindingIndex = 0 ; ValidBindingIndex < ValidBindings . Num ( ) ; + + ValidBindingIndex )
{
const TSharedRef < FCompilerBinding > & ValidBinding = ValidBindings [ ValidBindingIndex ] ;
const FMVVMBlueprintViewBinding & Binding = * ( BlueprintView - > GetBindingAt ( ValidBinding - > Key . ViewBindingIndex ) ) ;
2023-12-01 11:13:35 -05:00
const FMVVMVCompiledBinding * CompiledBinding = CompileResult . Bindings . Find ( ValidBinding - > BindingHandle ) ;
if ( CompiledBinding = = nullptr )
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " CompiledBindingNotGenerated " , " Could not generate compiled binding. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-12-01 11:13:35 -05:00
bIsCompileStepValid = false ;
continue ;
}
FMVVMViewClass_BindingKey BindingKey = FMVVMViewClass_BindingKey ( ViewExtension - > Bindings . AddDefaulted ( ) ) ;
FMVVMViewClass_Binding & NewBinding = ViewExtension - > Bindings [ BindingKey . GetIndex ( ) ] ;
NewBinding . Binding = CompiledBinding ? * CompiledBinding : FMVVMVCompiledBinding ( ) ;
2024-01-08 20:31:39 -05:00
NewBinding . Flags = 0 ;
2023-12-01 11:13:35 -05:00
NewBinding . ExecutionMode = Binding . bOverrideExecutionMode ? Binding . OverrideExecutionMode : ( EMVVMExecutionMode ) CVarDefaultExecutionMode - > GetInt ( ) ;
NewBinding . SourceBitField = 0 ;
NewBinding . EditorId = Binding . BindingId ;
2024-02-15 15:40:59 -05:00
// Add the write source.
if ( ValidBinding - > WritePath & & ValidBinding - > WritePath - > OptionalSource )
{
int32 ViewExtensionSourceCreatorsIndex = ViewExtension - > Sources . IndexOfByPredicate ( [ LookFor = ValidBinding - > WritePath - > OptionalSource - > Name ] ( const FMVVMViewClass_Source & Other )
{
return Other . GetName ( ) = = LookFor ;
} ) ;
if ( ViewExtension - > Sources . IsValidIndex ( ViewExtensionSourceCreatorsIndex ) )
{
FMVVMViewClass_SourceKey FieldClassSourceKey = FMVVMViewClass_SourceKey ( ViewExtensionSourceCreatorsIndex ) ;
NewBinding . SourceBitField | = FieldClassSourceKey . GetBit ( ) ;
}
}
2024-02-13 15:28:15 -05:00
TArray < FMVVMViewClass_SourceKey , TInlineAllocator < 16 > > SharedExecuteAtInitializationBindings ;
int32 SharedBindingsCount = 0 ;
2024-01-08 20:31:39 -05:00
// Find the source needed by the binding. Also generate every bindings on that source (that need to register to the FieldNotify).
2023-11-17 14:09:15 -05:00
for ( TSharedPtr < FGeneratedReadFieldPathContext > ReadPath : ValidBinding - > ReadPaths )
{
2024-02-15 15:40:59 -05:00
if ( ReadPath - > Source = = nullptr )
2023-11-17 14:09:15 -05:00
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " InvalidSourceInternal " , " Internal error. The binding has an invalid source. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
continue ;
}
2024-02-15 15:40:59 -05:00
int32 ViewExtensionSourceCreatorsIndex = ViewExtension - > Sources . IndexOfByPredicate ( [ LookFor = ReadPath - > Source - > Name ] ( const FMVVMViewClass_Source & Other )
2023-03-22 13:07:37 -04:00
{
2023-12-01 11:13:35 -05:00
return Other . GetName ( ) = = LookFor ;
2023-03-22 13:07:37 -04:00
} ) ;
2023-12-01 11:13:35 -05:00
if ( ! ViewExtension - > Sources . IsValidIndex ( ViewExtensionSourceCreatorsIndex ) )
2023-03-22 13:07:37 -04:00
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " CompiledSourceCreatorNotGenerated " , " Internal error. The source creator was not generated. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
2023-11-16 23:04:14 -05:00
continue ;
}
2023-12-01 11:13:35 -05:00
// Add the needed source.
FMVVMViewClass_SourceKey FieldClassSourceKey = FMVVMViewClass_SourceKey ( ViewExtensionSourceCreatorsIndex ) ;
NewBinding . SourceBitField | = FieldClassSourceKey . GetBit ( ) ;
2023-11-16 23:04:14 -05:00
2023-12-01 11:13:35 -05:00
// FieldId
const bool bHasField = ( ReadPath - > NotificationField & & ! ValidBinding - > bIsOneTimeBinding ) ;
const UE : : FieldNotification : : FFieldId * CompiledFieldId = bHasField ? CompileResult . FieldIds . Find ( ReadPath - > NotificationField - > LibraryCompilerHandle ) : nullptr ;
if ( CompiledFieldId = = nullptr & & bHasField )
2023-11-16 23:04:14 -05:00
{
2024-03-26 02:39:40 -04:00
AddMessageForBinding ( Binding , LOCTEXT ( " CompiledFieldNotGenerated " , " Internal error. The FieldId was not generated. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
continue ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
bool bExecuteAtInitialization = ValidBinding - > Key . bIsForwardBinding ;
2023-11-16 23:04:14 -05:00
2023-12-01 11:13:35 -05:00
FMVVMViewClass_Source & ClassSource = ViewExtension - > Sources [ ViewExtensionSourceCreatorsIndex ] ;
FMVVMViewClass_SourceBinding & NewSourceBinding = ClassSource . Bindings . AddDefaulted_GetRef ( ) ;
NewSourceBinding . BindingKey = BindingKey ;
2023-11-17 14:09:15 -05:00
2023-12-01 11:13:35 -05:00
NewSourceBinding . Flags = 0 ;
2024-01-08 20:31:39 -05:00
// Set the ExecuteAtInitialization but remove it later if it's not the last of the group.
2023-12-01 11:13:35 -05:00
NewSourceBinding . Flags | = ( bExecuteAtInitialization ) ? ( uint8 ) FMVVMViewClass_SourceBinding : : EFlags : : ExecuteAtInitialization : 0 ;
2023-11-16 23:04:14 -05:00
2023-12-01 11:13:35 -05:00
if ( CompiledFieldId )
{
NewSourceBinding . FieldId = FFieldNotificationId ( CompiledFieldId - > GetName ( ) ) ;
ClassSource . FieldToRegisterTo . AddUnique ( FMVVMViewClass_FieldId ( * CompiledFieldId ) ) ;
// Count the number of shared instance of that binding. (they can come from the same Source)
//This flag is used at runtime to know if we should delay the binding execution.
//We only delay when the field changes. It should only be shared if it has field.
2024-02-13 15:28:15 -05:00
+ + SharedBindingsCount ;
}
if ( bExecuteAtInitialization )
{
SharedExecuteAtInitializationBindings . Add ( FieldClassSourceKey ) ;
2023-12-01 11:13:35 -05:00
}
}
2024-01-08 20:31:39 -05:00
// Set binding flags.
NewBinding . Flags | = ( ! ValidBinding - > bIsOneTimeBinding ) ? ( uint8 ) FMVVMViewClass_Binding : : EFlags : : OneWay : 0 ;
2024-02-13 15:28:15 -05:00
NewBinding . Flags | = ( SharedBindingsCount > 1 ) ? ( uint8 ) FMVVMViewClass_Binding : : EFlags : : Shared : 0 ;
2024-01-08 20:31:39 -05:00
NewBinding . Flags | = ( Binding . bOverrideExecutionMode ) ? ( uint8 ) FMVVMViewClass_Binding : : EFlags : : OverrideExecuteMode : 0 ;
NewBinding . Flags | = ( Binding . bEnabled ) ? ( uint8 ) FMVVMViewClass_Binding : : EFlags : : EnabledByDefault : 0 ;
2023-12-01 11:13:35 -05:00
2024-02-13 15:28:15 -05:00
// Only the last binding in the complex conversion has ExecuteAtInitialization.
if ( SharedExecuteAtInitializationBindings . Num ( ) > 1 )
2023-12-01 11:13:35 -05:00
{
2024-02-13 15:28:15 -05:00
SharedExecuteAtInitializationBindings . Sort ( [ ] ( const FMVVMViewClass_SourceKey & A , const FMVVMViewClass_SourceKey & B )
2023-12-01 11:13:35 -05:00
{
return A . GetIndex ( ) < B . GetIndex ( ) ;
} ) ;
2024-02-13 15:28:15 -05:00
//Remove the flag on all the binding on all the sources ecept the last one.
for ( int32 Index = 0 ; Index < SharedExecuteAtInitializationBindings . Num ( ) - 1 ; + + Index )
2023-12-01 11:13:35 -05:00
{
2024-02-13 15:28:15 -05:00
FMVVMViewClass_Source & ClassSource = ViewExtension - > Sources [ SharedExecuteAtInitializationBindings [ Index ] . GetIndex ( ) ] ;
2023-12-01 11:13:35 -05:00
for ( FMVVMViewClass_SourceBinding & SourceBinding : ClassSource . Bindings )
{
if ( SourceBinding . GetBindingKey ( ) = = BindingKey )
{
SourceBinding . Flags & = ~ ( uint8 ) FMVVMViewClass_SourceBinding : : EFlags : : ExecuteAtInitialization ;
}
}
}
2024-02-13 15:28:15 -05:00
// Keep only one ExecuteAtInitialization on the last source.
2023-12-01 11:13:35 -05:00
{
2024-02-13 15:28:15 -05:00
FMVVMViewClass_Source & ClassSource = ViewExtension - > Sources [ SharedExecuteAtInitializationBindings . Last ( ) . GetIndex ( ) ] ;
2023-12-01 11:13:35 -05:00
bool bFound = false ;
for ( int32 Index = ClassSource . Bindings . Num ( ) - 1 ; Index > = 0 ; - - Index )
{
FMVVMViewClass_SourceBinding & SourceBinding = ClassSource . Bindings [ Index ] ;
if ( SourceBinding . GetBindingKey ( ) = = BindingKey )
{
if ( bFound )
{
SourceBinding . Flags & = ~ ( uint8 ) FMVVMViewClass_SourceBinding : : EFlags : : ExecuteAtInitialization ;
}
bFound = true ;
}
}
}
2023-11-16 23:04:14 -05:00
}
2023-11-16 20:11:58 -05:00
}
2023-12-01 11:13:35 -05:00
}
2023-11-16 20:11:58 -05:00
2023-12-01 11:13:35 -05:00
void FMVVMViewBlueprintCompiler : : CompileEvaluateSources ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
{
2023-11-17 14:09:15 -05:00
for ( const TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > & ViewModelDynamic : SourceViewModelDynamicCreatorContexts )
2023-11-16 20:11:58 -05:00
{
2023-12-01 11:13:35 -05:00
int32 ViewExtensionSourceCreatorsIndex = ViewExtension - > Sources . IndexOfByPredicate ( [ LookFor = ViewModelDynamic - > Source - > Name ] ( const FMVVMViewClass_Source & Other )
2023-11-17 14:09:15 -05:00
{
2023-12-01 11:13:35 -05:00
return Other . GetName ( ) = = LookFor ;
2023-11-17 14:09:15 -05:00
} ) ;
2023-12-01 11:13:35 -05:00
if ( ! ViewExtension - > Sources . IsValidIndex ( ViewExtensionSourceCreatorsIndex ) )
2023-11-16 23:04:14 -05:00
{
2024-03-26 02:39:40 -04:00
AddMessage ( LOCTEXT ( " CompiledSourceCreatorNotGenerated " , " Internal error. The source creator was not generated. " ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
continue ;
2023-11-16 23:04:14 -05:00
}
2023-12-01 11:13:35 -05:00
int32 ViewExtensionParentCreatorsIndex = ViewExtension - > Sources . IndexOfByPredicate ( [ LookFor = ViewModelDynamic - > ParentSource - > Name ] ( const FMVVMViewClass_Source & Other )
{
return Other . GetName ( ) = = LookFor ;
} ) ;
if ( ! ViewExtension - > Sources . IsValidIndex ( ViewExtensionParentCreatorsIndex ) )
{
2024-03-26 02:39:40 -04:00
AddMessage ( LOCTEXT ( " CompiledParentSourceCreatorNotGenerated " , " Internal error. The parent source creator was not generated. " ) , Compiler : : EMessageType : : Error ) ;
2023-12-01 11:13:35 -05:00
bIsCompileStepValid = false ;
continue ;
}
2023-11-16 23:04:14 -05:00
2023-12-01 11:13:35 -05:00
const UE : : FieldNotification : : FFieldId * CompiledFieldId = CompileResult . FieldIds . Find ( ViewModelDynamic - > NotificationIdLibraryCompilerHandle ) ;
2023-11-17 14:09:15 -05:00
if ( CompiledFieldId = = nullptr )
{
2024-03-26 02:39:40 -04:00
AddMessage ( LOCTEXT ( " CompiledFieldNotifyNotGenerated " , " Internal error. The field notify was not generated. " ) , Compiler : : EMessageType : : Error ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
continue ;
}
2023-12-01 11:13:35 -05:00
FMVVMViewClass_EvaluateSource & NewBinding = ViewExtension - > EvaluateSources . AddDefaulted_GetRef ( ) ;
NewBinding . ParentFieldId = FFieldNotificationId ( CompiledFieldId - > GetName ( ) ) ;
NewBinding . ParentSource = FMVVMViewClass_SourceKey ( ViewExtensionParentCreatorsIndex ) ;
NewBinding . ToEvaluate = FMVVMViewClass_SourceKey ( ViewExtensionSourceCreatorsIndex ) ;
2023-11-17 14:09:15 -05:00
2023-12-01 11:13:35 -05:00
FMVVMViewClass_Source & ClassSource = ViewExtension - > Sources [ ViewExtensionParentCreatorsIndex ] ;
ClassSource . FieldToRegisterTo . AddUnique ( FMVVMViewClass_FieldId ( * CompiledFieldId ) ) ;
2023-11-17 14:09:15 -05:00
2023-12-01 11:13:35 -05:00
ClassSource . Flags | = ( uint16 ) FMVVMViewClass_Source : : EFlags : : HasEvaluatedBindings ;
2023-11-17 14:09:15 -05:00
}
2023-12-01 11:13:35 -05:00
ViewExtension - > EvaluateSources . StableSort ( [ ] ( const FMVVMViewClass_EvaluateSource & A , const FMVVMViewClass_EvaluateSource & B )
2023-09-28 14:01:37 -04:00
{
2023-12-01 11:13:35 -05:00
if ( A . GetParentSource ( ) = = B . GetParentSource ( ) )
2023-09-28 14:01:37 -04:00
{
2023-12-01 11:13:35 -05:00
return A . GetFieldId ( ) . GetFieldName ( ) . Compare ( B . GetFieldId ( ) . GetFieldName ( ) ) < 0 ;
2023-11-17 14:09:15 -05:00
}
2023-12-01 11:13:35 -05:00
return A . GetParentSource ( ) . GetIndex ( ) < B . GetParentSource ( ) . GetIndex ( ) ;
} ) ;
2023-08-21 08:46:45 -04:00
}
2023-11-16 23:04:14 -05:00
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : PreCompileEvents ( UWidgetBlueprintGeneratedClass * Class )
{
2023-08-23 08:39:32 -04:00
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowBindingEvent & & BlueprintView - > GetEvents ( ) . Num ( ) > 0 )
{
WidgetBlueprintCompilerContext . MessageLog . Warning ( * LOCTEXT ( " EventsAreNotAllowed " , " Binding events are not allowed in your project settings. " ) . ToString ( ) ) ;
}
2023-11-17 14:09:15 -05:00
for ( TSharedRef < FCompilerEvent > & ValidEvent : ValidEvents )
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
UMVVMBlueprintViewEvent * EventPtr = ValidEvent - > Event . Get ( ) ;
check ( EventPtr ) ;
2023-11-16 23:04:14 -05:00
UEdGraph * GeneratedGraph = EventPtr - > GetOrCreateWrapperGraph ( ) ;
2023-11-17 14:09:15 -05:00
check ( GeneratedGraph ) ;
2023-11-16 23:04:14 -05:00
// Does it resolve and are the field allowed
2023-11-17 14:09:15 -05:00
TValueOrError < FCreateFieldsResult , FText > EventPathResult = CreateFieldContext ( Class , EventPtr - > GetEventPath ( ) , true ) ;
if ( EventPathResult . HasError ( ) )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
AddMessageForEvent ( EventPtr
, FText : : Format ( Private : : PropertyPathIsInvalidFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , EventPtr - > GetEventPath ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-11-16 23:04:14 -05:00
continue ;
}
2023-08-21 08:46:45 -04:00
//Does EventPath resolves to a MulticastDelegateProperty
2023-11-17 14:09:15 -05:00
const FMulticastDelegateProperty * DelegateProperty = EventPathResult . GetValue ( ) . SkeletalGeneratedFields . Last ( ) . IsProperty ( ) ? CastField < const FMulticastDelegateProperty > ( EventPathResult . GetValue ( ) . SkeletalGeneratedFields . Last ( ) . GetProperty ( ) ) : nullptr ;
2023-08-21 08:46:45 -04:00
if ( DelegateProperty = = nullptr )
{
AddMessageForEvent ( EventPtr
2023-11-17 14:09:15 -05:00
, FText : : Format ( LOCTEXT ( " EventPathIsNotMulticastDelegate " , " The event {0} is not a multicast delegate. " ) , PropertyPathToText ( Class , BlueprintView . Get ( ) , EventPtr - > GetEventPath ( ) ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-08-21 08:46:45 -04:00
continue ;
}
// Does it still have the same signature
if ( ! UE : : MVVM : : FunctionGraphHelper : : IsFunctionEntryMatchSignature ( GeneratedGraph , DelegateProperty - > SignatureFunction ) )
{
AddMessageForEvent ( EventPtr
, FText : : Format ( LOCTEXT ( " EventPathFunctionSignatureError " , " The event {0} doesn't match the function signature. " ) , DelegateProperty - > GetDisplayNameText ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-08-21 08:46:45 -04:00
continue ;
}
// Generate the FieldPath to get the delegate property at runtime
2023-11-17 14:09:15 -05:00
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > EventFieldPathResult = BindingLibraryCompiler . AddFieldPath ( EventPathResult . GetValue ( ) . SkeletalGeneratedFields , true ) ;
if ( EventFieldPathResult . HasError ( ) )
2023-08-21 08:46:45 -04:00
{
AddMessageForEvent ( EventPtr
2023-11-17 14:09:15 -05:00
, FText : : Format ( Private : : CouldNotCreateSourceFieldPathFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , EventPtr - > GetEventPath ( ) ) , EventFieldPathResult . GetError ( ) )
2024-03-26 02:39:40 -04:00
, Compiler : : EMessageType : : Error
2023-12-19 11:31:33 -05:00
, FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsPreCompileStepValid = false ;
2023-08-21 08:46:45 -04:00
continue ;
}
2023-11-17 14:09:15 -05:00
//todo ? this doesn't support long path?
2023-08-21 08:46:45 -04:00
FName SourceName ;
2023-10-20 10:45:57 -04:00
switch ( EventPtr - > GetEventPath ( ) . GetSource ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) )
{
case EMVVMBlueprintFieldPathSource : : SelfContext :
SourceName = WidgetBlueprintCompilerContext . WidgetBlueprint ( ) - > GetFName ( ) ;
break ;
case EMVVMBlueprintFieldPathSource : : Widget :
{
FName WidgetName = EventPtr - > GetEventPath ( ) . GetWidgetName ( ) ;
checkf ( ! WidgetName . IsNone ( ) , TEXT ( " The destination should have been checked and set bAreSourceContextsValid. " ) ) ;
const bool bSourceIsUserWidget = WidgetName = = Class - > ClassGeneratedBy - > GetFName ( ) ;
ensure ( ! bSourceIsUserWidget ) ;
2023-11-17 14:09:15 -05:00
SourceName = WidgetName ;
2023-10-20 10:45:57 -04:00
break ;
}
case EMVVMBlueprintFieldPathSource : : ViewModel :
2023-08-21 08:46:45 -04:00
{
const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( EventPtr - > GetEventPath ( ) . GetViewModelId ( ) ) ;
check ( SourceViewModelContext ) ;
FName ViewModelName = SourceViewModelContext - > GetViewModelName ( ) ;
2023-11-17 14:09:15 -05:00
SourceName = ViewModelName ;
2023-10-20 10:45:57 -04:00
break ;
2023-08-21 08:46:45 -04:00
}
2023-10-20 10:45:57 -04:00
default :
ensureAlwaysMsgf ( false , TEXT ( " An EMVVMBlueprintFieldPathSource case was not checked. " ) ) ;
2023-08-21 08:46:45 -04:00
}
// No need to add the generated function to the field compiler.
//They are in the BP generated code.
2023-11-17 14:09:15 -05:00
ValidEvent - > DelegateFieldPathHandle = EventFieldPathResult . StealValue ( ) ;
ValidEvent - > GeneratedGraphName = EventPtr - > GetWrapperGraphName ( ) ;
ValidEvent - > SourceName = SourceName ;
2023-08-21 08:46:45 -04:00
}
}
2023-11-17 14:09:15 -05:00
void FMVVMViewBlueprintCompiler : : CompileEvents ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
for ( const TSharedRef < FCompilerEvent > & ValidEvent : ValidEvents )
2023-08-21 08:46:45 -04:00
{
2023-11-17 14:09:15 -05:00
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( ValidEvent - > DelegateFieldPathHandle ) ;
2023-08-21 08:46:45 -04:00
if ( CompiledFieldPath = = nullptr )
{
2024-03-26 02:39:40 -04:00
AddMessageForEvent ( ValidEvent - > Event . Get ( ) , LOCTEXT ( " CompiledEventFieldPathNotGenerated " , " Could not generate the event path. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
2023-08-21 08:46:45 -04:00
continue ;
}
2023-11-17 14:09:15 -05:00
if ( ValidEvent - > GeneratedGraphName . IsNone ( ) | | Class - > FindFunctionByName ( ValidEvent - > GeneratedGraphName ) = = nullptr )
2023-08-21 08:46:45 -04:00
{
2024-03-26 02:39:40 -04:00
AddMessageForEvent ( ValidEvent - > Event . Get ( ) , LOCTEXT ( " CompiledEventFieldPathNotGenerated " , " Could not generate the event path. " ) , Compiler : : EMessageType : : Error , FMVVMBlueprintPinId ( ) ) ;
2023-11-17 14:09:15 -05:00
bIsCompileStepValid = false ;
2023-08-21 08:46:45 -04:00
continue ;
}
2023-12-01 11:13:35 -05:00
int32 FoundSourceIndex = INDEX_NONE ;
if ( ! ValidEvent - > SourceName . IsNone ( ) )
{
FoundSourceIndex = ViewExtension - > Sources . IndexOfByPredicate ( [ ToFind = ValidEvent - > SourceName ] ( const FMVVMViewClass_Source & Other )
{
return Other . GetName ( ) = = ToFind ;
} ) ;
}
FMVVMViewClass_Event & NewBinding = ViewExtension - > Events . AddDefaulted_GetRef ( ) ;
2023-08-21 08:46:45 -04:00
NewBinding . FieldPath = * CompiledFieldPath ;
2023-12-01 11:13:35 -05:00
NewBinding . UserWidgetFunctionName = ValidEvent - > GeneratedGraphName ;
NewBinding . SourceToReevaluate = FoundSourceIndex ! = INDEX_NONE ? FMVVMViewClass_SourceKey ( FoundSourceIndex ) : FMVVMViewClass_SourceKey ( ) ;
}
}
2024-03-26 02:39:40 -04:00
void FMVVMViewBlueprintCompiler : : PreCompileViewExtensions ( UWidgetBlueprintGeneratedClass * Class )
{
struct FExposedCompiler : Compiler : : IMVVMBlueprintViewPrecompile
{
FExposedCompiler ( FMVVMViewBlueprintCompiler * InSelf , UWidgetBlueprintGeneratedClass * InClass )
: Self ( InSelf ) ,
Class ( InClass )
{ }
virtual const UMVVMBlueprintView * GetBlueprintView ( ) const override
{
return Self - > BlueprintView . Get ( ) ;
}
virtual const TMap < FName , UWidget * > & GetWidgetNameToWidgetPointerMap ( ) const override
{
return Self - > WidgetNameToWidgetPointerMap ;
}
virtual TArray < Compiler : : FCompilerBindingHandle > GetAllBindings ( ) const override
{
TArray < Compiler : : FCompilerBindingHandle > BindingHandles ;
for ( const TSharedRef < FCompilerBinding > & Binding : Self - > ValidBindings )
{
BindingHandles . Add ( Binding - > CompilerBindingHandle ) ;
}
return BindingHandles ;
}
virtual TArray < TArray < UE : : MVVM : : FMVVMConstFieldVariant > > GetBindingReadFields ( Compiler : : FCompilerBindingHandle BindingHandle ) override
{
TArray < TArray < UE : : MVVM : : FMVVMConstFieldVariant > > BindingReadFields ;
const TSharedRef < FCompilerBinding > * Binding = Self - > ValidBindings . FindByPredicate ( [ BindingHandle ] ( const TSharedRef < FCompilerBinding > & OtherBinding ) { return OtherBinding - > CompilerBindingHandle = = BindingHandle ; } ) ;
if ( Binding )
{
for ( const TSharedPtr < FGeneratedReadFieldPathContext > & ReadPath : Binding - > Get ( ) . ReadPaths )
{
BindingReadFields . Add ( ReadPath - > SkeletalGeneratedFields ) ;
}
}
return BindingReadFields ;
}
virtual TArray < UE : : MVVM : : FMVVMConstFieldVariant > GetBindingWriteFields ( Compiler : : FCompilerBindingHandle BindingHandle ) override
{
const TSharedRef < FCompilerBinding > * Binding = Self - > ValidBindings . FindByPredicate ( [ BindingHandle ] ( const TSharedRef < FCompilerBinding > & OtherBinding ) { return OtherBinding - > CompilerBindingHandle = = BindingHandle ; } ) ;
if ( Binding )
{
if ( Binding - > Get ( ) . WritePath )
{
return Binding - > Get ( ) . WritePath - > SkeletalGeneratedFields ;
}
}
return TArray < UE : : MVVM : : FMVVMConstFieldVariant > ( ) ;
}
virtual const FProperty * GetBindingSourceProperty ( Compiler : : FCompilerBindingHandle BindingHandle ) override
{
const TSharedRef < FCompilerBinding > * Binding = Self - > ValidBindings . FindByPredicate ( [ BindingHandle ] ( const TSharedRef < FCompilerBinding > & OtherBinding ) { return OtherBinding - > CompilerBindingHandle = = BindingHandle ; } ) ;
if ( Binding )
{
if ( Binding - > Get ( ) . Type = = FCompilerBinding : : EType : : ComplexConversionFunction
| | Binding - > Get ( ) . Type = = FCompilerBinding : : EType : : SimpleConversionFunction )
{
if ( const UFunction * CompiledFunction = Binding - > Get ( ) . ConversionFunction - > GetCompiledFunction ( Class ) )
{
TValueOrError < const FProperty * , FText > ReturnProperty = UE : : MVVM : : BindingHelper : : TryGetReturnTypeForConversionFunction ( CompiledFunction ) ;
if ( ReturnProperty . HasValue ( ) )
{
return ReturnProperty . GetValue ( ) ;
}
}
}
else if ( Binding - > Get ( ) . Type = = FCompilerBinding : : EType : : Assignment )
{
if ( Binding - > Get ( ) . ReadPaths . Num ( ) > 0 )
{
if ( const TSharedPtr < FGeneratedReadFieldPathContext > & ReadPath = Binding - > Get ( ) . ReadPaths [ 0 ] )
{
if ( ReadPath - > SkeletalGeneratedFields . Num ( ) > 0 )
{
UE : : MVVM : : FMVVMConstFieldVariant LastSourceField = ReadPath - > SkeletalGeneratedFields . Last ( ) ;
if ( LastSourceField . IsProperty ( ) )
{
return LastSourceField . GetProperty ( ) ;
}
}
}
}
}
}
return nullptr ;
}
virtual TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > AddObjectFieldPath ( const UE : : MVVM : : Compiler : : IMVVMBlueprintViewPrecompile : : FObjectFieldPathArgs & Args ) override
{
return UE : : MVVM : : Private : : AddObjectFieldPath ( Self - > BindingLibraryCompiler , Args . Class , Args . ObjectPath , Args . ExpectedType ) ;
}
virtual TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > AddFieldPath ( TArrayView < const FMVVMConstFieldVariant > InFieldPath , bool bInRead ) override
{
return Self - > BindingLibraryCompiler . AddFieldPath ( InFieldPath , bInRead ) ;
}
virtual void AddMessageForBinding ( Compiler : : FCompilerBindingHandle BindingHandle , const FText & MessageText , Compiler : : EMessageType MessageType ) const override
{
const TSharedRef < FCompilerBinding > * Binding = Self - > ValidBindings . FindByPredicate ( [ BindingHandle ] ( const TSharedRef < FCompilerBinding > & OtherBinding ) { return OtherBinding - > CompilerBindingHandle = = BindingHandle ; } ) ;
if ( Binding )
{
Self - > AddMessageForBinding ( * Binding , MessageText , MessageType , FMVVMBlueprintPinId ( ) ) ;
}
}
virtual void AddMessage ( const FText & MessageText , Compiler : : EMessageType MessageType ) override
{
Self - > AddMessage ( MessageText , MessageType ) ;
}
virtual void MarkPrecompileStepInvalid ( ) override
{
Self - > bIsPreCompileStepValid = false ;
}
virtual ~ FExposedCompiler ( ) { } ;
private :
FMVVMViewBlueprintCompiler * Self = nullptr ;
UWidgetBlueprintGeneratedClass * Class = nullptr ;
} ;
FExposedCompiler ExposedCompiler ( this , Class ) ;
for ( FMVVMExtensionItem & Extension : BlueprintView - > GetOuterUMVVMWidgetBlueprintExtension_View ( ) - > BlueprintExtensions )
{
if ( ensure ( Extension . ExtensionObj ) )
{
Extension . ExtensionObj - > Precompile ( & ExposedCompiler , Class ) ;
}
}
}
void FMVVMViewBlueprintCompiler : : CompileViewExtensions ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
{
struct FExposedCompiler : Compiler : : IMVVMBlueprintViewCompile
{
FExposedCompiler ( FMVVMViewBlueprintCompiler * InSelf , const FCompiledBindingLibraryCompiler : : FCompileResult & InCompileResult , UMVVMViewClass * InViewExtension )
: Self ( InSelf )
, CompileResult ( InCompileResult )
, ViewClass ( InViewExtension )
{ }
virtual const UMVVMBlueprintView * GetBlueprintView ( ) const override
{
return Self - > BlueprintView . Get ( ) ;
}
virtual const TMap < FName , UWidget * > & GetWidgetNameToWidgetPointerMap ( ) const override
{
return Self - > WidgetNameToWidgetPointerMap ;
}
virtual TValueOrError < FMVVMVCompiledFieldPath , void > GetFieldPath ( FCompiledBindingLibraryCompiler : : FFieldPathHandle FieldPath ) override
{
if ( FMVVMVCompiledFieldPath * CompiledPath = CompileResult . FieldPaths . Find ( FieldPath ) )
{
return MakeValue ( * CompiledPath ) ;
}
else
{
return MakeError ( ) ;
}
}
virtual void AddMessageForBinding ( Compiler : : FCompilerBindingHandle BindingHandle , const FText & MessageText , Compiler : : EMessageType MessageType ) const override
{
const TSharedRef < FCompilerBinding > * Binding = Self - > ValidBindings . FindByPredicate ( [ BindingHandle ] ( const TSharedRef < FCompilerBinding > & OtherBinding ) { return OtherBinding - > CompilerBindingHandle = = BindingHandle ; } ) ;
if ( Binding )
{
Self - > AddMessageForBinding ( * Binding , MessageText , MessageType , FMVVMBlueprintPinId ( ) ) ;
}
}
virtual void AddMessage ( const FText & MessageText , Compiler : : EMessageType MessageType ) override
{
Self - > AddMessage ( MessageText , MessageType ) ;
}
virtual void MarkCompileStepInvalid ( ) override
{
Self - > bIsCompileStepValid = false ;
}
virtual UMVVMViewClassExtension * CreateViewClassExtension ( TSubclassOf < UMVVMViewClassExtension > ExtensionClass )
{
return Self - > CreateViewClassExtension ( ExtensionClass , ViewClass ) ;
}
virtual ~ FExposedCompiler ( ) { } ;
private :
FMVVMViewBlueprintCompiler * Self = nullptr ;
FCompiledBindingLibraryCompiler : : FCompileResult CompileResult ;
UMVVMViewClass * ViewClass = nullptr ;
} ;
FExposedCompiler ExposedCompiler ( this , CompileResult , ViewExtension ) ;
for ( FMVVMExtensionItem & Extension : BlueprintView - > GetOuterUMVVMWidgetBlueprintExtension_View ( ) - > BlueprintExtensions )
{
if ( ensure ( Extension . ExtensionObj ) )
{
Extension . ExtensionObj - > Compile ( & ExposedCompiler , Class , ViewExtension ) ;
}
}
}
2023-12-01 11:13:35 -05:00
2024-02-15 15:40:59 -05:00
void FMVVMViewBlueprintCompiler : : PreCompileSourceDependencies ( UWidgetBlueprintGeneratedClass * Class )
{
//i. ViewmodelA.ViewmodelB = ViewmodelC.Value -> ViewmodelA needs to init before ViewmodelA_ViewmodelB, ViewmodelA before ViewmodelC, ViewmodelC before ViewmodelA_ViewmodelB
//ii. ViewmodelA.ViewmodelB.Value = ViewmodelC.Value -> ViewmodelA needs to init before ViewmodelA_ViewmodelB, ViewmodelA before ViewmodelC, ViewmodelA_ViewmodelB before ViewmodelC
// Dynamic sources depends on their parent. (i and ii ViewmodelA before ViewmodelA_ViewmodelB)
for ( const TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > & DynamicContext : SourceViewModelDynamicCreatorContexts )
{
ensure ( DynamicContext - > Source ) ;
ensure ( DynamicContext - > ParentSource ) ;
DynamicContext - > Source - > Dependencies . AddUnique ( DynamicContext - > ParentSource ) ;
}
for ( const TSharedRef < FCompilerBinding > & ValidBinding : ValidBindings )
{
if ( ValidBinding - > WritePath )
{
for ( const TSharedPtr < FGeneratedReadFieldPathContext > & ReadPath : ValidBinding - > ReadPaths )
{
// All the read depends on the write (i and ii ViewmodelA before ViewmodelC)
if ( ensure ( ReadPath - > Source ) )
{
if ( ValidBinding - > WritePath - > OptionalSource )
{
ReadPath - > Source - > Dependencies . AddUnique ( ValidBinding - > WritePath - > OptionalSource ) ;
}
// The write depends on the read (i ViewmodelC before ViewmodelA_ViewmodelB)
if ( ValidBinding - > WritePath - > OptionalDependencySource )
{
ValidBinding - > WritePath - > OptionalDependencySource - > Dependencies . AddUnique ( ReadPath - > Source ) ;
}
}
// NB (ii ViewmodelA_ViewmodelB before ViewmodelC) is implicit
}
}
}
}
2023-12-01 11:13:35 -05:00
void FMVVMViewBlueprintCompiler : : SortSourceFields ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMViewClass * ViewExtension )
{
for ( FMVVMViewClass_Source & Source : ViewExtension - > Sources )
{
for ( const FMVVMViewClass_FieldId & Field : Source . FieldToRegisterTo )
{
if ( Field . GetFieldId ( ) . GetIndex ( ) = = INDEX_NONE )
{
2024-03-26 02:39:40 -04:00
AddMessage ( LOCTEXT ( " SortSourceFieldsInvalidId " , " Internal error. The id is invalid. " ) , Compiler : : EMessageType : : Error ) ;
2023-12-01 11:13:35 -05:00
bIsCompileStepValid = false ;
continue ;
}
}
Source . FieldToRegisterTo . StableSort ( [ ] ( const FMVVMViewClass_FieldId & A , const FMVVMViewClass_FieldId & B )
{
return A . GetFieldId ( ) . GetIndex ( ) < B . GetFieldId ( ) . GetIndex ( ) ;
} ) ;
2023-08-21 08:46:45 -04:00
}
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
TValueOrError < FMVVMViewBlueprintCompiler : : FGetFieldsResult , FText > FMVVMViewBlueprintCompiler : : GetFields ( const UWidgetBlueprintGeneratedClass * Class , const FMVVMBlueprintPropertyPath & PropertyPath ) const
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
FGetFieldsResult Result ;
2023-11-16 23:04:14 -05:00
if ( ! PropertyPath . IsValid ( ) )
{
2023-11-17 14:09:15 -05:00
ensureAlwaysMsgf ( false , TEXT ( " Empty property path found. It should have been catch before. " ) ) ;
return MakeError ( FText : : GetEmpty ( ) ) ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
auto FindSource = [ Self = this ] ( const FName PropertyName , FCompilerBindingSource : : EType ExpectedType ) - > TValueOrError < TSharedPtr < FCompilerBindingSource > , FText >
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
const TSharedRef < FCompilerBindingSource > * Found = Self - > NeededBindingSources . FindByPredicate ( [ PropertyName ] ( const TSharedRef < FCompilerBindingSource > & Other ) { return Other - > Name = = PropertyName ; } ) ;
if ( Found )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
if ( ( * Found ) - > Type ! = ExpectedType )
2023-11-16 23:04:14 -05:00
{
2023-11-17 14:09:15 -05:00
return MakeError ( LOCTEXT ( " NotExpectedSourceType " , " Internal error. The source of the path is not of the expected type. " ) ) ;
2023-11-16 23:04:14 -05:00
}
2023-11-17 14:09:15 -05:00
return MakeValue ( * Found ) ;
2023-11-16 23:04:14 -05:00
}
2024-02-15 15:40:59 -05:00
// It can be valid to not have a BindingSource, if it's a widget in a WritePath
2023-11-17 14:09:15 -05:00
return MakeValue ( TSharedPtr < FCompilerBindingSource > ( ) ) ;
} ;
2022-06-08 13:25:42 -04:00
2023-10-20 10:45:57 -04:00
switch ( PropertyPath . GetSource ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) )
{
case EMVVMBlueprintFieldPathSource : : ViewModel :
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
const FCompilerViewModelCreatorContext * FoundViewModelCreator = ViewModelCreatorContexts . FindByPredicate ( [ ViewModelId = PropertyPath . GetViewModelId ( ) ] ( const FCompilerViewModelCreatorContext & Other ) { return Other . ViewModelContext . GetViewModelId ( ) = = ViewModelId ; } ) ;
check ( FoundViewModelCreator ) ;
const FName PropertyName = FoundViewModelCreator - > ViewModelContext . GetViewModelName ( ) ;
const FCompilerUserWidgetProperty * FoundUserWidgetProperty = NeededUserWidgetProperties . FindByPredicate ( [ PropertyName ] ( const FCompilerUserWidgetProperty & Other ) { return Other . Name = = PropertyName ; } ) ;
check ( FoundUserWidgetProperty ) ;
check ( FoundViewModelCreator - > ViewModelContext . GetViewModelClass ( ) ) ;
check ( FoundUserWidgetProperty - > AuthoritativeClass = = FoundViewModelCreator - > ViewModelContext . GetViewModelClass ( ) ) ;
2022-06-08 13:25:42 -04:00
2023-11-17 14:09:15 -05:00
TValueOrError < TSharedPtr < FCompilerBindingSource > , FText > FindSourceResult = FindSource ( PropertyName , FCompilerBindingSource : : EType : : ViewModel ) ;
if ( FindSourceResult . HasError ( ) )
{
return MakeError ( FindSourceResult . StealError ( ) ) ;
}
if ( ! FindSourceResult . GetValue ( ) . IsValid ( ) )
{
return MakeError ( LOCTEXT ( " ViewModelShouldHaveSource " , " Internal error. Viewmodel should have a source. " ) ) ;
}
2024-02-15 15:40:59 -05:00
Result . GeneratedFields = AppendBaseField ( Class , PropertyName , PropertyPath . GetFields ( Class ) ) ;
Result . OptionalSource = FindSourceResult . StealValue ( ) ;
2023-11-17 14:09:15 -05:00
break ;
2022-06-08 13:25:42 -04:00
}
2023-10-20 10:45:57 -04:00
case EMVVMBlueprintFieldPathSource : : SelfContext :
{
2023-11-17 14:09:15 -05:00
TValueOrError < TSharedPtr < FCompilerBindingSource > , FText > FindSourceResult = FindSource ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) - > GetFName ( ) , FCompilerBindingSource : : EType : : Self ) ;
if ( FindSourceResult . HasError ( ) )
{
return MakeError ( FindSourceResult . StealError ( ) ) ;
}
if ( ! FindSourceResult . GetValue ( ) . IsValid ( ) )
{
return MakeError ( LOCTEXT ( " WidgetBlueprintShouldHaveSource " , " Internal error. The blueprint should have a source. " ) ) ;
}
2024-02-15 15:40:59 -05:00
Result . GeneratedFields = AppendBaseField ( Class , FName ( ) , PropertyPath . GetFields ( Class ) ) ;
Result . OptionalSource = FindSourceResult . StealValue ( ) ;
2023-11-17 14:09:15 -05:00
break ;
2023-10-20 10:45:57 -04:00
}
case EMVVMBlueprintFieldPathSource : : Widget :
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
FName DestinationWidgetName = PropertyPath . GetWidgetName ( ) ;
check ( WidgetNameToWidgetPointerMap . Contains ( DestinationWidgetName ) ) ;
checkf ( ! DestinationWidgetName . IsNone ( ) , TEXT ( " The destination should have been checked and set bAreSourceContextsValid. " ) ) ;
2023-10-20 10:45:57 -04:00
2023-11-17 14:09:15 -05:00
const FCompilerUserWidgetProperty * FoundUserWidgetProperty = NeededUserWidgetProperties . FindByPredicate ( [ DestinationWidgetName ] ( const FCompilerUserWidgetProperty & Other ) { return Other . Name = = DestinationWidgetName ; } ) ;
check ( FoundUserWidgetProperty ) ;
TValueOrError < TSharedPtr < FCompilerBindingSource > , FText > FindSourceResult = FindSource ( DestinationWidgetName , FCompilerBindingSource : : EType : : Widget ) ;
if ( FindSourceResult . HasError ( ) )
2022-06-08 13:25:42 -04:00
{
2023-11-17 14:09:15 -05:00
return MakeError ( FindSourceResult . StealError ( ) ) ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
2024-02-15 15:40:59 -05:00
Result . GeneratedFields = AppendBaseField ( Class , DestinationWidgetName , PropertyPath . GetFields ( Class ) ) ;
Result . OptionalSource = FindSourceResult . StealValue ( ) ;
2023-11-17 14:09:15 -05:00
break ;
2022-06-08 13:25:42 -04:00
}
2023-10-20 10:45:57 -04:00
default :
2024-02-15 15:40:59 -05:00
ensureAlwaysMsgf ( false , TEXT ( " Not supported. " ) ) ;
Result . GeneratedFields = AppendBaseField ( Class , FName ( ) , PropertyPath . GetFields ( Class ) ) ;
2023-11-17 14:09:15 -05:00
break ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
return MakeValue ( MoveTemp ( Result ) ) ;
2022-06-08 13:25:42 -04:00
}
2023-11-17 14:09:15 -05:00
2024-02-15 15:40:59 -05:00
TArray < FMVVMConstFieldVariant > FMVVMViewBlueprintCompiler : : AppendBaseField ( const UClass * Class , FName PropertyName , TArray < FMVVMConstFieldVariant > Properties )
2022-06-08 13:25:42 -04:00
{
if ( PropertyName . IsNone ( ) )
{
return Properties ;
}
check ( Class ) ;
FMVVMConstFieldVariant NewProperty = BindingHelper : : FindFieldByName ( Class , FMVVMBindingName ( PropertyName ) ) ;
Properties . Insert ( NewProperty , 0 ) ;
return Properties ;
}
2023-11-17 14:09:15 -05:00
TValueOrError < FMVVMViewBlueprintCompiler : : FCreateFieldsResult , FText > FMVVMViewBlueprintCompiler : : CreateFieldContext ( const UWidgetBlueprintGeneratedClass * Class , const FMVVMBlueprintPropertyPath & PropertyPath , bool bForSourceReading ) const
{
FMVVMViewBlueprintCompiler : : FCreateFieldsResult Result ;
// Evaluate the getter/setter path.
{
2024-02-15 15:40:59 -05:00
TValueOrError < FGetFieldsResult , FText > GetFieldResult = GetFields ( Class , PropertyPath ) ;
if ( GetFieldResult . HasError ( ) )
{
return MakeError ( GetFieldResult . StealError ( ) ) ;
}
2023-11-17 14:09:15 -05:00
2024-02-15 15:40:59 -05:00
Result . OptionalSource = MoveTemp ( GetFieldResult . GetValue ( ) . OptionalSource ) ;
Result . GeneratedFields = MoveTemp ( GetFieldResult . GetValue ( ) . GeneratedFields ) ;
}
2023-11-17 14:09:15 -05:00
if ( ! IsPropertyPathValid ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , Result . GeneratedFields ) )
{
return MakeError ( FText : : Format ( Private : : PropertyPathIsInvalidFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , PropertyPath ) ) ) ;
}
// Generate the path with property converted to BP function
TValueOrError < TArray < FMVVMConstFieldVariant > , FText > SkeletalGeneratedFieldsResult = FieldPathHelper : : GenerateFieldPathList ( Result . GeneratedFields , bForSourceReading ) ;
2024-02-08 13:38:56 -05:00
if ( SkeletalGeneratedFieldsResult . HasError ( ) )
2023-11-17 14:09:15 -05:00
{
return MakeError ( FText : : Format ( Private : : CouldNotCreateSourceFieldPathFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , PropertyPath ) , SkeletalGeneratedFieldsResult . GetError ( ) ) ) ;
}
2024-02-08 13:38:56 -05:00
if ( ! IsPropertyPathValid ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , SkeletalGeneratedFieldsResult . GetValue ( ) ) )
{
return MakeError ( FText : : Format ( Private : : CouldNotCreateSourceFieldPathFormat , PropertyPathToText ( Class , BlueprintView . Get ( ) , PropertyPath ) , LOCTEXT ( " NotAValidPropertyPath " , " The path is not valid. " ) ) ) ;
}
2023-11-17 14:09:15 -05:00
Result . SkeletalGeneratedFields = SkeletalGeneratedFieldsResult . StealValue ( ) ;
return MakeValue ( Result ) ;
}
TValueOrError < TSharedPtr < FMVVMViewBlueprintCompiler : : FCompilerNotifyFieldId > , FText > FMVVMViewBlueprintCompiler : : CreateNotifyFieldId ( const UWidgetBlueprintGeneratedClass * Class , const TSharedPtr < FGeneratedReadFieldPathContext > & ReadFieldContext , const FMVVMBlueprintViewBinding & Binding )
{
check ( ReadFieldContext - > SkeletalGeneratedFields . Num ( ) > 0 ) ;
// The path may contains another INotifyFieldValueChanged
TValueOrError < FieldPathHelper : : FParsedNotifyBindingInfo , FText > BindingInfoResult = FieldPathHelper : : GetNotifyBindingInfoFromFieldPath ( Class , ReadFieldContext - > SkeletalGeneratedFields ) ;
if ( BindingInfoResult . HasError ( ) )
{
return MakeError ( BindingInfoResult . StealError ( ) ) ;
}
const FieldPathHelper : : FParsedNotifyBindingInfo & BindingInfo = BindingInfoResult . GetValue ( ) ;
2024-02-15 15:40:59 -05:00
if ( ! BindingInfo . NotifyFieldId . IsValid ( ) | | ReadFieldContext - > Source = = nullptr )
2023-11-17 14:09:15 -05:00
{
return MakeValue ( TSharedPtr < FCompilerNotifyFieldId > ( ) ) ;
}
FCompilerNotifyFieldId Result ;
Result . NotificationId = BindingInfo . NotifyFieldId ;
2024-02-15 15:40:59 -05:00
Result . Source = ReadFieldContext - > Source ;
2023-11-17 14:09:15 -05:00
Result . ViewModelDynamic . Reset ( ) ;
auto GetClassFromField = [ ] ( UE : : MVVM : : FMVVMConstFieldVariant Field ) - > const UClass *
{
if ( Field . IsProperty ( ) )
{
const FObjectPropertyBase * ObjectProperty = CastField < const FObjectPropertyBase > ( Field . GetProperty ( ) ) ;
if ( ObjectProperty )
{
return ObjectProperty - > PropertyClass ;
}
}
else if ( Field . IsFunction ( ) )
{
const FObjectPropertyBase * ReturnValue = CastField < const FObjectPropertyBase > ( BindingHelper : : GetReturnProperty ( Field . GetFunction ( ) ) ) ;
if ( ReturnValue )
{
return ReturnValue - > PropertyClass ;
}
}
return nullptr ;
} ;
// Sanity check
{
const UClass * ExpectedClass = nullptr ;
if ( BindingInfo . ViewModelIndex < 1 & & BindingInfo . NotifyFieldClass )
{
2024-02-15 15:40:59 -05:00
ExpectedClass = ReadFieldContext - > Source - > AuthoritativeClass ;
2023-11-17 14:09:15 -05:00
}
else if ( BindingInfo . ViewModelIndex > = 0 )
{
ExpectedClass = GetClassFromField ( ReadFieldContext - > SkeletalGeneratedFields [ BindingInfo . ViewModelIndex ] ) ;
}
if ( ExpectedClass = = nullptr | | BindingInfo . NotifyFieldClass = = nullptr | | ! ExpectedClass - > IsChildOf ( BindingInfo . NotifyFieldClass ) )
{
return MakeError ( LOCTEXT ( " InvalidNotifyFieldClassInternal " , " Internal error. The viewmodel class doesn't matches. " ) ) ;
}
}
// The INotifyFieldValueChanged/viewmodel is not the first and only INotifyFieldValueChanged/viewmodel property path.
//Create a new source in PropertyPath creator mode. Create a special binding to update the viewmodel when it changes.
//This binding (calling this function) will use the new source.
if ( BindingInfo . ViewModelIndex > = 1 )
{
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowLongSourcePath )
{
return MakeError ( LOCTEXT ( " DynamicSourceEntryNotSupport " , " Long source entry is not supported. Add the viewmodel manually. " ) ) ;
}
for ( int32 DynamicIndex = 1 ; DynamicIndex < = BindingInfo . ViewModelIndex ; + + DynamicIndex )
{
if ( ! ReadFieldContext - > SkeletalGeneratedFields . IsValidIndex ( DynamicIndex ) )
{
return MakeError ( LOCTEXT ( " DynamicSourceEntryInternalIndex " , " Internal error. The source index is not valid. " ) ) ;
}
FName NewSourceName ;
FName NewParentSourceName ;
FString NewSourcePropertyPath ;
const UClass * NewSourceAuthoritativeClass = nullptr ;
{
TStringBuilder < 512 > PropertyPathBuilder ;
TStringBuilder < 512 > DynamicNameBuilder ;
for ( int32 Index = 0 ; Index < = DynamicIndex ; + + Index )
{
if ( Index > 0 )
{
PropertyPathBuilder < < TEXT ( ' . ' ) ;
DynamicNameBuilder < < TEXT ( ' _ ' ) ;
}
PropertyPathBuilder < < ReadFieldContext - > SkeletalGeneratedFields [ Index ] . GetName ( ) ;
DynamicNameBuilder < < ReadFieldContext - > SkeletalGeneratedFields [ Index ] . GetName ( ) ;
if ( Index = = DynamicIndex - 1 )
{
NewParentSourceName = FName ( DynamicNameBuilder . ToString ( ) ) ;
}
}
NewSourceName = FName ( DynamicNameBuilder . ToString ( ) ) ;
NewSourcePropertyPath = PropertyPathBuilder . ToString ( ) ;
const UClass * OwnerSkeletalClass = GetClassFromField ( ReadFieldContext - > SkeletalGeneratedFields [ DynamicIndex ] ) ;
if ( OwnerSkeletalClass = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_GeneratedFieldInvalid " , " Internal error. The GeneratedFiled is invalid. " ) ) ;
}
NewSourceAuthoritativeClass = OwnerSkeletalClass - > GetAuthoritativeClass ( ) ;
if ( NewSourceAuthoritativeClass = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_AuthoritativeClassInvalid " , " Internal error. No authoritative class. " ) ) ;
}
}
// Does the parent exist
TSharedPtr < FCompilerBindingSource > ParentBindingSource ;
{
const TSharedRef < FCompilerBindingSource > * FoundParentBindingSource = NeededBindingSources . FindByPredicate ( [ NewParentSourceName ] ( const TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Name = = NewParentSourceName ;
} ) ;
if ( FoundParentBindingSource = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_InvalidParentBindingSource " , " Internal error. Can't find the parent binding source. " ) ) ;
}
ParentBindingSource = * FoundParentBindingSource ;
}
// Did we already create the new source. It could be a dynamic or one added by the user
{
const TSharedRef < FCompilerBindingSource > * FoundBindingSource = NeededBindingSources . FindByPredicate ( [ NewSourceName ] ( const TSharedRef < FCompilerBindingSource > & Other )
{
return Other - > Name = = NewSourceName ;
} ) ;
if ( FoundBindingSource )
{
if ( ( * FoundBindingSource ) - > Type ! = FCompilerBindingSource : : EType : : DynamicViewmodel )
{
return MakeError ( LOCTEXT ( " DVM_ViewmodelOfSameName " , " The dynamic viewmodel cannot be added. There is already a source with that name. " ) ) ;
}
// is the class the same
if ( ! ( * FoundBindingSource ) - > AuthoritativeClass - > IsChildOf ( NewSourceAuthoritativeClass ) )
{
return MakeError ( LOCTEXT ( " DVM_ExistingNotSameClass " , " Internal error. The viewmodel was already added and is not the same type. " ) ) ;
}
}
const TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > * FoundViewModelDynamicCreatorContext = SourceViewModelDynamicCreatorContexts . FindByPredicate ( [ NewSourceName ] ( const TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > & Other )
{
return Other - > Source - > Name = = NewSourceName ;
} ) ;
if ( FoundViewModelDynamicCreatorContext )
{
if ( FoundBindingSource = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_BindingSourceShouldExist " , " Internal error. The binding source should exist. " ) ) ;
}
if ( ( * FoundViewModelDynamicCreatorContext ) - > Source ! = * FoundBindingSource )
{
return MakeError ( LOCTEXT ( " DVM_BindingSourceShouldBeTheSame " , " Internal error. The source should be the same. " ) ) ;
}
if ( ( * FoundViewModelDynamicCreatorContext ) - > ParentSource - > Name ! = NewParentSourceName )
{
return MakeError ( LOCTEXT ( " DVM_BindingSourceParentSameName " , " Internal error. The parent name should be the same. " ) ) ;
}
}
const FCompilerViewModelCreatorContext * FoundViewModelCreatorContext = ViewModelCreatorContexts . FindByPredicate ( [ NewSourceName ] ( const FCompilerViewModelCreatorContext & Other )
{
return Other . ViewModelContext . GetViewModelName ( ) = = NewSourceName ;
} ) ;
if ( FoundViewModelCreatorContext )
{
if ( FoundBindingSource = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_BindingSourceShouldExist " , " Internal error. The binding source should exist. " ) ) ;
}
if ( FoundViewModelCreatorContext - > Source ! = * FoundBindingSource )
{
return MakeError ( LOCTEXT ( " DVM_BindingSourceShouldBeTheSame " , " Internal error. The source should be the same. " ) ) ;
}
if ( FoundViewModelDynamicCreatorContext = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_DynamicCreatorContextShouldExit " , " Internal error. The creator context should exist. " ) ) ;
}
if ( FoundViewModelCreatorContext - > DynamicContext ! = * FoundViewModelDynamicCreatorContext )
{
return MakeError ( LOCTEXT ( " DVM_DynamicContextSHouldBeSame " , " Internal error. The creator context should be the same. " ) ) ;
}
if ( FoundViewModelCreatorContext - > ViewModelContext . CreationType ! = EMVVMBlueprintViewModelContextCreationType : : PropertyPath )
{
return MakeError ( LOCTEXT ( " DVM_DynamicContextShouldBePropertyPath " , " Internal error. The existing creator context should use a property path. " ) ) ;
}
if ( FoundViewModelCreatorContext - > ViewModelContext . ViewModelPropertyPath ! = NewSourcePropertyPath )
{
return MakeError ( LOCTEXT ( " DVM_DynamicContextSamePropertyPath " , " Internal error. The existing creator context use the same property path. " ) ) ;
}
}
if ( FoundBindingSource & & FoundViewModelCreatorContext = = nullptr & & FoundViewModelDynamicCreatorContext = = nullptr )
{
return MakeError ( LOCTEXT ( " DVM_MissingDefinition " , " Internal error. There are missing definition for the dynamic viewmodel. " ) ) ;
}
if ( FoundBindingSource )
{
Result . Source = * FoundBindingSource ;
Result . ViewModelDynamic = * FoundViewModelDynamicCreatorContext ;
continue ; // already exist and correct to use.
}
}
if ( ! NewSourceAuthoritativeClass - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) )
{
return MakeError ( LOCTEXT ( " DVM_NewDynamicNotViewmodel " , " The dynamic viewmodel is not an actual viewmodel. " ) ) ;
}
// Create the new source
TSharedRef < FCompilerBindingSource > NewBindingSource = MakeShared < FCompilerBindingSource > ( ) ;
NewBindingSource - > AuthoritativeClass = NewSourceAuthoritativeClass ;
NewBindingSource - > Name = NewSourceName ;
NewBindingSource - > Type = FCompilerBindingSource : : EType : : DynamicViewmodel ;
NewBindingSource - > bIsOptional = ParentBindingSource - > bIsOptional ;
NeededBindingSources . Add ( NewBindingSource ) ;
Result . Source = NewBindingSource ;
TSharedRef < FCompilerSourceViewModelDynamicCreatorContext > NewViewModelDynamic = MakeShared < FCompilerSourceViewModelDynamicCreatorContext > ( ) ;
NewViewModelDynamic - > Source = NewBindingSource ;
NewViewModelDynamic - > ParentSource = ParentBindingSource ;
NewViewModelDynamic - > NotificationId = FFieldNotificationId ( ReadFieldContext - > SkeletalGeneratedFields [ DynamicIndex ] . GetName ( ) ) ;
SourceViewModelDynamicCreatorContexts . Add ( NewViewModelDynamic ) ;
Result . ViewModelDynamic = NewViewModelDynamic ;
FCompilerViewModelCreatorContext & NewViewModelCreatorContext = ViewModelCreatorContexts . AddDefaulted_GetRef ( ) ;
NewViewModelCreatorContext . ViewModelContext = FMVVMBlueprintViewModelContext ( NewSourceAuthoritativeClass , NewSourceName ) ;
NewViewModelCreatorContext . ViewModelContext . bCreateSetterFunction = false ;
NewViewModelCreatorContext . ViewModelContext . bOptional = NewBindingSource - > bIsOptional ;
NewViewModelCreatorContext . ViewModelContext . CreationType = EMVVMBlueprintViewModelContextCreationType : : PropertyPath ;
NewViewModelCreatorContext . ViewModelContext . ViewModelPropertyPath = NewSourcePropertyPath ;
NewViewModelCreatorContext . Source = NewBindingSource ;
NewViewModelCreatorContext . DynamicContext = NewViewModelDynamic ;
}
}
return MakeValue ( MakeShared < FCompilerNotifyFieldId > ( Result ) ) ;
}
2024-03-26 02:39:40 -04:00
UMVVMViewClassExtension * FMVVMViewBlueprintCompiler : : CreateViewClassExtension ( TSubclassOf < UMVVMViewClassExtension > ExtensionClass , UMVVMViewClass * ViewClass )
{
if ( ensure ( ExtensionClass . Get ( ) ) )
{
UObject * ExtensionObj = NewObject < UObject > ( ViewClass , ExtensionClass . Get ( ) , NAME_None ) ;
UMVVMViewClassExtension * Extension = CastChecked < UMVVMViewClassExtension > ( ExtensionObj ) ;
ViewClass - > ViewClassExtensions . Add ( Extension ) ;
return Extension ;
}
return nullptr ;
}
2023-11-17 14:09:15 -05:00
2023-10-20 09:15:33 -04:00
bool FMVVMViewBlueprintCompiler : : IsPropertyPathValid ( const UBlueprint * Context , TArrayView < const FMVVMConstFieldVariant > PropertyPath )
2022-06-08 13:25:42 -04:00
{
2023-11-15 13:51:32 -05:00
const UStruct * CurrentContainer = Context - > GeneratedClass ? Context - > GeneratedClass : Context - > SkeletonGeneratedClass ;
2023-10-05 18:27:37 -04:00
int32 PathLength = PropertyPath . Num ( ) ;
for ( int32 Index = 0 ; Index < PathLength ; Index + + )
2022-06-08 13:25:42 -04:00
{
2023-10-05 18:27:37 -04:00
const FMVVMConstFieldVariant & Field = PropertyPath [ Index ] ;
2023-11-15 13:51:32 -05:00
if ( CurrentContainer = = nullptr )
{
return false ;
}
2022-06-08 13:25:42 -04:00
if ( Field . IsEmpty ( ) )
{
return false ;
}
2023-08-21 08:46:45 -04:00
if ( Field . IsProperty ( ) )
2022-06-08 13:25:42 -04:00
{
2023-08-21 08:46:45 -04:00
if ( Field . GetProperty ( ) = = nullptr )
{
return false ;
}
2023-10-05 18:27:37 -04:00
2023-11-15 13:51:32 -05:00
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsPropertyAllowed ( Context , CurrentContainer , Field . GetProperty ( ) ) )
2023-08-21 08:46:45 -04:00
{
return false ;
}
2022-06-08 13:25:42 -04:00
}
2023-08-21 08:46:45 -04:00
if ( Field . IsFunction ( ) )
2022-06-08 13:25:42 -04:00
{
2023-08-21 08:46:45 -04:00
if ( Field . GetFunction ( ) = = nullptr )
{
return false ;
}
2023-11-15 13:51:32 -05:00
const UClass * CurrentContainerAsClass = Cast < const UClass > ( CurrentContainer ) ;
if ( CurrentContainerAsClass = = nullptr )
{
return false ;
}
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsFunctionAllowed ( Context , CurrentContainerAsClass , Field . GetFunction ( ) ) )
2023-08-21 08:46:45 -04:00
{
return false ;
}
2022-06-08 13:25:42 -04:00
}
2023-11-15 13:51:32 -05:00
TValueOrError < const UStruct * , void > FieldAsContainerResult = UE : : MVVM : : FieldPathHelper : : GetFieldAsContainer ( Field ) ;
CurrentContainer = FieldAsContainerResult . HasValue ( ) ? FieldAsContainerResult . GetValue ( ) : nullptr ;
2022-06-08 13:25:42 -04:00
}
return true ;
}
2023-06-16 11:26:16 -04:00
bool FMVVMViewBlueprintCompiler : : CanBeSetInNative ( TArrayView < const FMVVMConstFieldVariant > PropertyPath )
{
for ( int32 Index = PropertyPath . Num ( ) - 1 ; Index > = 0 ; - - Index )
{
const FMVVMConstFieldVariant & Variant = PropertyPath [ Index ] ;
// Stop the algo if the path is already a function.
if ( Variant . IsFunction ( ) )
{
return true ;
}
2023-07-26 14:04:59 -04:00
2023-06-16 11:26:16 -04:00
// If the BP is defined in BP and has Net flags or FieldNotify flag, then the VaraibleSet K2Node need to be used to generate the proper byte-code.
if ( Variant . IsProperty ( ) )
{
// If it's an object then the path before the object doesn't matter.
if ( const FObjectPropertyBase * PropertyBase = CastField < const FObjectPropertyBase > ( Variant . GetProperty ( ) ) )
{
bool bLastPath = Index > = PropertyPath . Num ( ) - 1 ;
if ( ! bLastPath )
{
return true ;
}
}
if ( Cast < UBlueprintGeneratedClass > ( Variant . GetProperty ( ) - > GetOwnerStruct ( ) ) )
{
if ( Variant . GetProperty ( ) - > HasMetaData ( FName ( " FieldNotify " ) ) | | Variant . GetProperty ( ) - > HasAnyPropertyFlags ( CPF_Net ) )
{
return false ;
}
}
}
}
return true ;
}
2024-03-25 19:44:50 -04:00
TSharedRef < FMVVMViewBlueprintCompiler : : FGeneratedWriteFieldPathContext > FMVVMViewBlueprintCompiler : : MakeWriteFieldPath ( EMVVMBlueprintFieldPathSource GeneratedFrom , TArray < UE : : MVVM : : FMVVMConstFieldVariant > & & GeneratedFields , TArray < UE : : MVVM : : FMVVMConstFieldVariant > & & SkeletalGeneratedFields )
{
TSharedRef < FGeneratedWriteFieldPathContext > WriteFieldPath = MakeShared < FGeneratedWriteFieldPathContext > ( ) ;
//WriteFieldPath->OptionalSource;
WriteFieldPath - > GeneratedFields = MoveTemp ( GeneratedFields ) ;
WriteFieldPath - > SkeletalGeneratedFields = MoveTemp ( SkeletalGeneratedFields ) ;
WriteFieldPath - > GeneratedFrom = GeneratedFrom ;
WriteFieldPath - > bCanBeSetInNative = CanBeSetInNative ( WriteFieldPath - > SkeletalGeneratedFields ) ;
return WriteFieldPath ;
}
2023-10-20 09:15:33 -04:00
void FMVVMViewBlueprintCompiler : : TestGenerateSetter ( const UBlueprint * Context , FStringView ObjectName , FStringView FieldPath , FStringView FunctionName )
2023-06-16 11:26:16 -04:00
{
# if UE_BUILD_DEVELOPMENT || UE_BUILD_DEBUG
UWidgetBlueprint * WidgetBlueprint = nullptr ;
{
UObject * FoundObject = FindObject < UObject > ( nullptr , ObjectName . GetData ( ) , false ) ;
WidgetBlueprint = Cast < UWidgetBlueprint > ( FoundObject ) ;
}
if ( WidgetBlueprint = = nullptr )
{
return ;
}
UWidgetBlueprintGeneratedClass * NewSkeletonClass = Cast < UWidgetBlueprintGeneratedClass > ( WidgetBlueprint - > SkeletonGeneratedClass ) ;
TValueOrError < TArray < FMVVMConstFieldVariant > , FText > SkeletalSetterPathResult = FieldPathHelper : : GenerateFieldPathList ( NewSkeletonClass , FieldPath , false ) ;
if ( SkeletalSetterPathResult . HasError ( ) )
{
return ;
}
2023-10-20 09:15:33 -04:00
if ( ! IsPropertyPathValid ( Context , SkeletalSetterPathResult . GetValue ( ) ) )
2023-06-16 11:26:16 -04:00
{
return ;
}
UEdGraph * GeneratedSetterGraph = UE : : MVVM : : FunctionGraphHelper : : CreateFunctionGraph ( WidgetBlueprint , FunctionName , EFunctionFlags : : FUNC_None , TEXT ( " " ) , false ) ;
if ( GeneratedSetterGraph = = nullptr )
{
return ;
}
const FProperty * SetterProperty = nullptr ;
if ( SkeletalSetterPathResult . GetValue ( ) . Num ( ) > 0 & & ensure ( SkeletalSetterPathResult . GetValue ( ) . Last ( ) . IsProperty ( ) ) )
{
SetterProperty = SkeletalSetterPathResult . GetValue ( ) . Last ( ) . GetProperty ( ) ;
}
if ( SetterProperty = = nullptr )
{
return ;
}
UE : : MVVM : : FunctionGraphHelper : : AddFunctionArgument ( GeneratedSetterGraph , SetterProperty , " NewValue " ) ;
UE : : MVVM : : FunctionGraphHelper : : GenerateSetter ( WidgetBlueprint , GeneratedSetterGraph , SkeletalSetterPathResult . GetValue ( ) ) ;
# endif
}
2022-06-08 13:25:42 -04:00
} //namespace
2022-10-06 20:08:12 -04:00
# undef LOCTEXT_NAMESPACE