RealtimeStyleTransferRuntime/Plugins/UIExtension/Source/Private/UIExtensionSystem.cpp

378 lines
13 KiB
C++
Raw Permalink Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "UIExtensionSystem.h"
2022-09-13 07:18:28 +00:00
2022-05-23 18:41:30 +00:00
#include "Blueprint/UserWidget.h"
2022-09-13 07:18:28 +00:00
#include "Containers/UnrealString.h"
#include "HAL/Platform.h"
#include "LogUIExtension.h"
#include "Logging/LogCategory.h"
#include "Logging/LogMacros.h"
#include "Logging/LogVerbosity.h"
#include "Misc/AssertionMacros.h"
#include "Templates/Casts.h"
#include "Templates/UnrealTemplate.h"
#include "Trace/Detail/Channel.h"
#include "UObject/Stack.h"
#include "UObject/UObjectBaseUtility.h"
class FSubsystemCollectionBase;
2022-05-23 18:41:30 +00:00
//=========================================================
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();
}