446 lines
14 KiB
C++
446 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MVVMWidgetBlueprintExtension_View.h"
|
|
|
|
#include "Blueprint/WidgetTree.h"
|
|
#include "Extensions/MVVMBlueprintViewExtension.h"
|
|
#include "FindInBlueprintManager.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "MVVMBlueprintInstancedViewModel.h"
|
|
#include "MVVMBlueprintView.h"
|
|
#include "MVVMBlueprintViewModel.h"
|
|
#include "MVVMBlueprintViewConversionFunction.h"
|
|
#include "MVVMViewBlueprintCompiler.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "View/MVVMViewClass.h"
|
|
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(MVVMWidgetBlueprintExtension_View)
|
|
|
|
#define LOCTEXT_NAMESPACE "MVVMBlueprintExtensionView"
|
|
|
|
namespace UE::MVVM::Private
|
|
{
|
|
bool GAllowViewClass = true;
|
|
static FAutoConsoleVariableRef CVarAllowViewClass(
|
|
TEXT("MVVM.AllowViewClass"),
|
|
GAllowViewClass,
|
|
TEXT("Is the model view viewmodel view is allowed to be added to the generated Widget GeneratedClass."),
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
bool GAutogeneratedFunctionsAreTransient = false;
|
|
static FAutoConsoleVariableRef CVarAutogeneratedFunctionsAreTransient(
|
|
TEXT("MVVM.AutogeneratedFunctionsAreTransient"),
|
|
GAutogeneratedFunctionsAreTransient,
|
|
TEXT("Is the autogenerated function should be mark as transient."),
|
|
ECVF_ReadOnly
|
|
);
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::CreateBlueprintViewInstance()
|
|
{
|
|
BlueprintView = NewObject<UMVVMBlueprintView>(this, FName(), RF_Transactional);
|
|
BlueprintViewChangedDelegate.Broadcast();
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(GetWidgetBlueprint());
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::DestroyBlueprintViewInstance()
|
|
{
|
|
BlueprintView = nullptr;
|
|
BlueprintViewChangedDelegate.Broadcast();
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
if (!HasAnyFlags(RF_Transactional))
|
|
{
|
|
SetFlags(RF_Transactional);
|
|
}
|
|
}
|
|
|
|
UMVVMBlueprintViewExtension* UMVVMWidgetBlueprintExtension_View::CreateBlueprintWidgetExtension(TSubclassOf<UMVVMBlueprintViewExtension> ExtensionClass, FName WidgetName)
|
|
{
|
|
if (ensure(ExtensionClass.Get()))
|
|
{
|
|
UObject* ExtensionObj = NewObject<UObject>(this, ExtensionClass.Get(), NAME_None, RF_Transactional);
|
|
UMVVMBlueprintViewExtension* NewExtension = CastChecked<UMVVMBlueprintViewExtension>(ExtensionObj);
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("AddViewModelExtension", "Add viewmodel extension"));
|
|
NewExtension->Modify();
|
|
Modify();
|
|
FMVVMExtensionItem ExtensionToAdd;
|
|
ExtensionToAdd.WidgetName = WidgetName;
|
|
ExtensionToAdd.ExtensionObj = NewExtension;
|
|
BlueprintExtensions.Add(ExtensionToAdd);
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetWidgetBlueprint());
|
|
return NewExtension;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::RemoveBlueprintWidgetExtension(UMVVMBlueprintViewExtension* ExtensionToRemove, FName WidgetName)
|
|
{
|
|
FMVVMExtensionItem Extension;
|
|
Extension.WidgetName = WidgetName;
|
|
Extension.ExtensionObj = ExtensionToRemove;
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("RemoveViewModelExtension", "Remove viewmodel extension"));
|
|
Modify();
|
|
BlueprintExtensions.RemoveSingle(Extension);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetWidgetBlueprint());
|
|
}
|
|
|
|
TArray<UMVVMBlueprintViewExtension*> UMVVMWidgetBlueprintExtension_View::GetBlueprintExtensionsForWidget(FName WidgetName) const
|
|
{
|
|
TArray<UMVVMBlueprintViewExtension*> ThisWidgetExtensions;
|
|
for (const FMVVMExtensionItem& Extension : BlueprintExtensions)
|
|
{
|
|
if (Extension.WidgetName == WidgetName && Extension.ExtensionObj)
|
|
{
|
|
ThisWidgetExtensions.Add(Extension.ExtensionObj);
|
|
}
|
|
}
|
|
return ThisWidgetExtensions;
|
|
}
|
|
|
|
TArray<UMVVMBlueprintViewExtension*> UMVVMWidgetBlueprintExtension_View::GetAllBlueprintExtensions() const
|
|
{
|
|
TArray<UMVVMBlueprintViewExtension*> AllExtensions;
|
|
AllExtensions.Reset(BlueprintExtensions.Num());
|
|
|
|
for (const FMVVMExtensionItem& Extension : BlueprintExtensions)
|
|
{
|
|
if (Extension.ExtensionObj)
|
|
{
|
|
AllExtensions.Add(Extension.ExtensionObj);
|
|
}
|
|
}
|
|
|
|
AllExtensions.Shrink();
|
|
return AllExtensions;
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::VerifyWidgetExtensions()
|
|
{
|
|
if (const UWidgetBlueprint* WidgetBlueprint = GetWidgetBlueprint())
|
|
{
|
|
if (const UWidgetTree* WidgetTree = WidgetBlueprint->WidgetTree)
|
|
{
|
|
bool bModified = false;
|
|
auto UpdateModify = [&bModified, Self=this]()
|
|
{
|
|
if (!bModified)
|
|
{
|
|
bModified = true;
|
|
Self->Modify();
|
|
}
|
|
};
|
|
|
|
TArray <FName, TInlineAllocator<4>> WidgetNamesToRemove;
|
|
for (int32 Index = BlueprintExtensions.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
if (BlueprintExtensions[Index].ExtensionObj == nullptr)
|
|
{
|
|
UpdateModify();
|
|
BlueprintExtensions.RemoveAtSwap(Index);
|
|
}
|
|
else if (!BlueprintExtensions[Index].WidgetName.IsNone())
|
|
{
|
|
WidgetNamesToRemove.Add(BlueprintExtensions[Index].WidgetName);
|
|
}
|
|
}
|
|
|
|
// Find widgets that are no longer in the tree and delete their extensions.
|
|
if (WidgetNamesToRemove.Num() > 0)
|
|
{
|
|
WidgetTree->ForEachWidget([&WidgetNamesToRemove, this](TObjectPtr<UWidget> Widget) {
|
|
if (Widget)
|
|
{
|
|
for (int32 Index = WidgetNamesToRemove.Num() - 1; Index >= 0; Index--)
|
|
{
|
|
const FName WidgetName = WidgetNamesToRemove[Index];
|
|
if (WidgetName == Widget->GetFName())
|
|
{
|
|
WidgetNamesToRemove.RemoveSingleSwap(WidgetName);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (WidgetNamesToRemove.Num() > 0)
|
|
{
|
|
UpdateModify();
|
|
for (int32 Index = BlueprintExtensions.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
if (WidgetNamesToRemove.Contains(BlueprintExtensions[Index].WidgetName))
|
|
{
|
|
BlueprintExtensions.RemoveAtSwap(Index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::OnFieldRenamed(UClass* FieldOwnerClass, FName OldName, FName NewName)
|
|
{
|
|
const UWidgetBlueprint* WidgetBlueprint = GetWidgetBlueprint();
|
|
if (WidgetBlueprint != nullptr && WidgetBlueprint->GeneratedClass == FieldOwnerClass)
|
|
{
|
|
RenameWidgetExtensions(OldName, NewName);
|
|
}
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::RenameWidgetExtensions(FName OldWidgetName, FName NewWidgetName)
|
|
{
|
|
for (FMVVMExtensionItem& Extension : BlueprintExtensions)
|
|
{
|
|
if (Extension.WidgetName == OldWidgetName)
|
|
{
|
|
Modify();
|
|
Extension.WidgetName = NewWidgetName;
|
|
if (Extension.ExtensionObj)
|
|
{
|
|
Extension.ExtensionObj->WidgetRenamed(OldWidgetName, NewWidgetName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandlePreloadObjectsForCompilation(UBlueprint* OwningBlueprint)
|
|
{
|
|
if (IsInGameThread() && BlueprintView)
|
|
{
|
|
BlueprintView->ConditionalPostLoad();
|
|
|
|
for (const FMVVMBlueprintViewModelContext& AvailableViewModel : BlueprintView->GetViewModels())
|
|
{
|
|
if (AvailableViewModel.InstancedViewModel)
|
|
{
|
|
UBlueprint::ForceLoad(AvailableViewModel.InstancedViewModel);
|
|
AvailableViewModel.InstancedViewModel->GenerateClass(true);
|
|
}
|
|
if (AvailableViewModel.GetViewModelClass())
|
|
{
|
|
UBlueprint::ForceLoad(AvailableViewModel.GetViewModelClass());
|
|
}
|
|
}
|
|
for (FMVVMBlueprintViewBinding& Binding : BlueprintView->GetBindings())
|
|
{
|
|
if (Binding.Conversion.DestinationToSourceConversion)
|
|
{
|
|
UBlueprint::ForceLoad(Binding.Conversion.DestinationToSourceConversion);
|
|
}
|
|
if (Binding.Conversion.SourceToDestinationConversion)
|
|
{
|
|
UBlueprint::ForceLoad(Binding.Conversion.SourceToDestinationConversion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleBeginCompilation(FWidgetBlueprintCompilerContext& InCreationContext)
|
|
{
|
|
VerifyWidgetExtensions();
|
|
|
|
for (const FMVVMBlueprintViewModelContext& AvailableViewModel : BlueprintView->GetViewModels())
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
UWidgetBlueprint* WidgetBlueprint = GetWidgetBlueprint();
|
|
UClass* ViewModelClass = AvailableViewModel.GetViewModelClass();
|
|
if (WidgetBlueprint && ViewModelClass)
|
|
{
|
|
if (UBlueprint* ViewModelBP = Cast<UBlueprint>(ViewModelClass->ClassGeneratedBy))
|
|
{
|
|
ViewModelBP->CachedDependents.Add(WidgetBlueprint);
|
|
WidgetBlueprint->CachedDependencies.Add(ViewModelBP);
|
|
}
|
|
}
|
|
#endif // WITH_EDITORONLY_DATA
|
|
|
|
if (AvailableViewModel.InstancedViewModel)
|
|
{
|
|
AvailableViewModel.InstancedViewModel->GenerateClass(false);
|
|
}
|
|
}
|
|
|
|
CurrentCompilerContext.Reset();
|
|
GeneratedFunctions.Reset();
|
|
if (BlueprintView)
|
|
{
|
|
BlueprintView->ResetBindingMessages();
|
|
for (UMVVMBlueprintViewEvent* ViewEvent : BlueprintView->GetEvents())
|
|
{
|
|
ViewEvent->ResetCompilationMessages();
|
|
}
|
|
CurrentCompilerContext = MakePimpl<UE::MVVM::Private::FMVVMViewBlueprintCompiler>(InCreationContext, GetBlueprintView());
|
|
}
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleEndCompilation()
|
|
{
|
|
CurrentCompilerContext.Reset();
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleCleanAndSanitizeClass(UWidgetBlueprintGeneratedClass* ClassToClean, UObject* OldCDO)
|
|
{
|
|
Super::HandleCleanAndSanitizeClass(ClassToClean, OldCDO);
|
|
|
|
if (CurrentCompilerContext)
|
|
{
|
|
CurrentCompilerContext->CleanOldData(ClassToClean, OldCDO);
|
|
}
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandlePopulateGeneratedVariables(const FWidgetBlueprintCompilerContext::FPopulateGeneratedVariablesContext& Context)
|
|
{
|
|
Super::HandlePopulateGeneratedVariables(Context);
|
|
|
|
CurrentCompilerContext->GatherGeneratedVariables(Context);
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleCreateClassVariablesFromBlueprint(const FWidgetBlueprintCompilerContext::FCreateVariableContext& Context)
|
|
{
|
|
Super::HandleCreateClassVariablesFromBlueprint(Context);
|
|
|
|
CurrentCompilerContext->CreateVariables(Context);
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleCreateFunctionList(const FWidgetBlueprintCompilerContext::FCreateFunctionContext& Context)
|
|
{
|
|
Super::HandleCreateFunctionList(Context);
|
|
|
|
if (CurrentCompilerContext)
|
|
{
|
|
CurrentCompilerContext->CreateFunctions(Context);
|
|
}
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::HandleFinishCompilingClass(UWidgetBlueprintGeneratedClass* Class)
|
|
{
|
|
Super::HandleFinishCompilingClass(Class);
|
|
|
|
check(CurrentCompilerContext);
|
|
|
|
if (CurrentCompilerContext->GetCompilerContext().bIsFullCompile)
|
|
{
|
|
UMVVMViewClass* ViewExtension = nullptr;
|
|
bool bCompiled = false;
|
|
if (CurrentCompilerContext->PreCompile(Class))
|
|
{
|
|
FName ClassName = "ViewClass";
|
|
if (UObject* PreviousObj = StaticFindObjectFastInternal(nullptr, Class, ClassName, true))
|
|
{
|
|
// Remove previous object.
|
|
ERenameFlags RenameFlags = REN_NonTransactional | REN_DoNotDirty | REN_DontCreateRedirectors;
|
|
FName TrashName = MakeUniqueObjectName(GetTransientPackage(), PreviousObj->GetClass(), *FString::Printf(TEXT("TRASH_%s"), *PreviousObj->GetName()));
|
|
PreviousObj->Rename(*TrashName.ToString(), GetTransientPackage(), RenameFlags);
|
|
}
|
|
ViewExtension = NewObject<UMVVMViewClass>(Class);
|
|
bCompiled = CurrentCompilerContext->Compile(Class, ViewExtension);
|
|
}
|
|
|
|
if (bCompiled)
|
|
{
|
|
check(ViewExtension);
|
|
|
|
// Does it have any bindings
|
|
if (ViewExtension->GetBindings().Num() > 0 || ViewExtension->GetEvents().Num() > 0 || ViewExtension->GetConditions().Num() > 0 || (GetBlueprintView()->GetViewModels().Num() > 0 && ViewExtension->IsCreatedWithoutBindings()))
|
|
{
|
|
// Test if parent also has a view
|
|
if (Class->GetExtension<UMVVMViewClass>(true))
|
|
{
|
|
CurrentCompilerContext->GetCompilerContext().MessageLog.Warning(*LOCTEXT("MoreThanOneViewWarning", "There is more than one view. This can happen when inheriting from a widget blueprint that also contains a view. Please be aware that the parent view model is not available to the child widget.").ToString());
|
|
}
|
|
|
|
// If we are not allowed to add the view class
|
|
if (UE::MVVM::Private::GAllowViewClass)
|
|
{
|
|
CurrentCompilerContext->AddExtension(Class, ViewExtension);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GeneratedFunctions = CurrentCompilerContext->GetGeneratedFunctions();
|
|
|
|
// If we can't auto-generate function add the transient flags
|
|
if (UE::MVVM::Private::GAutogeneratedFunctionsAreTransient)
|
|
{
|
|
// If we are not allowed to add the view class, add the transient flags on added conversion graphs and event graphs.
|
|
for (TFieldIterator<UFunction> FunctionIter(Class, EFieldIteratorFlags::ExcludeSuper); FunctionIter; ++FunctionIter)
|
|
{
|
|
UFunction* Function = *FunctionIter;
|
|
Function->SetFlags(RF_Transient);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UMVVMWidgetBlueprintExtension_View::FSearchData UMVVMWidgetBlueprintExtension_View::HandleGatherSearchData(const UBlueprint* OwningBlueprint) const
|
|
{
|
|
UMVVMWidgetBlueprintExtension_View::FSearchData SearchData;
|
|
if (GetBlueprintView())
|
|
{
|
|
{
|
|
TUniquePtr<FSearchArrayData> ViewModelContextSearchData = MakeUnique<FSearchArrayData>();
|
|
ViewModelContextSearchData->Identifier = LOCTEXT("ViewmodelSearchTag", "Viewmodels");
|
|
for (const FMVVMBlueprintViewModelContext& ViewModelContext : GetBlueprintView()->GetViewModels())
|
|
{
|
|
FSearchData& ViewModelSearchData = ViewModelContextSearchData->SearchSubList.AddDefaulted_GetRef();
|
|
ViewModelSearchData.Datas.Emplace(LOCTEXT("ViewmodelGuidSearchTag", "Guid"), FText::FromString(ViewModelContext.GetViewModelId().ToString(EGuidFormats::Digits)));
|
|
ViewModelSearchData.Datas.Emplace(FFindInBlueprintSearchTags::FiB_Name, ViewModelContext.GetDisplayName());
|
|
ViewModelSearchData.Datas.Emplace(FFindInBlueprintSearchTags::FiB_ClassName, ViewModelContext.GetViewModelClass() ? ViewModelContext.GetViewModelClass()->GetDisplayNameText() : FText::GetEmpty());
|
|
ViewModelSearchData.Datas.Emplace(LOCTEXT("ViewmodelCreationTypeSearchTag", "CreationType"), StaticEnum<EMVVMBlueprintViewModelContextCreationType>()->GetDisplayNameTextByValue((int64)ViewModelContext.CreationType));
|
|
}
|
|
SearchData.SearchArrayDatas.Add(MoveTemp(ViewModelContextSearchData));
|
|
}
|
|
{
|
|
TUniquePtr<FSearchArrayData> BindingContextSearchData = MakeUnique<FSearchArrayData>();
|
|
BindingContextSearchData->Identifier = LOCTEXT("BindingSearchTag", "Bindings");
|
|
for (const FMVVMBlueprintViewBinding& Binding : GetBlueprintView()->GetBindings())
|
|
{
|
|
FSearchData& BindingSearchData = BindingContextSearchData->SearchSubList.AddDefaulted_GetRef();
|
|
BindingSearchData.Datas.Emplace(LOCTEXT("ViewBindingSearchTag", "Binding"), FText::FromString(Binding.GetDisplayNameString(GetWidgetBlueprint())));
|
|
}
|
|
SearchData.SearchArrayDatas.Add(MoveTemp(BindingContextSearchData));
|
|
}
|
|
}
|
|
return SearchData;
|
|
}
|
|
|
|
|
|
void UMVVMWidgetBlueprintExtension_View::SetFilterSettings(FMVVMViewBindingFilterSettings InFilterSettings)
|
|
{
|
|
FilterSettings = InFilterSettings;
|
|
}
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
void UMVVMWidgetBlueprintExtension_View::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
|
|
if (!IsTemplate())
|
|
{
|
|
SetFilterSettings(GetDefault<UMVVMDeveloperProjectSettings>()->FilterSettings);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#undef LOCTEXT_NAMESPACE
|