RealtimeStyleTransferRuntime/Plugins/CommonGame/Source/Public/PrimaryGameLayout.h

133 lines
5.7 KiB
C
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CommonUserWidget.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include "GameplayTagContainer.h"
#include "Engine/AssetManager.h"
#include "CommonUIExtensions.h"
#include "PrimaryGameLayout.generated.h"
class UCommonActivatableWidget;
class UCommonActivatableWidgetStack;
class UCommonActivatableWidgetContainerBase;
/**
* The state of an async load operation for the UI.
*/
enum class EAsyncWidgetLayerState : uint8
{
Canceled,
Initialize,
AfterPush
};
/**
* The primary game UI layout of your game. This widget class represents how to layout, push and display all layers
* of the UI for a single player. Each player in a split-screen game will receive their own primary game layout.
*/
UCLASS(Abstract, meta = (DisableNativeTick))
class COMMONGAME_API UPrimaryGameLayout : public UCommonUserWidget
{
GENERATED_BODY()
public:
static UPrimaryGameLayout* GetPrimaryGameLayoutForPrimaryPlayer(const UObject* WorldContextObject);
static UPrimaryGameLayout* GetPrimaryGameLayout(APlayerController* PlayerController);
static UPrimaryGameLayout* GetPrimaryGameLayout(ULocalPlayer* LocalPlayer);
public:
UPrimaryGameLayout(const FObjectInitializer& ObjectInitializer);
/** A dormant root layout is collapsed and responds only to persistent actions registered by the owning player */
void SetIsDormant(bool Dormant);
bool IsDormant() const { return bIsDormant; }
public:
template <typename ActivatableWidgetT = UCommonActivatableWidget>
TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync(FGameplayTag LayerName, bool bSuspendInputUntilComplete, TSoftClassPtr<UCommonActivatableWidget> ActivatableWidgetClass)
{
return PushWidgetToLayerStackAsync<ActivatableWidgetT>(LayerName, bSuspendInputUntilComplete, ActivatableWidgetClass, [](EAsyncWidgetLayerState, ActivatableWidgetT*) {});
}
template <typename ActivatableWidgetT = UCommonActivatableWidget>
TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync(FGameplayTag LayerName, bool bSuspendInputUntilComplete, TSoftClassPtr<UCommonActivatableWidget> ActivatableWidgetClass, TFunction<void(EAsyncWidgetLayerState, ActivatableWidgetT*)> StateFunc)
{
static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");
static FName NAME_PushingWidgetToLayer("PushingWidgetToLayer");
const FName SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(GetOwningPlayer(), NAME_PushingWidgetToLayer) : NAME_None;
FStreamableManager& StreamableManager = UAssetManager::Get().GetStreamableManager();
TSharedPtr<FStreamableHandle> StreamingHandle = StreamableManager.RequestAsyncLoad(ActivatableWidgetClass.ToSoftObjectPath(), FStreamableDelegate::CreateWeakLambda(this,
[this, LayerName, ActivatableWidgetClass, StateFunc, SuspendInputToken]()
{
UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
ActivatableWidgetT* Widget = PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass.Get(), [StateFunc](ActivatableWidgetT& WidgetToInit) {
StateFunc(EAsyncWidgetLayerState::Initialize, &WidgetToInit);
});
StateFunc(EAsyncWidgetLayerState::AfterPush, Widget);
})
);
// Setup a cancel delegate so that we can resume input if this handler is canceled.
StreamingHandle->BindCancelDelegate(FStreamableDelegate::CreateWeakLambda(this,
[this, StateFunc, SuspendInputToken]()
{
UCommonUIExtensions::ResumeInputForPlayer(GetOwningPlayer(), SuspendInputToken);
StateFunc(EAsyncWidgetLayerState::Canceled, nullptr);
})
);
return StreamingHandle;
}
template <typename ActivatableWidgetT = UCommonActivatableWidget>
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass)
{
return PushWidgetToLayerStack<ActivatableWidgetT>(LayerName, ActivatableWidgetClass, [](ActivatableWidgetT&) {});
}
template <typename ActivatableWidgetT = UCommonActivatableWidget>
ActivatableWidgetT* PushWidgetToLayerStack(FGameplayTag LayerName, UClass* ActivatableWidgetClass, TFunctionRef<void(ActivatableWidgetT&)> InitInstanceFunc)
{
static_assert(TIsDerivedFrom<ActivatableWidgetT, UCommonActivatableWidget>::IsDerived, "Only CommonActivatableWidgets can be used here");
if (UCommonActivatableWidgetContainerBase* Layer = GetLayerWidget(LayerName))
{
return Layer->AddWidget<ActivatableWidgetT>(ActivatableWidgetClass, InitInstanceFunc);
}
return nullptr;
}
// Find the widget if it exists on any of the layers and remove it from the layer.
void FindAndRemoveWidgetFromLayer(UCommonActivatableWidget* ActivatableWidget);
// Get the layer widget for the given layer tag.
UCommonActivatableWidgetContainerBase* GetLayerWidget(FGameplayTag LayerName);
protected:
/** Register a layer that widgets can be pushed onto. */
UFUNCTION(BlueprintCallable, Category="Layer")
void RegisterLayer(UPARAM(meta = (Categories = "UI.Layer")) FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget);
virtual void OnIsDormantChanged();
void OnWidgetStackTransitioning(UCommonActivatableWidgetContainerBase* Widget, bool bIsTransitioning);
private:
bool bIsDormant = false;
// Lets us keep track of all suspended input tokens so that multiple async UIs can be loading and we correctly suspend
// for the duration of all of them.
TArray<FName> SuspendInputTokens;
// The registered layers for the primary layout.
UPROPERTY(Transient, meta = (Categories = "UI.Layer"))
TMap<FGameplayTag, UCommonActivatableWidgetContainerBase*> Layers;
};