368 lines
13 KiB
C++
368 lines
13 KiB
C++
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
||
|
#include "UIExtensionSystem.h"
|
||
|
#include "LogUIExtension.h"
|
||
|
#include "AssetRegistry/AssetData.h"
|
||
|
#include "Engine/AssetManager.h"
|
||
|
#include "Blueprint/UserWidget.h"
|
||
|
#include "Engine/GameInstance.h"
|
||
|
#include "GameFramework/PlayerState.h"
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
void FUIExtensionPointHandle::Unregister()
|
||
|
{
|
||
|
if (UUIExtensionSubsystem* ExtensionSourcePtr = ExtensionSource.Get())
|
||
|
{
|
||
|
ExtensionSourcePtr->UnregisterExtensionPoint(*this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
void FUIExtensionHandle::Unregister()
|
||
|
{
|
||
|
if (UUIExtensionSubsystem* ExtensionSourcePtr = ExtensionSource.Get())
|
||
|
{
|
||
|
ExtensionSourcePtr->UnregisterExtension(*this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
bool FUIExtensionPoint::DoesExtensionPassContract(const FUIExtension* Extension) const
|
||
|
{
|
||
|
if (UObject* DataPtr = Extension->Data)
|
||
|
{
|
||
|
const bool bMatchesContext =
|
||
|
(ContextObject.IsExplicitlyNull() && Extension->ContextObject.IsExplicitlyNull()) ||
|
||
|
ContextObject == Extension->ContextObject;
|
||
|
|
||
|
// Make sure the contexts match.
|
||
|
if (bMatchesContext)
|
||
|
{
|
||
|
// The data can either be the literal class of the data type, or a instance of the class type.
|
||
|
const UClass* DataClass = DataPtr->IsA(UClass::StaticClass()) ? Cast<UClass>(DataPtr) : DataPtr->GetClass();
|
||
|
for (const UClass* AllowedDataClass : AllowedDataClasses)
|
||
|
{
|
||
|
if (DataClass->IsChildOf(AllowedDataClass) || DataClass->ImplementsInterface(AllowedDataClass))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
void UUIExtensionSubsystem::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
|
||
|
{
|
||
|
if (UUIExtensionSubsystem* ExtensionSubsystem = Cast<UUIExtensionSubsystem>(InThis))
|
||
|
{
|
||
|
for (auto MapIt = ExtensionSubsystem->ExtensionPointMap.CreateIterator(); MapIt; ++MapIt)
|
||
|
{
|
||
|
for (const TSharedPtr<FUIExtensionPoint>& ValueElement : MapIt.Value())
|
||
|
{
|
||
|
Collector.AddReferencedObjects(ValueElement->AllowedDataClasses);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (auto MapIt = ExtensionSubsystem->ExtensionMap.CreateIterator(); MapIt; ++MapIt)
|
||
|
{
|
||
|
for (const TSharedPtr<FUIExtension>& ValueElement : MapIt.Value())
|
||
|
{
|
||
|
Collector.AddReferencedObject(ValueElement->Data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||
|
{
|
||
|
Super::Initialize(Collection);
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::Deinitialize()
|
||
|
{
|
||
|
Super::Deinitialize();
|
||
|
}
|
||
|
|
||
|
FUIExtensionPointHandle UUIExtensionSubsystem::RegisterExtensionPoint(const FGameplayTag& ExtensionPointTag, EUIExtensionPointMatch ExtensionPointTagMatchType, const TArray<UClass*>& AllowedDataClasses, FExtendExtensionPointDelegate ExtensionCallback)
|
||
|
{
|
||
|
return RegisterExtensionPointForContext(ExtensionPointTag, nullptr, ExtensionPointTagMatchType, AllowedDataClasses, ExtensionCallback);
|
||
|
}
|
||
|
|
||
|
FUIExtensionPointHandle UUIExtensionSubsystem::RegisterExtensionPointForContext(const FGameplayTag& ExtensionPointTag, UObject* ContextObject, EUIExtensionPointMatch ExtensionPointTagMatchType, const TArray<UClass*>& AllowedDataClasses, FExtendExtensionPointDelegate ExtensionCallback)
|
||
|
{
|
||
|
if (!ExtensionPointTag.IsValid())
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to register an invalid extension point."));
|
||
|
return FUIExtensionPointHandle();
|
||
|
}
|
||
|
|
||
|
if (!ExtensionCallback.IsBound())
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to register an invalid extension point."));
|
||
|
return FUIExtensionPointHandle();
|
||
|
}
|
||
|
|
||
|
if (AllowedDataClasses.Num() == 0)
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to register an invalid extension point."));
|
||
|
return FUIExtensionPointHandle();
|
||
|
}
|
||
|
|
||
|
FExtensionPointList& List = ExtensionPointMap.FindOrAdd(ExtensionPointTag);
|
||
|
|
||
|
TSharedPtr<FUIExtensionPoint>& Entry = List.Add_GetRef(MakeShared<FUIExtensionPoint>());
|
||
|
Entry->ExtensionPointTag = ExtensionPointTag;
|
||
|
Entry->ContextObject = ContextObject;
|
||
|
Entry->ExtensionPointTagMatchType = ExtensionPointTagMatchType;
|
||
|
Entry->AllowedDataClasses = AllowedDataClasses;
|
||
|
Entry->Callback = MoveTemp(ExtensionCallback);
|
||
|
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension Point [%s] Registered"), *ExtensionPointTag.ToString());
|
||
|
|
||
|
NotifyExtensionPointOfExtensions(Entry);
|
||
|
|
||
|
return FUIExtensionPointHandle(this, Entry);
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::RegisterExtensionAsWidget(const FGameplayTag& ExtensionPointTag, TSubclassOf<UUserWidget> WidgetClass, int32 Priority)
|
||
|
{
|
||
|
return RegisterExtensionAsData(ExtensionPointTag, nullptr, WidgetClass, Priority);
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::RegisterExtensionAsWidgetForContext(const FGameplayTag& ExtensionPointTag, UObject* ContextObject, TSubclassOf<UUserWidget> WidgetClass, int32 Priority)
|
||
|
{
|
||
|
return RegisterExtensionAsData(ExtensionPointTag, ContextObject, WidgetClass, Priority);
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::RegisterExtensionAsData(const FGameplayTag& ExtensionPointTag, UObject* ContextObject, UObject* Data, int32 Priority)
|
||
|
{
|
||
|
if (!ExtensionPointTag.IsValid())
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to register an invalid extension."));
|
||
|
return FUIExtensionHandle();
|
||
|
}
|
||
|
|
||
|
if (!Data)
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to register an invalid extension."));
|
||
|
return FUIExtensionHandle();
|
||
|
}
|
||
|
|
||
|
FExtensionList& List = ExtensionMap.FindOrAdd(ExtensionPointTag);
|
||
|
|
||
|
TSharedPtr<FUIExtension>& Entry = List.Add_GetRef(MakeShared<FUIExtension>());
|
||
|
Entry->ExtensionPointTag = ExtensionPointTag;
|
||
|
Entry->ContextObject = ContextObject;
|
||
|
Entry->Data = Data;
|
||
|
Entry->Priority = Priority;
|
||
|
|
||
|
if (ContextObject)
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension [%s] @ [%s] Registered"), *GetNameSafe(Data), *ExtensionPointTag.ToString());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension [%s] for [%s] @ [%s] Registered"), *GetNameSafe(Data), *GetNameSafe(ContextObject), *ExtensionPointTag.ToString());
|
||
|
}
|
||
|
|
||
|
NotifyExtensionPointsOfExtension(EUIExtensionAction::Added, Entry);
|
||
|
|
||
|
return FUIExtensionHandle(this, Entry);
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::NotifyExtensionPointOfExtensions(TSharedPtr<FUIExtensionPoint>& ExtensionPoint)
|
||
|
{
|
||
|
for (FGameplayTag Tag = ExtensionPoint->ExtensionPointTag; Tag.IsValid(); Tag = Tag.RequestDirectParent())
|
||
|
{
|
||
|
if (const FExtensionList* ListPtr = ExtensionMap.Find(Tag))
|
||
|
{
|
||
|
// Copy in case there are removals while handling callbacks
|
||
|
FExtensionList ExtensionArray(*ListPtr);
|
||
|
|
||
|
for (const TSharedPtr<FUIExtension>& Extension : ExtensionArray)
|
||
|
{
|
||
|
if (ExtensionPoint->DoesExtensionPassContract(Extension.Get()))
|
||
|
{
|
||
|
FUIExtensionRequest Request = CreateExtensionRequest(Extension);
|
||
|
ExtensionPoint->Callback.ExecuteIfBound(EUIExtensionAction::Added, Request);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ExtensionPoint->ExtensionPointTagMatchType == EUIExtensionPointMatch::ExactMatch)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::NotifyExtensionPointsOfExtension(EUIExtensionAction Action, TSharedPtr<FUIExtension>& Extension)
|
||
|
{
|
||
|
bool bOnInitialTag = true;
|
||
|
for (FGameplayTag Tag = Extension->ExtensionPointTag; Tag.IsValid(); Tag = Tag.RequestDirectParent())
|
||
|
{
|
||
|
if (const FExtensionPointList* ListPtr = ExtensionPointMap.Find(Tag))
|
||
|
{
|
||
|
// Copy in case there are removals while handling callbacks
|
||
|
FExtensionPointList ExtensionPointArray(*ListPtr);
|
||
|
|
||
|
for (const TSharedPtr<FUIExtensionPoint>& ExtensionPoint : ExtensionPointArray)
|
||
|
{
|
||
|
if (bOnInitialTag || (ExtensionPoint->ExtensionPointTagMatchType == EUIExtensionPointMatch::PartialMatch))
|
||
|
{
|
||
|
if (ExtensionPoint->DoesExtensionPassContract(Extension.Get()))
|
||
|
{
|
||
|
FUIExtensionRequest Request = CreateExtensionRequest(Extension);
|
||
|
ExtensionPoint->Callback.ExecuteIfBound(Action, Request);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bOnInitialTag = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::UnregisterExtension(const FUIExtensionHandle& ExtensionHandle)
|
||
|
{
|
||
|
if (ExtensionHandle.IsValid())
|
||
|
{
|
||
|
checkf(ExtensionHandle.ExtensionSource == this, TEXT("Trying to unregister an extension that's not from this extension subsystem."));
|
||
|
|
||
|
TSharedPtr<FUIExtension> Extension = ExtensionHandle.DataPtr;
|
||
|
if (FExtensionList* ListPtr = ExtensionMap.Find(Extension->ExtensionPointTag))
|
||
|
{
|
||
|
if (Extension->ContextObject.IsExplicitlyNull())
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension [%s] @ [%s] Unregistered"), *GetNameSafe(Extension->Data), *Extension->ExtensionPointTag.ToString());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension [%s] for [%s] @ [%s] Unregistered"), *GetNameSafe(Extension->Data), *GetNameSafe(Extension->ContextObject.Get()), *Extension->ExtensionPointTag.ToString());
|
||
|
}
|
||
|
|
||
|
NotifyExtensionPointsOfExtension(EUIExtensionAction::Removed, Extension);
|
||
|
|
||
|
ListPtr->RemoveSwap(Extension);
|
||
|
|
||
|
if (ListPtr->Num() == 0)
|
||
|
{
|
||
|
ExtensionMap.Remove(Extension->ExtensionPointTag);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to unregister an invalid Handle."));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UUIExtensionSubsystem::UnregisterExtensionPoint(const FUIExtensionPointHandle& ExtensionPointHandle)
|
||
|
{
|
||
|
if (ExtensionPointHandle.IsValid())
|
||
|
{
|
||
|
check(ExtensionPointHandle.ExtensionSource == this);
|
||
|
|
||
|
const TSharedPtr<FUIExtensionPoint> ExtensionPoint = ExtensionPointHandle.DataPtr;
|
||
|
if (FExtensionPointList* ListPtr = ExtensionPointMap.Find(ExtensionPoint->ExtensionPointTag))
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Verbose, TEXT("Extension Point [%s] Unregistered"), *ExtensionPoint->ExtensionPointTag.ToString());
|
||
|
|
||
|
ListPtr->RemoveSwap(ExtensionPoint);
|
||
|
if (ListPtr->Num() == 0)
|
||
|
{
|
||
|
ExtensionPointMap.Remove(ExtensionPoint->ExtensionPointTag);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UE_LOG(LogUIExtension, Warning, TEXT("Trying to unregister an invalid Handle."));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FUIExtensionRequest UUIExtensionSubsystem::CreateExtensionRequest(const TSharedPtr<FUIExtension>& Extension)
|
||
|
{
|
||
|
FUIExtensionRequest Request;
|
||
|
Request.ExtensionHandle = FUIExtensionHandle(this, Extension);
|
||
|
Request.ExtensionPointTag = Extension->ExtensionPointTag;
|
||
|
Request.Priority = Extension->Priority;
|
||
|
Request.Data = Extension->Data;
|
||
|
Request.ContextObject = Extension->ContextObject.Get();
|
||
|
|
||
|
return Request;
|
||
|
}
|
||
|
|
||
|
FUIExtensionPointHandle UUIExtensionSubsystem::K2_RegisterExtensionPoint(FGameplayTag ExtensionPointTag, EUIExtensionPointMatch ExtensionPointTagMatchType, const TArray<UClass*>& AllowedDataClasses, FExtendExtensionPointDynamicDelegate ExtensionCallback)
|
||
|
{
|
||
|
return RegisterExtensionPoint(ExtensionPointTag, ExtensionPointTagMatchType, AllowedDataClasses, FExtendExtensionPointDelegate::CreateWeakLambda(ExtensionCallback.GetUObject(), [this, ExtensionCallback](EUIExtensionAction Action, const FUIExtensionRequest& Request) {
|
||
|
ExtensionCallback.ExecuteIfBound(Action, Request);
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::K2_RegisterExtensionAsWidget(FGameplayTag ExtensionPointTag, TSubclassOf<UUserWidget> WidgetClass, int32 Priority)
|
||
|
{
|
||
|
return RegisterExtensionAsWidget(ExtensionPointTag, WidgetClass, Priority);
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::K2_RegisterExtensionAsWidgetForContext(FGameplayTag ExtensionPointTag, TSubclassOf<UUserWidget> WidgetClass, UObject* ContextObject, int32 Priority)
|
||
|
{
|
||
|
if (ContextObject)
|
||
|
{
|
||
|
return RegisterExtensionAsWidgetForContext(ExtensionPointTag, ContextObject, WidgetClass, Priority);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FFrame::KismetExecutionMessage(TEXT("A null ContextObject was passed to Register Extension (Widget For Context)"), ELogVerbosity::Error);
|
||
|
return FUIExtensionHandle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::K2_RegisterExtensionAsData(FGameplayTag ExtensionPointTag, UObject* Data, int32 Priority)
|
||
|
{
|
||
|
return RegisterExtensionAsData(ExtensionPointTag, nullptr, Data, Priority);
|
||
|
}
|
||
|
|
||
|
FUIExtensionHandle UUIExtensionSubsystem::K2_RegisterExtensionAsDataForContext(FGameplayTag ExtensionPointTag, UObject* ContextObject, UObject* Data, int32 Priority)
|
||
|
{
|
||
|
if (ContextObject)
|
||
|
{
|
||
|
return RegisterExtensionAsData(ExtensionPointTag, ContextObject, Data, Priority);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FFrame::KismetExecutionMessage(TEXT("A null ContextObject was passed to Register Extension (Data For Context)"), ELogVerbosity::Error);
|
||
|
return FUIExtensionHandle();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
void UUIExtensionHandleFunctions::Unregister(FUIExtensionHandle& Handle)
|
||
|
{
|
||
|
Handle.Unregister();
|
||
|
}
|
||
|
|
||
|
bool UUIExtensionHandleFunctions::IsValid(FUIExtensionHandle& Handle)
|
||
|
{
|
||
|
return Handle.IsValid();
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
void UUIExtensionPointHandleFunctions::Unregister(FUIExtensionPointHandle& Handle)
|
||
|
{
|
||
|
Handle.Unregister();
|
||
|
}
|
||
|
|
||
|
bool UUIExtensionPointHandleFunctions::IsValid(FUIExtensionPointHandle& Handle)
|
||
|
{
|
||
|
return Handle.IsValid();
|
||
|
}
|