2022-06-08 13:25:42 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MVVMViewBlueprintCompiler.h"
2023-03-17 16:33:42 -04:00
# include "Blueprint/WidgetTree.h"
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"
2023-01-12 01:48:34 -05:00
# include "Components/Widget.h"
2023-02-17 15:28:46 -05:00
# include "HAL/IConsoleManager.h"
2022-06-08 13:25:42 -04:00
# include "MVVMBlueprintView.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-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-02-09 12:14:47 -05:00
# include "View/MVVMViewClass.h"
2023-02-11 12:38:02 -05:00
# include "View/MVVMViewModelContextResolver.h"
2022-06-08 13:25:42 -04:00
2022-10-06 20:08:12 -04:00
# define LOCTEXT_NAMESPACE "MVVMViewBlueprintCompiler"
2022-06-08 13:25:42 -04:00
namespace UE : : MVVM : : Private
{
2023-03-22 13:07:37 -04:00
FAutoConsoleVariable CVarLogViewCompliedResult (
TEXT ( " MVVM.LogViewCompliedResult " ) ,
false ,
TEXT ( " After the view is compiled log the compiled bindings and sources. " )
) ;
2023-03-16 13:02:33 -04:00
2022-10-06 20:08:12 -04:00
FString PropertyPathToString ( const UMVVMBlueprintView * BlueprintView , const FMVVMBlueprintPropertyPath & PropertyPath )
2022-06-08 13:25:42 -04:00
{
2022-10-06 20:08:12 -04:00
if ( PropertyPath . IsEmpty ( ) )
{
return FString ( ) ;
}
TStringBuilder < 512 > Result ;
if ( PropertyPath . IsFromViewModel ( ) )
{
if ( const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) )
{
Result < < SourceViewModelContext - > GetViewModelName ( ) ;
}
else
{
Result < < TEXT ( " <none> " ) ;
}
}
else if ( PropertyPath . IsFromWidget ( ) )
{
Result < < PropertyPath . GetWidgetName ( ) ;
}
else
{
Result < < TEXT ( " <none> " ) ;
}
FString BasePropertyPath = PropertyPath . GetBasePropertyPath ( ) ;
if ( BasePropertyPath . Len ( ) )
{
Result < < TEXT ( ' . ' ) ;
Result < < MoveTemp ( BasePropertyPath ) ;
}
return Result . ToString ( ) ;
2022-06-08 13:25:42 -04:00
}
2022-10-06 20:08:12 -04:00
FText PropertyPathToText ( const UMVVMBlueprintView * BlueprintView , const FMVVMBlueprintPropertyPath & PropertyPath )
{
return FText : : FromString ( PropertyPathToString ( BlueprintView , PropertyPath ) ) ;
}
FText GetViewModelIdText ( const FMVVMBlueprintPropertyPath & PropertyPath )
{
return FText : : FromString ( PropertyPath . GetViewModelId ( ) . ToString ( EGuidFormats : : DigitsWithHyphensInBraces ) ) ;
}
2023-03-22 13:07:37 -04:00
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > AddObjectFieldPath ( FCompiledBindingLibraryCompiler & BindingLibraryCompiler , const UWidgetBlueprintGeneratedClass * Class , FStringView ObjectPath , UClass * ExpectedType )
{
// 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-03-17 16:33:42 -04:00
void FMVVMViewBlueprintCompiler : : AddMessageForBinding ( FMVVMBlueprintViewBinding & Binding , UMVVMBlueprintView * BlueprintView , const FText & MessageText , EBindingMessageType MessageType , FName ArgumentName ) const
2022-10-06 20:08:12 -04:00
{
const FText BindingName = FText : : FromString ( Binding . GetDisplayNameString ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ) ;
FText FormattedError ;
if ( ! ArgumentName . IsNone ( ) )
{
2023-03-17 16:33:42 -04:00
FormattedError = FText : : Format ( LOCTEXT ( " BindingFormatWithArgument " , " Binding '{0}': Argument '{1}' - {2} " ) , BindingName , FText : : FromName ( ArgumentName ) , 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-03-17 16:33:42 -04:00
switch ( MessageType )
{
case EBindingMessageType : : Info :
WidgetBlueprintCompilerContext . MessageLog . Note ( * FormattedError . ToString ( ) ) ;
break ;
case EBindingMessageType : : Warning :
WidgetBlueprintCompilerContext . MessageLog . Warning ( * FormattedError . ToString ( ) ) ;
break ;
case EBindingMessageType : : Error :
WidgetBlueprintCompilerContext . MessageLog . Error ( * FormattedError . ToString ( ) ) ;
break ;
default :
break ;
}
FBindingMessage NewMessage = { FormattedError , MessageType } ;
BlueprintView - > AddMessageToBinding ( Binding . BindingId , NewMessage ) ;
2022-10-06 20:08:12 -04:00
}
void FMVVMViewBlueprintCompiler : : AddErrorForViewModel ( const FMVVMBlueprintViewModelContext & ViewModel , const FText & Message ) const
{
const FText FormattedError = FText : : Format ( LOCTEXT ( " ViewModelFormat " , " Viewodel '{0}': {1} " ) , ViewModel . GetDisplayName ( ) , Message ) ;
WidgetBlueprintCompilerContext . MessageLog . Error ( * FormattedError . ToString ( ) ) ;
}
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 )
{
auto RenameObjectToTransientPackage = [ ] ( UObject * ObjectToRename )
{
2022-08-24 13:14:51 -04:00
const ERenameFlags RenFlags = REN_DoNotDirty | REN_ForceNoResetLoaders | REN_DontCreateRedirectors ;
2022-06-08 13:25:42 -04:00
ObjectToRename - > Rename ( nullptr , GetTransientPackage ( ) , RenFlags ) ;
ObjectToRename - > SetFlags ( RF_Transient ) ;
ObjectToRename - > ClearFlags ( RF_Public | RF_Standalone | RF_ArchetypeObject ) ;
FLinkerLoad : : InvalidateExport ( ObjectToRename ) ;
} ;
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 ) ;
}
}
}
void FMVVMViewBlueprintCompiler : : CleanTemporaries ( UWidgetBlueprintGeneratedClass * ClassToClean )
{
}
void FMVVMViewBlueprintCompiler : : CreateFunctions ( UMVVMBlueprintView * BlueprintView )
{
if ( ! bAreSourcesCreatorValid | | ! bIsBindingsValid )
{
return ;
}
2023-03-10 23:20:09 -05:00
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter )
{
return ;
}
2022-07-21 14:19:45 -04:00
for ( const FCompilerSourceCreatorContext & SourceCreator : CompilerSourceCreatorContexts )
2022-06-08 13:25:42 -04:00
{
if ( SourceCreator . SetterGraph )
{
if ( ! UE : : MVVM : : FunctionGraphHelper : : GenerateViewModelSetter ( WidgetBlueprintCompilerContext , SourceCreator . SetterGraph , SourceCreator . ViewModelContext . GetViewModelName ( ) ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( SourceCreator . ViewModelContext , LOCTEXT ( " SetterFunctionCouldNotBeGenerated " , " The setter function could not be generated. " ) ) ;
2022-06-08 13:25:42 -04:00
continue ;
}
}
}
}
void FMVVMViewBlueprintCompiler : : CreateVariables ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context , UMVVMBlueprintView * BlueprintView )
{
if ( ! BlueprintView )
{
return ;
}
if ( ! bAreSourcesCreatorValid | | ! bAreSourceContextsValid | | ! bIsBindingsValid )
{
return ;
}
if ( Context . GetCompileType ( ) = = EKismetCompileType : : SkeletonOnly )
{
CreateWidgetMap ( Context , BlueprintView ) ;
CreateSourceLists ( Context , BlueprintView ) ;
CreateFunctionsDeclaration ( Context , BlueprintView ) ;
}
2022-11-25 13:55:44 -05:00
auto CreateVariable = [ & Context ] ( const FCompilerUserWidgetPropertyContext & SourceContext ) - > FProperty *
2022-06-08 13:25:42 -04:00
{
2022-11-25 13:55:44 -05:00
FEdGraphPinType NewPropertyPinType ( UEdGraphSchema_K2 : : PC_Object , NAME_None , SourceContext . Class , EPinContainerType : : None , false , FEdGraphTerminalType ( ) ) ;
FProperty * NewProperty = Context . CreateVariable ( SourceContext . PropertyName , NewPropertyPinType ) ;
2022-10-12 14:35:36 -04:00
if ( NewProperty ! = nullptr )
2022-06-08 13:25:42 -04:00
{
2022-11-25 13:55:44 -05:00
NewProperty - > SetPropertyFlags ( CPF_BlueprintVisible | CPF_RepSkip | CPF_Transient | CPF_DuplicateTransient ) ;
if ( SourceContext . BlueprintSetter . IsEmpty ( ) )
{
NewProperty - > SetPropertyFlags ( CPF_BlueprintReadOnly ) ;
}
2022-10-12 14:35:36 -04:00
NewProperty - > SetPropertyFlags ( SourceContext . bExposeOnSpawn ? CPF_ExposeOnSpawn : CPF_DisableEditOnInstance ) ;
2022-06-08 13:25:42 -04:00
# if WITH_EDITOR
if ( ! SourceContext . BlueprintSetter . IsEmpty ( ) )
{
2022-10-12 14:35:36 -04:00
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_PropertySetFunction , * SourceContext . BlueprintSetter ) ;
2022-06-08 13:25:42 -04:00
}
if ( ! SourceContext . DisplayName . IsEmpty ( ) )
{
2022-10-12 14:35:36 -04:00
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_DisplayName , * SourceContext . DisplayName . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
}
if ( ! SourceContext . CategoryName . IsEmpty ( ) )
{
2022-10-12 14:35:36 -04:00
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_FunctionCategory , * SourceContext . CategoryName ) ;
2022-06-08 13:25:42 -04:00
}
if ( SourceContext . bExposeOnSpawn )
{
2022-10-12 14:35:36 -04:00
NewProperty - > SetMetaData ( FBlueprintMetadata : : MD_ExposeOnSpawn , TEXT ( " true " ) ) ;
2022-06-08 13:25:42 -04:00
}
2023-03-06 13:07:33 -05:00
//if (!SourceContext.bPublicGetter)
//{
// NewProperty->SetMetaData(FBlueprintMetadata::MD_Private, TEXT("true"));
//}
2022-06-08 13:25:42 -04:00
# endif
}
2022-10-12 14:35:36 -04:00
return NewProperty ;
2022-06-08 13:25:42 -04:00
} ;
2023-01-04 15:47:23 -05:00
for ( FCompilerUserWidgetPropertyContext & SourceContext : CompilerUserWidgetPropertyContexts )
2022-06-08 13:25:42 -04:00
{
SourceContext . Field = BindingHelper : : FindFieldByName ( Context . GetSkeletonGeneratedClass ( ) , FMVVMBindingName ( SourceContext . PropertyName ) ) ;
// The class is not linked yet. It may not be available yet.
if ( SourceContext . Field . IsEmpty ( ) )
{
for ( FField * Field = Context . GetSkeletonGeneratedClass ( ) - > ChildProperties ; Field ! = nullptr ; Field = Field - > Next )
{
if ( Field - > GetFName ( ) = = SourceContext . PropertyName )
{
if ( FProperty * Property = CastField < FProperty > ( Field ) )
{
SourceContext . Field = FMVVMFieldVariant ( Property ) ;
break ;
}
else
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FieldIsNotProperty " , " The field for source '{0}' exists but is not a property. " ) , SourceContext . DisplayName ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
}
}
}
// Reuse the property if found
if ( ! SourceContext . Field . IsEmpty ( ) )
{
if ( ! BindingHelper : : IsValidForSourceBinding ( SourceContext . Field ) )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " FieldNotAccessibleAtRuntime " , " The field for source '{0}' exists but is not accessible at runtime. " ) , SourceContext . DisplayName ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
const FProperty * Property = SourceContext . Field . IsProperty ( ) ? SourceContext . Field . GetProperty ( ) : BindingHelper : : GetReturnProperty ( SourceContext . Field . GetFunction ( ) ) ;
const FObjectPropertyBase * ObjectProperty = CastField < const FObjectPropertyBase > ( Property ) ;
const bool bIsCompatible = ObjectProperty & & SourceContext . Class - > IsChildOf ( ObjectProperty - > PropertyClass ) ;
if ( ! bIsCompatible )
{
2022-10-06 20:08:12 -04:00
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. " ) , SourceContext . DisplayName ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourceContextsValid = false ;
continue ;
}
}
if ( SourceContext . Field . IsEmpty ( ) )
{
SourceContext . Field = FMVVMConstFieldVariant ( CreateVariable ( SourceContext ) ) ;
}
if ( SourceContext . Field . IsEmpty ( ) )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " VariableCouldNotBeCreated " , " The variable for '{0}' could not be created. " ) , SourceContext . DisplayName ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourceContextsValid = false ;
continue ;
}
}
}
void FMVVMViewBlueprintCompiler : : CreateWidgetMap ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context , UMVVMBlueprintView * BlueprintView )
{
// 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 ) ;
}
}
void FMVVMViewBlueprintCompiler : : CreateSourceLists ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context , UMVVMBlueprintView * BlueprintView )
{
2023-01-04 15:47:23 -05:00
CompilerUserWidgetPropertyContexts . Reset ( ) ;
2022-07-21 14:19:45 -04:00
CompilerSourceCreatorContexts . 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
TSet < FName > WidgetSources ;
for ( const FMVVMBlueprintViewModelContext & ViewModelContext : BlueprintView - > GetViewModels ( ) )
{
if ( ! ViewModelContext . GetViewModelId ( ) . IsValid ( ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidGuid " , " GUID is invalid. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
2022-10-06 20:08:12 -04:00
if ( ViewModelGuids . Contains ( ViewModelContext . GetViewModelId ( ) ) )
2022-06-08 13:25:42 -04:00
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelAlreadyAdded " , " Identical viewmodel has already been added. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
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
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidClass " , " Invalid class. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
2023-03-10 23:20:09 -05:00
const bool bCreateSetterFunction = GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter
& & ( ViewModelContext . bCreateSetterFunction | | ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual ) ;
2022-06-08 13:25:42 -04:00
2022-07-13 13:52:41 -04:00
int32 FoundSourceCreatorContextIndex = INDEX_NONE ;
2022-06-08 13:25:42 -04:00
if ( Context . GetCompileType ( ) = = EKismetCompileType : : SkeletonOnly )
{
FCompilerSourceCreatorContext SourceContext ;
SourceContext . ViewModelContext = ViewModelContext ;
SourceContext . Type = ECompilerSourceCreatorType : : ViewModel ;
if ( bCreateSetterFunction )
{
SourceContext . SetterFunctionName = TEXT ( " Set " ) + ViewModelContext . GetViewModelName ( ) . ToString ( ) ;
}
2022-07-21 14:19:45 -04:00
FoundSourceCreatorContextIndex = CompilerSourceCreatorContexts . Emplace ( MoveTemp ( SourceContext ) ) ;
2022-06-08 13:25:42 -04:00
}
else
{
FGuid ViewModelId = ViewModelContext . GetViewModelId ( ) ;
2022-07-21 14:19:45 -04:00
FoundSourceCreatorContextIndex = CompilerSourceCreatorContexts . IndexOfByPredicate ( [ ViewModelId ] ( const FCompilerSourceCreatorContext & Other )
2022-06-08 13:25:42 -04:00
{
return Other . ViewModelContext . GetViewModelId ( ) = = ViewModelId ;
} ) ;
}
2022-07-13 13:52:41 -04:00
checkf ( FoundSourceCreatorContextIndex ! = INDEX_NONE , TEXT ( " The viewmodel was added after the skeleton was created? " ) ) ;
2022-06-08 13:25:42 -04:00
2022-11-25 13:55:44 -05:00
FCompilerUserWidgetPropertyContext SourceVariable ;
2022-06-29 19:00:59 -04:00
SourceVariable . Class = ViewModelContext . GetViewModelClass ( ) ;
2022-06-08 13:25:42 -04:00
SourceVariable . PropertyName = ViewModelContext . GetViewModelName ( ) ;
2022-10-06 20:08:12 -04:00
SourceVariable . DisplayName = ViewModelContext . GetDisplayName ( ) ;
2022-06-08 13:25:42 -04:00
SourceVariable . CategoryName = TEXT ( " Viewmodel " ) ;
SourceVariable . bExposeOnSpawn = bCreateSetterFunction ;
2023-03-06 09:41:11 -05:00
//SourceVariable.bPublicGetter = ViewModelContext.bCreateGetterFunction;
2022-07-21 14:19:45 -04:00
SourceVariable . BlueprintSetter = CompilerSourceCreatorContexts [ FoundSourceCreatorContextIndex ] . SetterFunctionName ;
2023-01-04 15:47:23 -05:00
SourceVariable . ViewModelId = ViewModelContext . GetViewModelId ( ) ;
CompilerUserWidgetPropertyContexts . Emplace ( MoveTemp ( SourceVariable ) ) ;
2022-06-08 13:25:42 -04:00
}
bAreSourceContextsValid = bAreSourcesCreatorValid ;
2022-10-06 20:08:12 -04:00
UWidgetBlueprintGeneratedClass * SkeletonClass = Context . GetSkeletonGeneratedClass ( ) ;
2022-10-12 14:35:36 -04:00
const FName DefaultWidgetCategory = Context . GetWidgetBlueprint ( ) - > GetFName ( ) ;
2022-10-06 20:08:12 -04:00
2022-06-08 13:25:42 -04:00
// Only find the source first property and destination first property.
//The full path will be tested later. We want to build the list of property needed.
for ( int32 Index = 0 ; Index < BlueprintView - > GetNumBindings ( ) ; + + Index )
{
2022-10-06 20:08:12 -04:00
FMVVMBlueprintViewBinding * BindingPtr = BlueprintView - > GetBindingAt ( Index ) ;
2022-06-08 13:25:42 -04:00
if ( BindingPtr = = nullptr )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " BindingInvalidIndex " , " Internal error: Tried to fetch binding for invalid index {0}. " ) , Index ) . ToString ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourceContextsValid = false ;
continue ;
}
2022-10-06 20:08:12 -04:00
FMVVMBlueprintViewBinding & Binding = * BindingPtr ;
2022-06-08 13:25:42 -04:00
if ( ! Binding . bCompile )
{
continue ;
}
2022-07-13 13:52:41 -04:00
FMVVMViewBlueprintCompiler * Self = this ;
2023-05-17 12:34:34 -04:00
auto GenerateCompilerSourceContext = [ Self , BlueprintView , DefaultWidgetCategory , Class = SkeletonClass , & Binding , & ViewModelGuids , & WidgetSources ] ( const FMVVMBlueprintPropertyPath & PropertyPath , FName ArgumentName = FName ( ) ) - > bool
2022-06-08 13:25:42 -04:00
{
2022-07-13 13:52:41 -04:00
if ( PropertyPath . IsFromWidget ( ) )
2022-06-08 13:25:42 -04:00
{
2022-07-13 13:52:41 -04:00
if ( PropertyPath . GetWidgetName ( ) = = Class - > ClassGeneratedBy - > GetFName ( ) )
{
// it's the userwidget
return true ;
}
// If the widget doesn't have a property, add one automatically.
if ( ! WidgetSources . Contains ( PropertyPath . GetWidgetName ( ) ) )
{
WidgetSources . Add ( PropertyPath . GetWidgetName ( ) ) ;
UWidget * * WidgetPtr = Self - > WidgetNameToWidgetPointerMap . Find ( PropertyPath . GetWidgetName ( ) ) ;
2022-06-08 13:25:42 -04:00
if ( WidgetPtr = = nullptr | | * WidgetPtr = = nullptr )
{
2023-03-17 16:33:42 -04:00
Self - > AddMessageForBinding ( Binding ,
BlueprintView ,
2022-10-06 20:08:12 -04:00
FText : : Format ( LOCTEXT ( " InvalidWidgetFormat " , " Could not find the targeted widget: {0} " ) ,
FText : : FromName ( PropertyPath . GetWidgetName ( ) )
) ,
2023-03-17 16:33:42 -04:00
EBindingMessageType : : Error ,
2022-10-06 20:08:12 -04:00
ArgumentName
) ;
2022-07-13 13:52:41 -04:00
return false ;
2022-06-08 13:25:42 -04:00
}
UWidget * Widget = * WidgetPtr ;
2022-11-25 13:55:44 -05:00
FCompilerUserWidgetPropertyContext SourceVariable ;
2022-06-08 13:25:42 -04:00
SourceVariable . Class = Widget - > GetClass ( ) ;
2022-07-13 13:52:41 -04:00
SourceVariable . PropertyName = PropertyPath . GetWidgetName ( ) ;
2022-11-25 13:55:44 -05:00
SourceVariable . DisplayName = FText : : FromString ( Widget - > GetDisplayLabel ( ) ) ;
SourceVariable . CategoryName = TEXT ( " Widget " ) ;
2023-01-04 15:47:23 -05:00
SourceVariable . ViewModelId = FGuid ( ) ;
2023-03-06 09:41:11 -05:00
SourceVariable . bPublicGetter = false ;
2023-01-04 15:47:23 -05:00
Self - > CompilerUserWidgetPropertyContexts . Emplace ( MoveTemp ( SourceVariable ) ) ;
2022-06-08 13:25:42 -04:00
}
}
2022-07-13 13:52:41 -04:00
else if ( PropertyPath . IsFromViewModel ( ) )
{
const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) ;
if ( SourceViewModelContext = = nullptr )
{
2023-03-17 16:33:42 -04:00
Self - > AddMessageForBinding ( Binding ,
BlueprintView ,
2022-10-06 20:08:12 -04:00
FText : : Format ( LOCTEXT ( " BindingViewModelNotFound " , " Could not find viewmodel with GUID {0}. " ) , GetViewModelIdText ( PropertyPath ) ) ,
2023-03-17 16:33:42 -04:00
EBindingMessageType : : Error ,
2022-10-06 20:08:12 -04:00
ArgumentName
) ;
2022-07-13 13:52:41 -04:00
return false ;
}
2022-06-08 13:25:42 -04:00
2022-10-06 20:08:12 -04:00
if ( ! ViewModelGuids . Contains ( SourceViewModelContext - > GetViewModelId ( ) ) )
{
2023-03-17 16:33:42 -04:00
Self - > AddMessageForBinding ( Binding ,
BlueprintView ,
2022-10-06 20:08:12 -04:00
FText : : Format ( LOCTEXT ( " BindingViewModelInvalid " , " Viewmodel {0} {1} was invalid. " ) ,
SourceViewModelContext - > GetDisplayName ( ) ,
GetViewModelIdText ( PropertyPath )
) ,
2023-03-17 16:33:42 -04:00
EBindingMessageType : : Error ,
2022-10-06 20:08:12 -04:00
ArgumentName
) ;
2022-07-13 13:52:41 -04:00
return false ;
}
}
else
{
2023-05-17 12:34:34 -04:00
Self - > AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " SourcePathNotSet " , " A source path is required, but not set. " ) , EBindingMessageType : : Error , ArgumentName ) ;
2022-07-13 13:52:41 -04:00
return false ;
}
return true ;
} ;
2022-10-06 20:08:12 -04:00
const bool bIsForwardBinding = IsForwardBinding ( Binding . BindingType ) ;
const bool bIsBackwardBinding = IsBackwardBinding ( Binding . BindingType ) ;
if ( bIsForwardBinding | | bIsBackwardBinding )
2022-07-13 13:52:41 -04:00
{
2022-10-06 20:08:12 -04:00
TMap < FName , FMVVMBlueprintPropertyPath > ConversionFunctionArguments = ConversionFunctionHelper : : GetAllArgumentPropertyPaths ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , Binding , bIsForwardBinding , true ) ;
if ( ConversionFunctionArguments . Num ( ) > 0 )
{
if ( Binding . BindingType = = EMVVMBindingMode : : TwoWay )
{
2023-03-17 16:33:42 -04:00
Self - > AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " TwoWayBindingsWithConversion " , " Two-way bindings are not allowed to use conversion functions. " ) , EBindingMessageType : : Error ) ;
2022-10-06 20:08:12 -04:00
bAreSourceContextsValid = false ;
continue ;
}
// generate sources for conversion function arguments
for ( const TPair < FName , FMVVMBlueprintPropertyPath > & Arg : ConversionFunctionArguments )
{
2023-05-19 12:03:31 -04:00
bAreSourceContextsValid & = GenerateCompilerSourceContext ( Arg . Value , Arg . Key ) ;
2022-10-06 20:08:12 -04:00
}
// generate destination source
if ( bIsForwardBinding )
{
2023-05-17 12:34:34 -04:00
bAreSourceContextsValid & = GenerateCompilerSourceContext ( Binding . DestinationPath ) ;
2022-10-06 20:08:12 -04:00
}
else
{
2023-05-17 12:34:34 -04:00
bAreSourceContextsValid & = GenerateCompilerSourceContext ( Binding . SourcePath ) ;
2022-10-06 20:08:12 -04:00
}
}
else
{
// if we aren't using a conversion function, just validate the widget and viewmodel paths
2023-05-17 12:34:34 -04:00
bAreSourceContextsValid & = GenerateCompilerSourceContext ( Binding . DestinationPath ) ;
bAreSourceContextsValid & = GenerateCompilerSourceContext ( Binding . SourcePath ) ;
2022-10-06 20:08:12 -04:00
}
2022-06-08 13:25:42 -04:00
}
}
}
void FMVVMViewBlueprintCompiler : : CreateFunctionsDeclaration ( const FWidgetBlueprintCompilerContext : : FCreateVariableContext & Context , UMVVMBlueprintView * BlueprintView )
{
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.
{
auto RenameObjectToTransientPackage = [ ] ( UObject * ObjectToRename )
{
const ERenameFlags RenFlags = REN_DontCreateRedirectors | REN_NonTransactional | REN_DoNotDirty ;
ObjectToRename - > Rename ( nullptr , GetTransientPackage ( ) , RenFlags ) ;
ObjectToRename - > SetFlags ( RF_Transient ) ;
ObjectToRename - > ClearFlags ( RF_Public | RF_Standalone | RF_ArchetypeObject ) ;
FLinkerLoad : : InvalidateExport ( ObjectToRename ) ;
} ;
for ( UEdGraph * OldGraph : BlueprintView - > TemporaryGraph )
{
if ( OldGraph )
{
RenameObjectToTransientPackage ( OldGraph ) ;
}
}
BlueprintView - > TemporaryGraph . Reset ( ) ;
}
2023-03-10 23:20:09 -05:00
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowGeneratedViewModelSetter )
{
return ;
}
2022-07-21 14:19:45 -04:00
for ( FCompilerSourceCreatorContext & SourceCreator : CompilerSourceCreatorContexts )
2022-06-08 13:25:42 -04:00
{
2022-07-13 13:52:41 -04:00
if ( ! SourceCreator . SetterFunctionName . IsEmpty ( ) & & SourceCreator . Type = = ECompilerSourceCreatorType : : ViewModel )
2022-06-08 13:25:42 -04:00
{
ensure ( SourceCreator . SetterGraph = = nullptr ) ;
2022-08-24 13:14:51 -04:00
2022-06-08 13:25:42 -04:00
SourceCreator . SetterGraph = UE : : MVVM : : FunctionGraphHelper : : CreateIntermediateFunctionGraph (
WidgetBlueprintCompilerContext
, SourceCreator . SetterFunctionName
, ( FUNC_BlueprintCallable | FUNC_Public )
, TEXT ( " Viewmodel " )
, false ) ;
2022-08-24 13:14:51 -04:00
BlueprintView - > TemporaryGraph . Add ( SourceCreator . SetterGraph ) ;
2022-06-08 13:25:42 -04:00
if ( SourceCreator . SetterGraph = = nullptr | | SourceCreator . SetterGraph - > GetFName ( ) ! = FName ( * SourceCreator . SetterFunctionName ) )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " SetterNameAlreadyExists " , " The setter name {0} already exists and could not be autogenerated. " ) ,
FText : : FromString ( SourceCreator . SetterFunctionName )
) . ToString ( )
) ;
2022-06-08 13:25:42 -04:00
}
2022-06-29 19:00:59 -04:00
UE : : MVVM : : FunctionGraphHelper : : AddFunctionArgument ( SourceCreator . SetterGraph , SourceCreator . ViewModelContext . GetViewModelClass ( ) , " Viewmodel " ) ;
2022-06-08 13:25:42 -04:00
}
}
}
bool FMVVMViewBlueprintCompiler : : PreCompile ( UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView )
{
if ( ! bAreSourcesCreatorValid | | ! bAreSourceContextsValid | | ! bIsBindingsValid )
{
return false ;
}
2022-07-13 13:52:41 -04:00
const int32 NumBindings = BlueprintView - > GetNumBindings ( ) ;
2022-07-21 14:19:45 -04:00
CompilerBindings . Reset ( NumBindings * 2 ) ;
2022-07-13 13:52:41 -04:00
BindingSourceContexts . Reset ( NumBindings * 2 ) ;
2023-02-24 03:57:54 -05:00
FPropertyEditorPermissionList : : Get ( ) . AddPermissionList ( Class , FNamePermissionList ( ) , EPropertyPermissionListRules : : AllowListAllProperties ) ;
2023-02-17 15:28:46 -05:00
2022-07-13 13:52:41 -04:00
PreCompileBindingSources ( Class , BlueprintView ) ;
2022-06-08 13:25:42 -04:00
PreCompileSourceCreators ( Class , BlueprintView ) ;
PreCompileBindings ( Class , BlueprintView ) ;
return bAreSourcesCreatorValid & & bAreSourceContextsValid & & bIsBindingsValid ;
}
bool FMVVMViewBlueprintCompiler : : Compile ( UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView , UMVVMViewClass * ViewExtension )
{
if ( ! bAreSourcesCreatorValid | | ! bAreSourceContextsValid | | ! bIsBindingsValid )
{
return false ;
}
2022-10-06 20:08:12 -04:00
TValueOrError < FCompiledBindingLibraryCompiler : : FCompileResult , FText > CompileResult = BindingLibraryCompiler . Compile ( ) ;
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 ;
}
CompileSourceCreators ( CompileResult . GetValue ( ) , Class , BlueprintView , ViewExtension ) ;
CompileBindings ( CompileResult . GetValue ( ) , Class , BlueprintView , ViewExtension ) ;
bool bResult = bAreSourcesCreatorValid & & bAreSourceContextsValid & & bIsBindingsValid ;
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
if ( CVarLogViewCompliedResult - > GetBool ( ) )
2023-03-16 13:02:33 -04:00
{
2023-03-22 13:07:37 -04:00
FMVVMViewClass_SourceCreator : : FToStringArgs CreatorsToStringArgs = FMVVMViewClass_SourceCreator : : FToStringArgs : : All ( ) ;
CreatorsToStringArgs . bUseDisplayName = false ;
FMVVMViewClass_CompiledBinding : : FToStringArgs BindingToStringArgs = FMVVMViewClass_CompiledBinding : : FToStringArgs : : All ( ) ;
BindingToStringArgs . bUseDisplayName = false ;
ViewExtension - > Log ( CreatorsToStringArgs , BindingToStringArgs ) ;
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
}
return bResult ;
}
2022-07-13 13:52:41 -04:00
bool FMVVMViewBlueprintCompiler : : PreCompileBindingSources ( UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView )
{
2023-03-22 13:07:37 -04:00
const int32 NumBindings = BlueprintView - > GetNumBindings ( ) ; // NB Binding can be added when creating a dynamic vm
2022-07-13 13:52:41 -04:00
for ( int32 Index = 0 ; Index < NumBindings ; + + Index )
{
FMVVMBlueprintViewBinding * BindingPtr = BlueprintView - > GetBindingAt ( Index ) ;
if ( BindingPtr = = nullptr )
{
2022-10-06 20:08:12 -04:00
WidgetBlueprintCompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " InvalidBindingIndex " , " Internal error. Invalid binding index given. " ) , Index ) . ToString ( ) ) ;
2022-07-13 13:52:41 -04:00
bIsBindingsValid = false ;
continue ;
}
FMVVMBlueprintViewBinding & Binding = * BindingPtr ;
if ( ! Binding . bCompile )
{
continue ;
}
2023-03-22 13:07:37 -04:00
bool bIsOneTimeBinding = IsOneTimeBinding ( Binding . BindingType ) ;
auto CreateSourceContextForPropertyPath = [ this , & Binding , BlueprintView , Class , Index , bIsOneTimeBinding ] ( const FMVVMBlueprintPropertyPath & Path , bool bForwardBinding , FName ArgumentName ) - > bool
2022-07-13 13:52:41 -04:00
{
2023-03-22 13:07:37 -04:00
const TValueOrError < FBindingSourceContext , FText > CreatedBindingSourceContext = CreateBindingSourceContext ( BlueprintView , Class , Path , bIsOneTimeBinding ) ;
2022-07-13 13:52:41 -04:00
if ( CreatedBindingSourceContext . HasError ( ) )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView ,
2022-10-06 20:08:12 -04:00
FText : : Format ( LOCTEXT ( " PropertyPathInvalidWithReason " , " The property path '{0}' is invalid. {1} " ) ,
2023-03-09 08:41:28 -05:00
PropertyPathToText ( BlueprintView , Binding . SourcePath ) ,
2022-10-06 20:08:12 -04:00
CreatedBindingSourceContext . GetError ( )
) ,
2023-03-17 16:33:42 -04:00
EBindingMessageType : : Error ,
2022-10-06 20:08:12 -04:00
ArgumentName
) ;
return false ;
2022-07-13 13:52:41 -04:00
}
FBindingSourceContext BindingSourceContext = CreatedBindingSourceContext . GetValue ( ) ;
if ( ! IsPropertyPathValid ( BindingSourceContext . PropertyPath ) )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding ,
BlueprintView ,
2022-10-06 20:08:12 -04:00
FText : : Format ( LOCTEXT ( " PropertyPathIsInvalid " , " The property path '{0}' is invalid. " ) ,
2023-03-09 08:41:28 -05:00
PropertyPathToText ( BlueprintView , Binding . SourcePath )
2022-10-06 20:08:12 -04:00
) ,
2023-03-17 16:33:42 -04:00
EBindingMessageType : : Error ,
2022-10-06 20:08:12 -04:00
ArgumentName
) ;
return false ;
2022-07-13 13:52:41 -04:00
}
2022-10-06 20:08:12 -04:00
2022-07-13 13:52:41 -04:00
if ( BindingSourceContext . SourceClass = = nullptr )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " BindingInvalidSourceClass " , " Internal error. The binding could not find its source class. " ) , EBindingMessageType : : Error , ArgumentName ) ;
2022-10-06 20:08:12 -04:00
return false ;
2022-07-13 13:52:41 -04:00
}
2022-10-06 20:08:12 -04:00
2023-03-06 09:41:11 -05:00
if ( ! BindingSourceContext . bIsRootWidget & & BindingSourceContext . UserWidgetPropertyContextIndex = = INDEX_NONE & & BindingSourceContext . SourceCreatorContextIndex = = INDEX_NONE )
2022-07-13 13:52:41 -04:00
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " BindingInvalidSource " , " Internal error. The binding could not find its source. " ) , EBindingMessageType : : Error , ArgumentName ) ;
2022-10-06 20:08:12 -04:00
return false ;
2022-07-13 13:52:41 -04:00
}
BindingSourceContext . BindingIndex = Index ;
2022-10-06 20:08:12 -04:00
BindingSourceContext . bIsForwardBinding = bForwardBinding ;
2023-03-22 13:07:37 -04:00
BindingSourceContext . bIsComplexBinding = ! ArgumentName . IsNone ( ) ;
2022-10-06 20:08:12 -04:00
this - > BindingSourceContexts . Add ( MoveTemp ( BindingSourceContext ) ) ;
return true ;
} ;
2023-03-22 13:07:37 -04:00
enum class ECreateSourcesForConversionFunctionResult : uint8 { Valid , Failed , Continue } ;
auto CreateSourcesForConversionFunction = [ this , & Binding , BlueprintView , & CreateSourceContextForPropertyPath ] ( bool bForwardBinding )
2022-10-06 20:08:12 -04:00
{
2023-03-22 13:07:37 -04:00
ECreateSourcesForConversionFunctionResult Result = ECreateSourcesForConversionFunctionResult : : Continue ;
2022-10-06 20:08:12 -04:00
TMap < FName , FMVVMBlueprintPropertyPath > ArgumentPaths = ConversionFunctionHelper : : GetAllArgumentPropertyPaths ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) , Binding , bForwardBinding , true ) ;
for ( const TPair < FName , FMVVMBlueprintPropertyPath > & Pair : ArgumentPaths )
{
2023-03-22 13:07:37 -04:00
if ( Pair . Key . IsNone ( ) )
{
AddMessageForBinding ( Binding , BlueprintView ,
FText : : Format ( LOCTEXT ( " InvalidArgumentPathName " , " The conversion function {0} has an invalid argument " ) , FText : : FromString ( Binding . GetDisplayNameString ( WidgetBlueprintCompilerContext . WidgetBlueprint ( ) ) ) ) ,
EBindingMessageType : : Error
) ;
Result = ECreateSourcesForConversionFunctionResult : : Failed ;
}
else if ( CreateSourceContextForPropertyPath ( Pair . Value , bForwardBinding , Pair . Key ) )
{
Result = ECreateSourcesForConversionFunctionResult : : Valid ;
}
else
{
Result = ECreateSourcesForConversionFunctionResult : : Failed ;
break ;
}
2022-10-06 20:08:12 -04:00
}
2023-03-22 13:07:37 -04:00
return Result ;
2022-10-06 20:08:12 -04:00
} ;
2023-03-17 16:33:42 -04:00
auto AddWarningForPropertyWithMVVMAndLegacyBinding = [ this , & Binding , & BlueprintView , Class ] ( const FMVVMBlueprintPropertyPath & Path )
{
if ( ! Path . HasPaths ( ) )
{
return ;
}
// There can't be a legacy binding in the local scope, so we can skip this if the MVVM binding refers to a property in local scope.
if ( Path . HasFieldInLocalScope ( ) )
{
return ;
}
TArrayView < FMVVMBlueprintFieldPath const > MVVMBindingPath = Path . GetFieldPaths ( ) ;
TArray < FDelegateRuntimeBinding > LegacyBindings = Class - > Bindings ;
FName MVVMFieldName = Path . GetPaths ( ) . Last ( ) ;
FName MVVMObjectName = Path . GetWidgetName ( ) ;
if ( Path . GetFieldPaths ( ) . Last ( ) . GetBindingKind ( ) = = EBindingKind : : Function )
{
return ;
}
// If the first field is a UserWidget, we know this property resides in a nested UserWidget.
if ( MVVMBindingPath [ 0 ] . GetParentClass ( ) & & MVVMBindingPath [ 0 ] . GetParentClass ( ) - > IsChildOf ( UUserWidget : : StaticClass ( ) ) & & MVVMBindingPath . Num ( ) > 1 )
{
if ( UWidgetBlueprintGeneratedClass * NestedBPGClass = Cast < UWidgetBlueprintGeneratedClass > ( MVVMBindingPath [ MVVMBindingPath . Num ( ) - 2 ] . GetParentClass ( ) ) )
{
LegacyBindings = NestedBPGClass - > Bindings ;
// We can't use Path.GetWidgetName() when we are dealing with nested UserWidgets, because it refers to the topmost UserWidget.
MVVMObjectName = MVVMBindingPath [ MVVMBindingPath . Num ( ) - 2 ] . GetFieldName ( ) ;
}
else
{
return ;
}
}
for ( const FDelegateRuntimeBinding & LegacyBinding : LegacyBindings )
{
if ( LegacyBinding . ObjectName = = MVVMObjectName )
{
if ( LegacyBinding . PropertyName = = MVVMFieldName )
{
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " BindingConflictWithLegacy " , " The binding is set on a property with legacy binding. " ) , EBindingMessageType : : Warning ) ;
break ;
}
}
}
} ;
2023-03-22 13:07:37 -04:00
// Add the forward binding. If the binding has a conversion function, use it instead of the regular binding.
2022-10-06 20:08:12 -04:00
if ( IsForwardBinding ( Binding . BindingType ) )
{
2023-03-22 13:07:37 -04:00
ECreateSourcesForConversionFunctionResult ConversionFunctionResult = CreateSourcesForConversionFunction ( true ) ;
if ( ConversionFunctionResult = = ECreateSourcesForConversionFunctionResult : : Continue )
2022-10-06 20:08:12 -04:00
{
2023-03-22 13:07:37 -04:00
if ( ! Binding . SourcePath . IsEmpty ( ) )
2023-03-17 16:33:42 -04:00
{
2023-03-22 13:07:37 -04:00
if ( ! Binding . DestinationPath . IsEmpty ( ) )
{
AddWarningForPropertyWithMVVMAndLegacyBinding ( Binding . DestinationPath ) ;
}
if ( ! CreateSourceContextForPropertyPath ( Binding . SourcePath , true , FName ( ) ) )
{
bIsBindingsValid = false ;
continue ;
}
2023-03-17 16:33:42 -04:00
}
2023-03-22 13:07:37 -04:00
else
2022-10-06 20:08:12 -04:00
{
2023-03-22 13:07:37 -04:00
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " BindingEmptySourcePath " , " The binding doesn't have a Source or a Conversion function. " ) , EBindingMessageType : : Error ) ;
2022-10-06 20:08:12 -04:00
bIsBindingsValid = false ;
continue ;
}
}
2023-03-22 13:07:37 -04:00
else if ( ConversionFunctionResult = = ECreateSourcesForConversionFunctionResult : : Failed )
{
bIsBindingsValid = false ;
continue ;
}
2022-07-13 13:52:41 -04:00
}
2023-03-22 13:07:37 -04:00
// Add the backward binding. If the binding has a conversion function, use it instead of the regular binding.
2022-07-13 13:52:41 -04:00
if ( IsBackwardBinding ( Binding . BindingType ) )
{
2023-03-22 13:07:37 -04:00
ECreateSourcesForConversionFunctionResult ConversionFunctionResult = CreateSourcesForConversionFunction ( false ) ;
if ( ConversionFunctionResult = = ECreateSourcesForConversionFunctionResult : : Continue )
2022-07-13 13:52:41 -04:00
{
2023-03-22 13:07:37 -04:00
if ( ! Binding . DestinationPath . IsEmpty ( ) )
2023-03-17 16:33:42 -04:00
{
2023-03-22 13:07:37 -04:00
if ( ! Binding . SourcePath . IsEmpty ( ) )
{
AddWarningForPropertyWithMVVMAndLegacyBinding ( Binding . SourcePath ) ;
}
if ( ! CreateSourceContextForPropertyPath ( Binding . DestinationPath , false , FName ( ) ) )
{
bIsBindingsValid = false ;
continue ;
}
2023-03-17 16:33:42 -04:00
}
2023-03-22 13:07:37 -04:00
else
2022-10-06 20:08:12 -04:00
{
2023-03-22 13:07:37 -04:00
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " BindingEmptyDestinationPath " , " The binding doesn't have a Destination or a Conversion function. " ) , EBindingMessageType : : Error ) ;
2022-10-06 20:08:12 -04:00
bIsBindingsValid = false ;
continue ;
}
2022-07-13 13:52:41 -04:00
}
2023-03-22 13:07:37 -04:00
else if ( ConversionFunctionResult = = ECreateSourcesForConversionFunctionResult : : Failed )
{
bIsBindingsValid = false ;
continue ;
}
2022-07-13 13:52:41 -04:00
}
}
return bIsBindingsValid ;
}
2022-06-08 13:25:42 -04:00
bool FMVVMViewBlueprintCompiler : : PreCompileSourceCreators ( UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView )
{
if ( ! bAreSourcesCreatorValid )
{
return false ;
}
2022-07-21 14:19:45 -04:00
for ( FCompilerSourceCreatorContext & SourceCreatorContext : CompilerSourceCreatorContexts )
2022-06-08 13:25:42 -04:00
{
FMVVMViewClass_SourceCreator CompiledSourceCreator ;
if ( SourceCreatorContext . Type = = ECompilerSourceCreatorType : : ViewModel )
{
const FMVVMBlueprintViewModelContext & ViewModelContext = SourceCreatorContext . ViewModelContext ;
2022-07-13 13:52:41 -04:00
checkf ( ViewModelContext . GetViewModelClass ( ) , TEXT ( " The viewmodel class is invalid. It was checked in CreateSourceList " ) ) ;
2022-06-08 13:25:42 -04:00
if ( ViewModelContext . GetViewModelClass ( ) - > HasAllClassFlags ( CLASS_Deprecated ) )
{
2023-02-10 10:45:24 -05:00
AddErrorForViewModel ( ViewModelContext , FText : : Format ( LOCTEXT ( " ViewModelTypeDeprecated " , " Viewmodel class '{0}' is deprecated and should not be used. Please update it in the View Models panel. " ) ,
2022-10-06 20:08:12 -04:00
ViewModelContext . GetViewModelClass ( ) - > GetDisplayNameText ( )
) ) ;
2022-06-08 13:25:42 -04:00
}
2023-02-10 10:45:24 -05:00
if ( ! GetAllowedContextCreationType ( ViewModelContext . GetViewModelClass ( ) ) . Contains ( ViewModelContext . CreationType ) )
{
AddErrorForViewModel ( ViewModelContext , FText : : Format ( LOCTEXT ( " ViewModelContextCreationTypeInvalid " , " Viewmodel '{0}' has an invalidate creation type. You can change it in the View Models panel. " ) ,
ViewModelContext . GetViewModelClass ( ) - > GetDisplayNameText ( )
) ) ;
bAreSourcesCreatorValid = false ;
continue ;
}
2022-06-08 13:25:42 -04:00
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual )
{
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance )
{
if ( ViewModelContext . GetViewModelClass ( ) - > HasAllClassFlags ( CLASS_Abstract ) )
{
2023-02-10 10:45:24 -05:00
AddErrorForViewModel ( ViewModelContext , FText : : Format ( LOCTEXT ( " ViewModelTypeAbstract " , " Viewmodel class '{0}' is abstract and can't be created. You can change it in the View Models panel. " ) ,
2022-10-06 20:08:12 -04:00
ViewModelContext . GetViewModelClass ( ) - > GetDisplayNameText ( )
) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : PropertyPath )
{
if ( ViewModelContext . ViewModelPropertyPath . IsEmpty ( ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelInvalidGetter " , " Viewmodel has an invalid Getter. You can select a new one in the View Models panel. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = true ;
continue ;
}
2023-03-22 13:07:37 -04:00
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = AddObjectFieldPath ( BindingLibraryCompiler , Class , ViewModelContext . ViewModelPropertyPath , ViewModelContext . GetViewModelClass ( ) ) ;
2022-06-08 13:25:42 -04:00
if ( ReadFieldPathResult . HasError ( ) )
{
2023-03-22 13:07:37 -04:00
AddErrorForViewModel ( ViewModelContext , ReadFieldPathResult . GetError ( ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
SourceCreatorContext . ReadPropertyPath = ReadFieldPathResult . StealValue ( ) ;
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : GlobalViewModelCollection )
{
if ( ViewModelContext . GlobalViewModelIdentifier . IsNone ( ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidGlobalIdentifier " , " Viewmodel doesn't have a valid Global identifier. You can specify a new one in the Viewmodels panel. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
}
2023-02-09 12:14:47 -05:00
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Resolver )
{
if ( ! ViewModelContext . Resolver )
{
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidResolver " , " Viewmodel doesn't have a valid Resolver. You can specify a new one in the Viewmodels panel. " ) ) ;
bAreSourcesCreatorValid = false ;
continue ;
}
}
2022-06-08 13:25:42 -04:00
else
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelInvalidCreationType " , " Viewmodel doesn't have a valid creation type. You can select one in the Viewmodels panel. " ) ) ;
bAreSourcesCreatorValid = false ;
2022-06-08 13:25:42 -04:00
continue ;
}
}
}
return bAreSourcesCreatorValid ;
}
bool FMVVMViewBlueprintCompiler : : CompileSourceCreators ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView , UMVVMViewClass * ViewExtension )
{
if ( ! bAreSourcesCreatorValid )
{
return false ;
}
2022-07-21 14:19:45 -04:00
for ( const FCompilerSourceCreatorContext & SourceCreatorContext : CompilerSourceCreatorContexts )
2022-06-08 13:25:42 -04:00
{
2023-03-22 13:07:37 -04:00
const FMVVMBlueprintViewModelContext & ViewModelContext = SourceCreatorContext . ViewModelContext ;
FMVVMViewClass_SourceCreator CompiledSourceCreator ;
ensure ( ViewModelContext . GetViewModelClass ( ) & & ViewModelContext . GetViewModelClass ( ) - > ImplementsInterface ( UNotifyFieldValueChanged : : StaticClass ( ) ) ) ;
CompiledSourceCreator . ExpectedSourceType = ViewModelContext . GetViewModelClass ( ) ;
CompiledSourceCreator . PropertyName = ViewModelContext . GetViewModelName ( ) ;
bool bCanBeSet = false ;
bool CanBeEvaluated = false ;
bool bIsOptional = false ;
bool bCreateInstance = false ;
bool IsUserWidgetProperty = false ;
2022-06-08 13:25:42 -04:00
if ( SourceCreatorContext . Type = = ECompilerSourceCreatorType : : ViewModel )
{
2023-03-22 13:07:37 -04:00
IsUserWidgetProperty = true ;
bCanBeSet = ViewModelContext . bCreateSetterFunction ;
2022-06-08 13:25:42 -04:00
if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual )
{
2023-03-22 13:07:37 -04:00
bCanBeSet = true ;
bIsOptional = true ;
2022-06-08 13:25:42 -04:00
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : CreateInstance )
{
2023-03-22 13:07:37 -04:00
bCreateInstance = true ;
2022-06-08 13:25:42 -04:00
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : PropertyPath )
{
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( SourceCreatorContext . ReadPropertyPath ) ;
if ( CompiledFieldPath = = nullptr )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelInvalidInitializationBindingNotGenerated " , " The viewmodel initialization binding was not generated. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
2023-03-22 13:07:37 -04:00
CompiledSourceCreator . FieldPath = * CompiledFieldPath ;
bIsOptional = ViewModelContext . bOptional ;
2022-06-08 13:25:42 -04:00
}
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : GlobalViewModelCollection )
{
if ( ViewModelContext . GlobalViewModelIdentifier . IsNone ( ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelInvalidGlobalIdentifier " , " The viewmodel doesn't have a valid Global identifier. You can specify a new one in the Viewmodels panel. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
FMVVMViewModelContext GlobalViewModelInstance ;
GlobalViewModelInstance . ContextClass = ViewModelContext . GetViewModelClass ( ) ;
GlobalViewModelInstance . ContextName = ViewModelContext . GlobalViewModelIdentifier ;
if ( ! GlobalViewModelInstance . IsValid ( ) )
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelCouldNotBeCreated " , " The context for viewmodel could not be created. You can change the viewmodel in the Viewmodels panel. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
2023-03-22 13:07:37 -04:00
CompiledSourceCreator . GlobalViewModelInstance = MoveTemp ( GlobalViewModelInstance ) ;
bIsOptional = ViewModelContext . bOptional ;
2022-06-08 13:25:42 -04:00
}
2023-02-09 12:14:47 -05:00
else if ( ViewModelContext . CreationType = = EMVVMBlueprintViewModelContextCreationType : : Resolver )
{
2023-02-11 12:38:02 -05:00
UMVVMViewModelContextResolver * Resolver = DuplicateObject ( ViewModelContext . Resolver . Get ( ) , ViewExtension ) ;
2023-02-09 12:14:47 -05:00
if ( ! Resolver )
{
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewmodelFailedResolverDuplicate " , " Internal error. The resolver could not be dupliated. " ) ) ;
bAreSourcesCreatorValid = false ;
continue ;
}
2023-03-22 13:07:37 -04:00
CompiledSourceCreator . Resolver = Resolver ;
bIsOptional = ViewModelContext . bOptional ;
2023-02-09 12:14:47 -05:00
}
2022-06-08 13:25:42 -04:00
else
{
2022-10-06 20:08:12 -04:00
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelWithoutValidCreationType " , " The viewmodel doesn't have a valid creation type. " ) ) ;
2022-06-08 13:25:42 -04:00
bAreSourcesCreatorValid = false ;
continue ;
}
}
2023-03-22 13:07:37 -04:00
else if ( SourceCreatorContext . Type = = ECompilerSourceCreatorType : : ViewModelDynamic )
{
const FMVVMVCompiledFieldPath * CompiledFieldPath = CompileResult . FieldPaths . Find ( SourceCreatorContext . ReadPropertyPath ) ;
if ( CompiledFieldPath = = nullptr )
{
AddErrorForViewModel ( ViewModelContext , LOCTEXT ( " ViewModelInvalidInitializationBindingNotGenerated " , " The viewmodel initialization binding was not generated. " ) ) ;
bAreSourcesCreatorValid = false ;
continue ;
}
CanBeEvaluated = true ;
CompiledSourceCreator . FieldPath = * CompiledFieldPath ;
2023-04-06 16:59:04 -04:00
CompiledSourceCreator . ParentSourceName = SourceCreatorContext . DynamicParentSourceName ;
2023-03-22 13:07:37 -04:00
}
CompiledSourceCreator . Flags = 0 ;
CompiledSourceCreator . Flags | = bCreateInstance ? ( uint8 ) FMVVMViewClass_SourceCreator : : ESourceFlags : : TypeCreateInstance : 0 ;
CompiledSourceCreator . Flags | = IsUserWidgetProperty ? ( uint8 ) FMVVMViewClass_SourceCreator : : ESourceFlags : : IsUserWidgetProperty : 0 ;
CompiledSourceCreator . Flags | = bIsOptional ? ( uint8 ) FMVVMViewClass_SourceCreator : : ESourceFlags : : IsOptional : 0 ;
CompiledSourceCreator . Flags | = bCanBeSet ? ( uint8 ) FMVVMViewClass_SourceCreator : : ESourceFlags : : CanBeSet : 0 ;
CompiledSourceCreator . Flags | = CanBeEvaluated ? ( uint8 ) FMVVMViewClass_SourceCreator : : ESourceFlags : : CanBeEvaluated : 0 ;
ViewExtension - > SourceCreators . Add ( MoveTemp ( CompiledSourceCreator ) ) ;
2022-06-08 13:25:42 -04:00
}
return bAreSourcesCreatorValid ;
}
bool FMVVMViewBlueprintCompiler : : PreCompileBindings ( UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView )
{
2022-07-13 13:52:41 -04:00
if ( ! bAreSourceContextsValid | | ! bIsBindingsValid )
2022-06-08 13:25:42 -04:00
{
return false ;
}
2022-07-13 13:52:41 -04:00
for ( const FBindingSourceContext & BindingSourceContext : BindingSourceContexts )
2022-06-08 13:25:42 -04:00
{
2022-07-13 13:52:41 -04:00
FMVVMBlueprintViewBinding * BindingPtr = BlueprintView - > GetBindingAt ( BindingSourceContext . BindingIndex ) ;
check ( BindingPtr ) ;
2022-06-08 13:25:42 -04:00
FMVVMBlueprintViewBinding & Binding = * BindingPtr ;
FMVVMViewBlueprintCompiler * Self = this ;
2023-03-22 13:07:37 -04:00
auto AddFieldId = [ Self ] ( const UClass * SourceContextClass , bool bNotifyFieldValueChangedRequired , EMVVMBindingMode BindingMode , FName FieldToListenTo ) - > TValueOrError < FCompiledBindingLibraryCompiler : : FFieldIdHandle , FText >
2022-06-08 13:25:42 -04:00
{
if ( ! IsOneTimeBinding ( BindingMode ) & & bNotifyFieldValueChangedRequired )
{
return Self - > BindingLibraryCompiler . AddFieldId ( SourceContextClass , FieldToListenTo ) ;
}
return MakeValue ( FCompiledBindingLibraryCompiler : : FFieldIdHandle ( ) ) ;
} ;
2023-01-31 10:21:03 -05:00
if ( Binding . bOverrideExecutionMode )
{
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > IsExecutionModeAllowed ( Binding . OverrideExecutionMode ) )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , LOCTEXT ( " NotAllowedExecutionMode " , " The binding has a restricted execution mode. " ) , EBindingMessageType : : Error ) ;
2023-01-31 10:21:03 -05:00
}
}
2022-10-06 20:08:12 -04:00
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldIdHandle , FText > AddFieldResult = AddFieldId ( BindingSourceContext . SourceClass , true , Binding . BindingType , BindingSourceContext . FieldId . GetFieldName ( ) ) ;
2022-07-13 13:52:41 -04:00
if ( AddFieldResult . HasError ( ) )
2022-06-08 13:25:42 -04:00
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , FText : : Format ( LOCTEXT ( " CouldNotCreateSource " , " Could not create source. {0} " ) ,
AddFieldResult . GetError ( ) ) , EBindingMessageType : : Error ) ;
2022-07-13 13:52:41 -04:00
bIsBindingsValid = false ;
continue ;
2022-06-08 13:25:42 -04:00
}
2022-07-13 13:52:41 -04:00
TArray < UE : : MVVM : : FMVVMConstFieldVariant > SetterPath ;
{
2023-06-02 15:16:58 -04:00
const FMVVMBlueprintPropertyPath & DestinationPath = BindingSourceContext . bIsForwardBinding ? Binding . DestinationPath : Binding . SourcePath ;
SetterPath = CreateBindingDestinationPath ( BlueprintView , Class , DestinationPath ) ;
2022-07-13 13:52:41 -04:00
if ( ! IsPropertyPathValid ( SetterPath ) )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , FText : : Format ( LOCTEXT ( " PropertyPathIsInvalid " , " The property path '{0}' is invalid. " ) ,
PropertyPathToText ( BlueprintView , DestinationPath ) ) ,
EBindingMessageType : : Error
2022-10-06 20:08:12 -04:00
) ;
2022-07-13 13:52:41 -04:00
bIsBindingsValid = false ;
continue ;
}
}
2022-07-21 12:13:57 -04:00
FMemberReference ConversionFunctionReference = BindingSourceContext . bIsForwardBinding ? Binding . Conversion . SourceToDestinationFunction : Binding . Conversion . DestinationToSourceFunction ;
FName ConversionFunctionWrapper = BindingSourceContext . bIsForwardBinding ? Binding . Conversion . SourceToDestinationWrapper : Binding . Conversion . DestinationToSourceWrapper ;
if ( ! ConversionFunctionWrapper . IsNone ( ) )
2022-07-13 13:52:41 -04:00
{
2022-07-21 12:13:57 -04:00
ConversionFunctionReference . SetSelfMember ( ConversionFunctionWrapper ) ;
2022-07-13 13:52:41 -04:00
}
2022-07-21 12:13:57 -04:00
const UFunction * ConversionFunction = ConversionFunctionReference . ResolveMember < UFunction > ( Class ) ;
2022-08-24 13:14:51 -04:00
if ( ! ConversionFunctionWrapper . IsNone ( ) & & ConversionFunction = = nullptr )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView , FText : : Format ( LOCTEXT ( " ConversionFunctionNotFound " , " The conversion function '{0}' could not be found. " ) ,
FText : : FromName ( ConversionFunctionReference . GetMemberName ( ) ) ) ,
EBindingMessageType : : Error
2022-10-06 20:08:12 -04:00
) ;
2022-08-24 13:14:51 -04:00
bIsBindingsValid = false ;
continue ;
}
2022-07-13 13:52:41 -04:00
2023-03-22 13:07:37 -04:00
TValueOrError < FCompiledBinding , FText > AddBindingResult = CreateCompiledBinding ( Class , BindingSourceContext . PropertyPath , SetterPath , ConversionFunction , BindingSourceContext . bIsComplexBinding ) ;
2022-07-13 13:52:41 -04:00
if ( AddBindingResult . HasError ( ) )
{
2023-03-17 16:33:42 -04:00
AddMessageForBinding ( Binding , BlueprintView ,
FText : : Format ( LOCTEXT ( " CouldNotCreateBinding " , " Could not create binding. {0} " ) , AddBindingResult . GetError ( ) ) ,
EBindingMessageType : : Error
2022-10-06 20:08:12 -04:00
) ;
2022-07-13 13:52:41 -04:00
bIsBindingsValid = false ;
continue ;
}
2023-03-22 13:07:37 -04:00
FCompilerBinding NewBinding ;
2022-07-13 13:52:41 -04:00
NewBinding . BindingIndex = BindingSourceContext . BindingIndex ;
2023-01-04 15:47:23 -05:00
NewBinding . UserWidgetPropertyContextIndex = BindingSourceContext . UserWidgetPropertyContextIndex ;
2023-03-06 09:41:11 -05:00
NewBinding . SourceCreatorContextIndex = BindingSourceContext . SourceCreatorContextIndex ;
2022-07-13 13:52:41 -04:00
NewBinding . bSourceIsUserWidget = BindingSourceContext . bIsRootWidget ;
NewBinding . bFieldIdNeeded = ! IsOneTimeBinding ( Binding . BindingType ) ;
2023-03-22 13:07:37 -04:00
NewBinding . bIsForwardBinding = BindingSourceContext . bIsForwardBinding ;
NewBinding . CompiledBinding = AddBindingResult . StealValue ( ) ;
NewBinding . FieldIdHandle = AddFieldResult . StealValue ( ) ;
2022-07-21 14:19:45 -04:00
CompilerBindings . Emplace ( NewBinding ) ;
2022-06-08 13:25:42 -04:00
}
return bIsBindingsValid ;
}
bool FMVVMViewBlueprintCompiler : : CompileBindings ( const FCompiledBindingLibraryCompiler : : FCompileResult & CompileResult , UWidgetBlueprintGeneratedClass * Class , UMVVMBlueprintView * BlueprintView , UMVVMViewClass * ViewExtension )
{
if ( ! bIsBindingsValid )
{
return false ;
}
2022-11-09 21:03:55 -05:00
IConsoleVariable * CVarDefaultExecutionMode = IConsoleManager : : Get ( ) . FindConsoleVariable ( TEXT ( " MVVM.DefaultExecutionMode " ) ) ;
if ( ! CVarDefaultExecutionMode )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " CantFindDefaultExecutioMode " , " The default execution mode cannot be found. " ) . ToString ( ) ) ;
return false ;
}
2022-07-21 14:19:45 -04:00
for ( const FCompilerBinding & CompileBinding : CompilerBindings )
2022-06-08 13:25:42 -04:00
{
2023-03-22 13:07:37 -04:00
// PropertyBinding needs a valid CompileBinding.BindingIndex.
check ( CompileBinding . Type ! = ECompilerBindingType : : PropertyBinding | | ( CompileBinding . BindingIndex > = 0 & & CompileBinding . BindingIndex < BlueprintView - > GetNumBindings ( ) ) ) ;
FMVVMBlueprintViewBinding * ViewBinding = CompileBinding . Type = = ECompilerBindingType : : PropertyBinding
? BlueprintView - > GetBindingAt ( CompileBinding . BindingIndex )
: nullptr ;
2022-06-08 13:25:42 -04:00
FMVVMViewClass_CompiledBinding NewBinding ;
2023-03-06 09:41:11 -05:00
const bool bIsSourceSelf = CompileBinding . bSourceIsUserWidget ;
2022-12-13 18:24:52 -05:00
if ( ! bIsSourceSelf )
{
2023-03-06 09:41:11 -05:00
if ( CompilerUserWidgetPropertyContexts . IsValidIndex ( CompileBinding . UserWidgetPropertyContextIndex ) )
{
NewBinding . SourcePropertyName = CompilerUserWidgetPropertyContexts [ CompileBinding . UserWidgetPropertyContextIndex ] . PropertyName ;
}
else if ( CompilerSourceCreatorContexts . IsValidIndex ( CompileBinding . SourceCreatorContextIndex ) )
{
NewBinding . SourcePropertyName = CompilerSourceCreatorContexts [ CompileBinding . SourceCreatorContextIndex ] . ViewModelContext . GetViewModelName ( ) ;
}
else
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * LOCTEXT ( " InvalidSourceInternal " , " Internal error. The binding doesn't have a valid source. " ) . ToString ( ) ) ;
return false ;
}
2022-12-13 18:24:52 -05:00
}
2022-06-08 13:25:42 -04:00
2023-03-22 13:07:37 -04:00
auto AddErroMessage = [ this , ViewBinding , BlueprintView ] ( const FText & ErrorMessage )
{
if ( ViewBinding )
{
AddMessageForBinding ( * ViewBinding , BlueprintView , ErrorMessage , EBindingMessageType : : Error ) ;
}
else
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * ErrorMessage . ToString ( ) ) ;
}
} ;
2022-06-08 13:25:42 -04:00
const FMVVMVCompiledFieldId * CompiledFieldId = CompileResult . FieldIds . Find ( CompileBinding . FieldIdHandle ) ;
if ( CompiledFieldId = = nullptr & & CompileBinding . bFieldIdNeeded )
{
2023-03-22 13:07:37 -04:00
AddErroMessage ( FText : : Format ( LOCTEXT ( " FieldIdNotGenerated " , " Could not generate field ID for property '{0}'. " ) , FText : : FromName ( NewBinding . SourcePropertyName ) ) ) ;
2022-06-08 13:25:42 -04:00
bIsBindingsValid = false ;
continue ;
}
2023-03-22 13:07:37 -04:00
const FMVVMVCompiledBinding * CompiledBinding = CompileResult . Bindings . Find ( CompileBinding . CompiledBinding . BindingHandle ) ;
if ( CompiledBinding = = nullptr & & CompileBinding . Type ! = ECompilerBindingType : : ViewModelDynamic )
2022-06-08 13:25:42 -04:00
{
2023-03-22 13:07:37 -04:00
AddErroMessage ( LOCTEXT ( " CompiledBindingNotGenerated " , " Could not generate compiled binding. " ) ) ;
2022-06-08 13:25:42 -04:00
bIsBindingsValid = false ;
continue ;
}
2022-08-24 13:14:51 -04:00
bool bIsOptional = false ;
2022-12-13 18:24:52 -05:00
if ( ! bIsSourceSelf )
2022-08-24 13:14:51 -04:00
{
2023-03-22 13:07:37 -04:00
const FMVVMBlueprintViewModelContext * ViewModelContext = nullptr ;
2023-03-06 09:41:11 -05:00
if ( CompilerUserWidgetPropertyContexts . IsValidIndex ( CompileBinding . UserWidgetPropertyContextIndex ) )
{
2023-03-22 13:07:37 -04:00
if ( CompilerUserWidgetPropertyContexts [ CompileBinding . UserWidgetPropertyContextIndex ] . ViewModelId . IsValid ( ) )
{
ViewModelContext = BlueprintView - > FindViewModel ( CompilerUserWidgetPropertyContexts [ CompileBinding . UserWidgetPropertyContextIndex ] . ViewModelId ) ;
if ( ViewModelContext = = nullptr )
{
AddErroMessage ( LOCTEXT ( " CompiledBindingWithInvalidIVewModelId " , " Internal error: the viewmodel became invalid. " ) ) ;
bIsBindingsValid = false ;
continue ;
}
}
2023-03-06 09:41:11 -05:00
}
else if ( CompilerSourceCreatorContexts . IsValidIndex ( CompileBinding . SourceCreatorContextIndex ) )
{
2023-03-22 13:07:37 -04:00
ViewModelContext = & CompilerSourceCreatorContexts [ CompileBinding . SourceCreatorContextIndex ] . ViewModelContext ;
2023-03-06 09:41:11 -05:00
}
2023-03-22 13:07:37 -04:00
if ( ViewModelContext )
2022-08-24 13:14:51 -04:00
{
2023-03-22 13:07:37 -04:00
bIsOptional = ViewModelContext - > bOptional | | ViewModelContext - > CreationType = = EMVVMBlueprintViewModelContextCreationType : : Manual ;
2022-08-24 13:14:51 -04:00
}
}
2023-03-22 13:07:37 -04:00
if ( CompileBinding . Type = = ECompilerBindingType : : ViewModelDynamic )
{
int32 FoundSourceCreatorIndex = ViewExtension - > SourceCreators . IndexOfByPredicate ( [ LookFor = CompileBinding . DynamicViewModelName ] ( const FMVVMViewClass_SourceCreator & Other )
{
return Other . GetSourceName ( ) = = LookFor ;
} ) ;
2022-06-08 13:25:42 -04:00
2023-03-22 13:07:37 -04:00
check ( FoundSourceCreatorIndex < std : : numeric_limits < int8 > : : max ( ) ) ; // the index is saved as a int8
NewBinding . EvaluateSourceCreatorIndex = FoundSourceCreatorIndex ;
if ( NewBinding . EvaluateSourceCreatorIndex = = INDEX_NONE )
{
FText ErrorMessage = FText : : Format ( LOCTEXT ( " CompiledViewModelDynamicBindingNotGenerated " , " Was not able to find the source for {0}. " ) , FText : : FromName ( CompileBinding . DynamicViewModelName ) ) ;
WidgetBlueprintCompilerContext . MessageLog . Error ( * ErrorMessage . ToString ( ) ) ;
bIsBindingsValid = false ;
continue ;
}
}
FText WrongBindingTypeInternalErrorMessage = LOCTEXT ( " WrongBindingTypeInternalError " , " Internal Error. Wrong binding type. " ) ;
// FieldId can be null if it's a one time binding
NewBinding . FieldId = CompiledFieldId ? * CompiledFieldId : FMVVMVCompiledFieldId ( ) ;
NewBinding . Binding = CompiledBinding ? * CompiledBinding : FMVVMVCompiledBinding ( ) ;
2022-06-08 13:25:42 -04:00
NewBinding . Flags = 0 ;
2023-03-22 13:07:37 -04:00
if ( ViewBinding )
{
if ( CompileBinding . Type ! = ECompilerBindingType : : PropertyBinding )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * WrongBindingTypeInternalErrorMessage . ToString ( ) ) ;
bIsBindingsValid = false ;
continue ;
}
NewBinding . ExecutionMode = ViewBinding - > bOverrideExecutionMode ? ViewBinding - > OverrideExecutionMode : ( EMVVMExecutionMode ) CVarDefaultExecutionMode - > GetInt ( ) ; ;
NewBinding . EditorId = ViewBinding - > BindingId ;
NewBinding . Flags | = ( ViewBinding - > bEnabled ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : EnabledByDefault : 0 ;
NewBinding . Flags | = ( CompileBinding . bIsForwardBinding ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : ExecuteAtInitialization : 0 ;
NewBinding . Flags | = ( IsOneTimeBinding ( ViewBinding - > BindingType ) ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : OneTime : 0 ;
NewBinding . Flags | = ( bIsOptional ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : ViewModelOptional : 0 ;
NewBinding . Flags | = ( ViewBinding - > bOverrideExecutionMode ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : OverrideExecuteMode : 0 ;
NewBinding . Flags | = ( bIsSourceSelf ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : SourceObjectIsSelf : 0 ;
}
else
{
if ( CompileBinding . Type ! = ECompilerBindingType : : ViewModelDynamic )
{
WidgetBlueprintCompilerContext . MessageLog . Error ( * WrongBindingTypeInternalErrorMessage . ToString ( ) ) ;
bIsBindingsValid = false ;
continue ;
}
NewBinding . ExecutionMode = EMVVMExecutionMode : : Immediate ;
NewBinding . EditorId = FGuid ( ) ;
NewBinding . Flags | = FMVVMViewClass_CompiledBinding : : EBindingFlags : : EnabledByDefault ;
NewBinding . Flags | = ( bIsOptional ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : ViewModelOptional : 0 ;
NewBinding . Flags | = FMVVMViewClass_CompiledBinding : : EBindingFlags : : OverrideExecuteMode ; // The mode needs to be Immediate.
NewBinding . Flags | = ( bIsSourceSelf ) ? FMVVMViewClass_CompiledBinding : : EBindingFlags : : SourceObjectIsSelf : 0 ;
}
2022-06-08 13:25:42 -04:00
ViewExtension - > CompiledBindings . Emplace ( MoveTemp ( NewBinding ) ) ;
}
return bIsBindingsValid ;
}
2023-03-22 13:07:37 -04:00
TValueOrError < FMVVMViewBlueprintCompiler : : FCompiledBinding , FText > FMVVMViewBlueprintCompiler : : CreateCompiledBinding ( const UWidgetBlueprintGeneratedClass * Class , TArrayView < const UE : : MVVM : : FMVVMConstFieldVariant > GetterFields , TArrayView < const UE : : MVVM : : FMVVMConstFieldVariant > SetterFields , const UFunction * ConversionFunction , bool bIsComplexBinding )
{
FCompiledBinding Result ;
if ( ConversionFunction ! = nullptr )
{
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = BindingLibraryCompiler . AddConversionFunctionFieldPath ( Class , ConversionFunction ) ;
if ( FieldPathResult . HasError ( ) )
{
return MakeError ( FText : : Format ( LOCTEXT ( " CouldNotCreateConversionFunctionFieldPath " , " Couldn't create the conversion function field path '{0}'. {1} " )
, FText : : FromString ( ConversionFunction - > GetPathName ( ) )
, FieldPathResult . GetError ( ) ) ) ;
}
Result . ConversionFunction = FieldPathResult . StealValue ( ) ;
Result . bIsConversionFunctionComplex = bIsComplexBinding ;
// Sanity check
if ( bIsComplexBinding & & ! BindingHelper : : IsValidForComplexRuntimeConversion ( ConversionFunction ) )
{
2023-06-02 15:16:58 -04:00
return MakeError ( LOCTEXT ( " ConversionFunctionIsNotComplex " , " Internal Error. The complex conversion function does not respect the prerequist. " ) ) ;
2023-03-22 13:07:37 -04:00
}
}
if ( ! Result . bIsConversionFunctionComplex )
{
2023-06-02 15:16:58 -04:00
static const FText CouldNotCreateSourceFieldPathFormat = LOCTEXT ( " CouldNotCreateSourceFieldPath " , " Couldn't create the source field path '{0}'. {1} " ) ;
2023-03-22 13:07:37 -04:00
// Generate a path to read the value at runtime
TValueOrError < TArray < FMVVMConstFieldVariant > , FText > GeneratedField = FieldPathHelper : : GenerateFieldPathList ( GetterFields , true ) ;
if ( GeneratedField . HasError ( ) )
{
2023-06-02 15:16:58 -04:00
return MakeError ( FText : : Format ( CouldNotCreateSourceFieldPathFormat
2023-03-22 13:07:37 -04:00
, : : UE : : MVVM : : FieldPathHelper : : ToText ( GetterFields )
, GeneratedField . GetError ( ) ) ) ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = BindingLibraryCompiler . AddFieldPath ( GeneratedField . GetValue ( ) , true ) ;
if ( FieldPathResult . HasError ( ) )
{
2023-06-02 15:16:58 -04:00
return MakeError ( FText : : Format ( CouldNotCreateSourceFieldPathFormat
2023-03-22 13:07:37 -04:00
, : : UE : : MVVM : : FieldPathHelper : : ToText ( GetterFields )
, FieldPathResult . GetError ( ) ) ) ;
}
Result . SourceRead = FieldPathResult . StealValue ( ) ;
}
{
static const FText CouldNotCreateDestinationFieldPathFormat = LOCTEXT ( " CouldNotCreateDestinationFieldPath " , " Couldn't create the destination field path '{0}'. {1} " ) ;
TValueOrError < TArray < FMVVMConstFieldVariant > , FText > GeneratedField = FieldPathHelper : : GenerateFieldPathList ( SetterFields , false ) ;
if ( GeneratedField . HasError ( ) )
{
return MakeError ( FText : : Format ( CouldNotCreateDestinationFieldPathFormat
, : : UE : : MVVM : : FieldPathHelper : : ToText ( SetterFields )
, GeneratedField . GetError ( ) ) ) ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > FieldPathResult = BindingLibraryCompiler . AddFieldPath ( GeneratedField . GetValue ( ) , false ) ;
if ( FieldPathResult . HasError ( ) )
{
return MakeError ( FText : : Format ( CouldNotCreateDestinationFieldPathFormat
, : : UE : : MVVM : : FieldPathHelper : : ToText ( SetterFields )
, FieldPathResult . GetError ( ) ) ) ;
}
Result . DestinationWrite = FieldPathResult . StealValue ( ) ;
}
// Generate the binding
TValueOrError < FCompiledBindingLibraryCompiler : : FBindingHandle , FText > BindingResult = Result . bIsConversionFunctionComplex
? BindingLibraryCompiler . AddComplexBinding ( Result . DestinationWrite , Result . ConversionFunction )
: BindingLibraryCompiler . AddBinding ( Result . SourceRead , Result . DestinationWrite , Result . ConversionFunction ) ;
if ( BindingResult . HasError ( ) )
{
return MakeError ( BindingResult . StealError ( ) ) ;
}
Result . BindingHandle = BindingResult . StealValue ( ) ;
return MakeValue ( Result ) ;
}
2022-06-08 13:25:42 -04:00
const FMVVMViewBlueprintCompiler : : FCompilerSourceCreatorContext * FMVVMViewBlueprintCompiler : : FindViewModelSource ( FGuid Id ) const
{
2022-07-21 14:19:45 -04:00
return CompilerSourceCreatorContexts . FindByPredicate ( [ Id ] ( const FCompilerSourceCreatorContext & Other )
2022-07-13 13:52:41 -04:00
{
return Other . Type = = ECompilerSourceCreatorType : : ViewModel ? Other . ViewModelContext . GetViewModelId ( ) = = Id : false ;
} ) ;
2022-06-08 13:25:42 -04:00
}
2023-03-22 13:07:37 -04:00
TValueOrError < FMVVMViewBlueprintCompiler : : FBindingSourceContext , FText > FMVVMViewBlueprintCompiler : : CreateBindingSourceContext ( const UMVVMBlueprintView * BlueprintView , const UWidgetBlueprintGeneratedClass * Class , const FMVVMBlueprintPropertyPath & PropertyPath , bool bIsOneTimeBinding )
2022-06-08 13:25:42 -04:00
{
if ( PropertyPath . IsEmpty ( ) )
{
2022-07-13 13:52:41 -04:00
ensureAlways ( false ) ;
2022-12-13 18:24:52 -05:00
return MakeError ( LOCTEXT ( " EmptyPropertyPath " , " Empty property path found. This is ilegal. " ) ) ;
2022-06-08 13:25:42 -04:00
}
2022-07-13 13:52:41 -04:00
FBindingSourceContext Result ;
2022-06-08 13:25:42 -04:00
if ( PropertyPath . IsFromViewModel ( ) )
{
Result . bIsRootWidget = false ;
const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) ;
check ( SourceViewModelContext ) ;
const FName SourceName = SourceViewModelContext - > GetViewModelName ( ) ;
2023-01-04 15:47:23 -05:00
Result . UserWidgetPropertyContextIndex = CompilerUserWidgetPropertyContexts . IndexOfByPredicate ( [ SourceName ] ( const FCompilerUserWidgetPropertyContext & Other ) { return Other . PropertyName = = SourceName ; } ) ;
2022-11-25 13:55:44 -05:00
check ( Result . UserWidgetPropertyContextIndex ! = INDEX_NONE ) ;
2022-06-08 13:25:42 -04:00
2023-01-04 15:47:23 -05:00
Result . SourceClass = CompilerUserWidgetPropertyContexts [ Result . UserWidgetPropertyContextIndex ] . Class ;
Result . PropertyPath = CreatePropertyPath ( Class , CompilerUserWidgetPropertyContexts [ Result . UserWidgetPropertyContextIndex ] . PropertyName , PropertyPath . GetFields ( ) ) ;
2022-06-08 13:25:42 -04:00
}
else if ( PropertyPath . IsFromWidget ( ) )
{
const FName SourceName = PropertyPath . GetWidgetName ( ) ;
Result . bIsRootWidget = SourceName = = Class - > ClassGeneratedBy - > GetFName ( ) ;
if ( Result . bIsRootWidget )
{
2022-11-25 13:55:44 -05:00
Result . UserWidgetPropertyContextIndex = INDEX_NONE ;
2022-12-13 18:24:52 -05:00
Result . SourceClass = const_cast < UWidgetBlueprintGeneratedClass * > ( Class ) ;
2022-06-08 13:25:42 -04:00
Result . PropertyPath = CreatePropertyPath ( Class , FName ( ) , PropertyPath . GetFields ( ) ) ;
}
else
{
2023-01-04 15:47:23 -05:00
Result . UserWidgetPropertyContextIndex = CompilerUserWidgetPropertyContexts . IndexOfByPredicate ( [ SourceName ] ( const FCompilerUserWidgetPropertyContext & Other ) { return Other . PropertyName = = SourceName ; } ) ;
2022-11-25 13:55:44 -05:00
check ( Result . UserWidgetPropertyContextIndex ! = INDEX_NONE ) ;
2023-01-04 15:47:23 -05:00
Result . SourceClass = CompilerUserWidgetPropertyContexts [ Result . UserWidgetPropertyContextIndex ] . Class ;
Result . PropertyPath = CreatePropertyPath ( Class , CompilerUserWidgetPropertyContexts [ Result . UserWidgetPropertyContextIndex ] . PropertyName , PropertyPath . GetFields ( ) ) ;
2022-06-08 13:25:42 -04:00
}
}
else
{
ensureAlwaysMsgf ( false , TEXT ( " Not supported yet. " ) ) ;
}
2022-07-13 13:52:41 -04:00
// The path may contains another INotifyFieldValueChanged
2023-03-22 13:07:37 -04:00
TValueOrError < FieldPathHelper : : FParsedNotifyBindingInfo , FText > BindingInfoResult = FieldPathHelper : : GetNotifyBindingInfoFromFieldPath ( Class , Result . PropertyPath ) ;
2022-07-13 13:52:41 -04:00
if ( BindingInfoResult . HasError ( ) )
{
return MakeError ( BindingInfoResult . StealError ( ) ) ;
}
2023-03-22 13:07:37 -04:00
const FieldPathHelper : : FParsedNotifyBindingInfo & BindingInfo = BindingInfoResult . GetValue ( ) ;
2022-07-13 13:52:41 -04:00
Result . FieldId = BindingInfo . NotifyFieldId ;
2023-03-22 13:07:37 -04:00
if ( BindingInfo . ViewModelIndex < 1 & & BindingInfo . NotifyFieldClass & & Result . SourceClass )
2022-07-13 13:52:41 -04:00
{
2023-03-22 13:07:37 -04:00
if ( ! Result . SourceClass - > IsChildOf ( BindingInfo . NotifyFieldClass ) )
2023-03-06 09:41:11 -05:00
{
2023-03-22 13:07:37 -04:00
return MakeError ( LOCTEXT ( " InvalidNotifyFieldClassInternal " , " Internal error. The viewmodel class doesn't matches. " ) ) ;
2023-03-06 09:41:11 -05:00
}
2023-03-22 13:07:37 -04:00
}
// 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 & & ! bIsOneTimeBinding )
{
if ( ! GetDefault < UMVVMDeveloperProjectSettings > ( ) - > bAllowLongSourcePath )
2023-03-06 09:41:11 -05:00
{
2023-03-22 13:07:37 -04:00
return MakeError ( LOCTEXT ( " DynamicSourceEntryNotSupport " , " Long source entry is not supported. Add the viewmodel manually. " ) ) ;
}
2023-04-06 16:59:04 -04:00
//NB. The for loop order is important. The order is used in UMVVMView::SetViewModelInternal when enabling the bindings
2023-03-22 13:07:37 -04:00
int32 SourceCreatorContextIndex = INDEX_NONE ;
for ( int32 DynamicIndex = 1 ; DynamicIndex < = BindingInfo . ViewModelIndex ; + + DynamicIndex )
{
if ( ! Result . PropertyPath . IsValidIndex ( DynamicIndex ) )
2023-03-06 09:41:11 -05:00
{
2023-03-22 13:07:37 -04:00
return MakeError ( LOCTEXT ( " DynamicSourceEntryInternalIndex " , " Internal error. The source index is not valid. " ) ) ;
2023-03-06 09:41:11 -05:00
}
2022-07-13 13:52:41 -04:00
2023-03-22 13:07:37 -04:00
FName NewSourceName ;
2023-04-06 16:59:04 -04:00
FName ParentSourceName ;
2023-03-22 13:07:37 -04:00
FString NewSourcePropertyPath ;
2023-03-06 09:41:11 -05:00
{
2023-03-22 13:07:37 -04:00
TStringBuilder < 512 > PropertyPathBuilder ;
TStringBuilder < 512 > DynamicNameBuilder ;
for ( int32 Index = 0 ; Index < = DynamicIndex ; + + Index )
{
if ( Index > 0 )
{
PropertyPathBuilder < < TEXT ( ' . ' ) ;
DynamicNameBuilder < < TEXT ( ' _ ' ) ;
}
PropertyPathBuilder < < Result . PropertyPath [ Index ] . GetName ( ) ;
DynamicNameBuilder < < Result . PropertyPath [ Index ] . GetName ( ) ;
2023-04-06 16:59:04 -04:00
if ( Index = = DynamicIndex - 1 )
{
ParentSourceName = FName ( DynamicNameBuilder . ToString ( ) ) ;
}
2023-03-22 13:07:37 -04:00
}
2022-07-13 13:52:41 -04:00
2023-03-22 13:07:37 -04:00
NewSourceName = FName ( DynamicNameBuilder . ToString ( ) ) ;
NewSourcePropertyPath = PropertyPathBuilder . ToString ( ) ;
}
2023-03-06 09:41:11 -05:00
2023-03-22 13:07:37 -04:00
// Did we already create the new source?
int32 PreviousSourceCreatorContextIndex = SourceCreatorContextIndex ;
SourceCreatorContextIndex = CompilerSourceCreatorContexts . IndexOfByPredicate ( [ NewSourceName ] ( const FCompilerSourceCreatorContext & Other )
{
return Other . Type = = ECompilerSourceCreatorType : : ViewModelDynamic
& & Other . ViewModelContext . GetViewModelName ( ) = = NewSourceName ;
} ) ;
if ( SourceCreatorContextIndex = = INDEX_NONE )
{
// Create the new source
{
const UClass * ViewModelClass = Cast < const UClass > ( Result . PropertyPath [ DynamicIndex + 1 ] . GetOwner ( ) ) ;
if ( ! ViewModelClass )
{
return MakeError ( FText : : GetEmpty ( ) ) ;
}
FCompilerSourceCreatorContext SourceCreatorContext ;
SourceCreatorContext . Type = ECompilerSourceCreatorType : : ViewModelDynamic ;
2023-04-06 16:59:04 -04:00
SourceCreatorContext . DynamicParentSourceName = ParentSourceName ;
2023-03-22 13:07:37 -04:00
SourceCreatorContext . ViewModelContext = FMVVMBlueprintViewModelContext ( ViewModelClass , NewSourceName ) ;
SourceCreatorContext . ViewModelContext . bCreateSetterFunction = false ;
SourceCreatorContext . ViewModelContext . bOptional = false ;
SourceCreatorContext . ViewModelContext . CreationType = EMVVMBlueprintViewModelContextCreationType : : PropertyPath ;
SourceCreatorContext . ViewModelContext . ViewModelPropertyPath = NewSourcePropertyPath ;
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldPathHandle , FText > ReadFieldPathResult = AddObjectFieldPath ( BindingLibraryCompiler , Class , SourceCreatorContext . ViewModelContext . ViewModelPropertyPath , SourceCreatorContext . ViewModelContext . GetViewModelClass ( ) ) ;
if ( ReadFieldPathResult . HasError ( ) )
{
return MakeError ( FText : : Format ( LOCTEXT ( " DynamicSourceEntryInvalidPath " , " Internal error. {0}. " ) , ReadFieldPathResult . StealError ( ) ) ) ;
}
SourceCreatorContext . ReadPropertyPath = ReadFieldPathResult . StealValue ( ) ;
SourceCreatorContextIndex = CompilerSourceCreatorContexts . Add ( MoveTemp ( SourceCreatorContext ) ) ;
}
// Create the binding to update the source when it changes.
{
FCompilerBinding NewCompilerBinding ;
NewCompilerBinding . Type = ECompilerBindingType : : ViewModelDynamic ;
NewCompilerBinding . bSourceIsUserWidget = false ;
NewCompilerBinding . bIsForwardBinding = true ;
NewCompilerBinding . bFieldIdNeeded = true ;
NewCompilerBinding . DynamicViewModelName = NewSourceName ;
if ( PreviousSourceCreatorContextIndex = = INDEX_NONE )
{
NewCompilerBinding . UserWidgetPropertyContextIndex = Result . UserWidgetPropertyContextIndex ;
}
else
{
NewCompilerBinding . SourceCreatorContextIndex = PreviousSourceCreatorContextIndex ;
}
{
const UClass * OwnerClass = Cast < const UClass > ( Result . PropertyPath [ DynamicIndex ] . GetOwner ( ) ) ;
if ( ! OwnerClass )
{
return MakeError ( FText : : GetEmpty ( ) ) ;
}
TValueOrError < FCompiledBindingLibraryCompiler : : FFieldIdHandle , FText > AddFieldResult = BindingLibraryCompiler . AddFieldId ( OwnerClass , Result . PropertyPath [ DynamicIndex ] . GetName ( ) ) ;
if ( AddFieldResult . HasError ( ) )
{
return MakeError ( AddFieldResult . StealError ( ) ) ;
}
NewCompilerBinding . FieldIdHandle = AddFieldResult . StealValue ( ) ;
}
CompilerBindings . Add ( MoveTemp ( NewCompilerBinding ) ) ;
}
}
2023-03-06 09:41:11 -05:00
}
Result . SourceClass = BindingInfo . NotifyFieldClass ;
Result . FieldId = BindingInfo . NotifyFieldId ;
Result . UserWidgetPropertyContextIndex = INDEX_NONE ;
Result . SourceCreatorContextIndex = SourceCreatorContextIndex ;
Result . bIsRootWidget = false ;
2022-07-13 13:52:41 -04:00
}
return MakeValue ( Result ) ;
2022-06-08 13:25:42 -04:00
}
TArray < FMVVMConstFieldVariant > FMVVMViewBlueprintCompiler : : CreateBindingDestinationPath ( const UMVVMBlueprintView * BlueprintView , const UWidgetBlueprintGeneratedClass * Class , const FMVVMBlueprintPropertyPath & PropertyPath ) const
{
if ( PropertyPath . IsEmpty ( ) )
{
2022-07-13 13:52:41 -04:00
ensureAlwaysMsgf ( false , TEXT ( " Empty property path found. This is legal. " ) ) ;
2022-06-08 13:25:42 -04:00
return TArray < FMVVMConstFieldVariant > ( ) ;
}
if ( PropertyPath . IsFromViewModel ( ) )
{
const FMVVMBlueprintViewModelContext * SourceViewModelContext = BlueprintView - > FindViewModel ( PropertyPath . GetViewModelId ( ) ) ;
check ( SourceViewModelContext ) ;
FName DestinationName = SourceViewModelContext - > GetViewModelName ( ) ;
2023-01-04 15:47:23 -05:00
const int32 DestinationVariableContextIndex = CompilerUserWidgetPropertyContexts . IndexOfByPredicate ( [ DestinationName ] ( const FCompilerUserWidgetPropertyContext & Other ) { return Other . PropertyName = = DestinationName ; } ) ;
2022-06-08 13:25:42 -04:00
check ( DestinationVariableContextIndex ! = INDEX_NONE ) ;
2023-01-04 15:47:23 -05:00
return CreatePropertyPath ( Class , CompilerUserWidgetPropertyContexts [ DestinationVariableContextIndex ] . PropertyName , PropertyPath . GetFields ( ) ) ;
2022-06-08 13:25:42 -04:00
}
else if ( PropertyPath . IsFromWidget ( ) )
{
FName DestinationName = PropertyPath . GetWidgetName ( ) ;
checkf ( ! DestinationName . IsNone ( ) , TEXT ( " The destination should have been checked and set bAreSourceContextsValid. " ) ) ;
const bool bSourceIsUserWidget = DestinationName = = Class - > ClassGeneratedBy - > GetFName ( ) ;
if ( bSourceIsUserWidget )
{
return CreatePropertyPath ( Class , FName ( ) , PropertyPath . GetFields ( ) ) ;
}
else
{
2023-01-04 15:47:23 -05:00
const int32 DestinationVariableContextIndex = CompilerUserWidgetPropertyContexts . IndexOfByPredicate ( [ DestinationName ] ( const FCompilerUserWidgetPropertyContext & Other ) { return Other . PropertyName = = DestinationName ; } ) ;
2022-10-06 20:08:12 -04:00
if ( ensureAlwaysMsgf ( DestinationVariableContextIndex ! = INDEX_NONE , TEXT ( " Could not find source context for destination '%s' " ) , * DestinationName . ToString ( ) ) )
{
2023-01-04 15:47:23 -05:00
return CreatePropertyPath ( Class , CompilerUserWidgetPropertyContexts [ DestinationVariableContextIndex ] . PropertyName , PropertyPath . GetFields ( ) ) ;
2022-10-06 20:08:12 -04:00
}
2022-06-08 13:25:42 -04:00
}
}
else
{
ensureAlwaysMsgf ( false , TEXT ( " Not supported yet. " ) ) ;
return CreatePropertyPath ( Class , FName ( ) , PropertyPath . GetFields ( ) ) ;
}
2022-10-06 20:08:12 -04:00
return TArray < FMVVMConstFieldVariant > ( ) ;
2022-06-08 13:25:42 -04:00
}
2023-06-02 15:16:58 -04:00
TArray < FMVVMConstFieldVariant > FMVVMViewBlueprintCompiler : : CreatePropertyPath ( const UClass * Class , FName PropertyName , TArray < FMVVMConstFieldVariant > Properties ) const
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-06-02 15:16:58 -04:00
bool FMVVMViewBlueprintCompiler : : IsPropertyPathValid ( TArrayView < const FMVVMConstFieldVariant > PropertyPath ) const
2022-06-08 13:25:42 -04:00
{
for ( const FMVVMConstFieldVariant & Field : PropertyPath )
{
if ( Field . IsEmpty ( ) )
{
return false ;
}
if ( Field . IsProperty ( ) & & Field . GetProperty ( ) = = nullptr )
{
return false ;
}
if ( Field . IsFunction ( ) & & Field . GetFunction ( ) = = nullptr )
{
return false ;
}
}
return true ;
}
} //namespace
2022-10-06 20:08:12 -04:00
# undef LOCTEXT_NAMESPACE