搬了lyra的hudlayout

This commit is contained in:
2023-08-28 03:38:23 +08:00
parent b9666b7fe7
commit c90b2d2956
343 changed files with 10665 additions and 2 deletions

View File

@@ -0,0 +1,97 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Actions/AsyncAction_CreateWidgetAsync.h"
#include "Blueprint/UserWidget.h"
#include "Blueprint/WidgetBlueprintLibrary.h"
#include "CommonUIExtensions.h"
#include "Engine/AssetManager.h"
#include "Engine/Engine.h"
#include "Engine/GameInstance.h"
#include "Engine/StreamableManager.h"
#include "UObject/Stack.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_CreateWidgetAsync)
class UUserWidget;
static const FName InputFilterReason_Template = FName(TEXT("CreatingWidgetAsync"));
UAsyncAction_CreateWidgetAsync::UAsyncAction_CreateWidgetAsync(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bSuspendInputUntilComplete(true)
{
}
UAsyncAction_CreateWidgetAsync* UAsyncAction_CreateWidgetAsync::CreateWidgetAsync(UObject* InWorldContextObject, TSoftClassPtr<UUserWidget> InUserWidgetSoftClass, APlayerController* InOwningPlayer, bool bSuspendInputUntilComplete)
{
if (InUserWidgetSoftClass.IsNull())
{
FFrame::KismetExecutionMessage(TEXT("CreateWidgetAsync was passed a null UserWidgetSoftClass"), ELogVerbosity::Error);
return nullptr;
}
UWorld* World = GEngine->GetWorldFromContextObject(InWorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UAsyncAction_CreateWidgetAsync* Action = NewObject<UAsyncAction_CreateWidgetAsync>();
Action->UserWidgetSoftClass = InUserWidgetSoftClass;
Action->OwningPlayer = InOwningPlayer;
Action->World = World;
Action->GameInstance = World->GetGameInstance();
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
Action->RegisterWithGameInstance(World);
return Action;
}
void UAsyncAction_CreateWidgetAsync::Activate()
{
SuspendInputToken = bSuspendInputUntilComplete ? UCommonUIExtensions::SuspendInputForPlayer(OwningPlayer.Get(), InputFilterReason_Template) : NAME_None;
TWeakObjectPtr<UAsyncAction_CreateWidgetAsync> LocalWeakThis(this);
StreamingHandle = UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(
UserWidgetSoftClass.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &ThisClass::OnWidgetLoaded),
FStreamableManager::AsyncLoadHighPriority
);
// Setup a cancel delegate so that we can resume input if this handler is canceled.
StreamingHandle->BindCancelDelegate(FStreamableDelegate::CreateWeakLambda(this,
[this]()
{
UCommonUIExtensions::ResumeInputForPlayer(OwningPlayer.Get(), SuspendInputToken);
})
);
}
void UAsyncAction_CreateWidgetAsync::Cancel()
{
Super::Cancel();
if (StreamingHandle.IsValid())
{
StreamingHandle->CancelHandle();
StreamingHandle.Reset();
}
}
void UAsyncAction_CreateWidgetAsync::OnWidgetLoaded()
{
if (bSuspendInputUntilComplete)
{
UCommonUIExtensions::ResumeInputForPlayer(OwningPlayer.Get(), SuspendInputToken);
}
// If the load as successful, create it, otherwise don't complete this.
TSubclassOf<UUserWidget> UserWidgetClass = UserWidgetSoftClass.Get();
if (UserWidgetClass)
{
UUserWidget* UserWidget = UWidgetBlueprintLibrary::Create(World.Get(), UserWidgetClass, OwningPlayer.Get());
OnComplete.Broadcast(UserWidget);
}
StreamingHandle.Reset();
SetReadyToDestroy();
}

View File

@@ -0,0 +1,81 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Actions/AsyncAction_PushContentToLayerForPlayer.h"
#include "Engine/Engine.h"
#include "PrimaryGameLayout.h"
#include "UObject/Stack.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_PushContentToLayerForPlayer)
UAsyncAction_PushContentToLayerForPlayer::UAsyncAction_PushContentToLayerForPlayer(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UAsyncAction_PushContentToLayerForPlayer* UAsyncAction_PushContentToLayerForPlayer::PushContentToLayerForPlayer(APlayerController* InOwningPlayer, TSoftClassPtr<UCommonActivatableWidget> InWidgetClass, FGameplayTag InLayerName, bool bSuspendInputUntilComplete)
{
if (InWidgetClass.IsNull())
{
FFrame::KismetExecutionMessage(TEXT("PushContentToLayerForPlayer was passed a null WidgetClass"), ELogVerbosity::Error);
return nullptr;
}
if (UWorld* World = GEngine->GetWorldFromContextObject(InOwningPlayer, EGetWorldErrorMode::LogAndReturnNull))
{
UAsyncAction_PushContentToLayerForPlayer* Action = NewObject<UAsyncAction_PushContentToLayerForPlayer>();
Action->WidgetClass = InWidgetClass;
Action->OwningPlayerPtr = InOwningPlayer;
Action->LayerName = InLayerName;
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
Action->RegisterWithGameInstance(World);
return Action;
}
return nullptr;
}
void UAsyncAction_PushContentToLayerForPlayer::Cancel()
{
Super::Cancel();
if (StreamingHandle.IsValid())
{
StreamingHandle->CancelHandle();
StreamingHandle.Reset();
}
}
void UAsyncAction_PushContentToLayerForPlayer::Activate()
{
if (UPrimaryGameLayout* RootLayout = UPrimaryGameLayout::GetPrimaryGameLayout(OwningPlayerPtr.Get()))
{
TWeakObjectPtr<UAsyncAction_PushContentToLayerForPlayer> WeakThis = this;
StreamingHandle = RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(LayerName, bSuspendInputUntilComplete, WidgetClass, [this, WeakThis](EAsyncWidgetLayerState State, UCommonActivatableWidget* Widget) {
if (WeakThis.IsValid())
{
switch (State)
{
case EAsyncWidgetLayerState::Initialize:
BeforePush.Broadcast(Widget);
break;
case EAsyncWidgetLayerState::AfterPush:
AfterPush.Broadcast(Widget);
SetReadyToDestroy();
break;
case EAsyncWidgetLayerState::Canceled:
SetReadyToDestroy();
break;
}
}
SetReadyToDestroy();
});
}
else
{
SetReadyToDestroy();
}
}

View File

@@ -0,0 +1,88 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Actions/AsyncAction_ShowConfirmation.h"
#include "Engine/GameInstance.h"
#include "Messaging/CommonGameDialog.h"
#include "Messaging/CommonMessagingSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_ShowConfirmation)
UAsyncAction_ShowConfirmation::UAsyncAction_ShowConfirmation(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationYesNo(UObject* InWorldContextObject, FText Title, FText Message)
{
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
Action->WorldContextObject = InWorldContextObject;
Action->Descriptor = UCommonGameDialogDescriptor::CreateConfirmationYesNo(Title, Message);
Action->RegisterWithGameInstance(InWorldContextObject);
return Action;
}
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationOkCancel(UObject* InWorldContextObject, FText Title, FText Message)
{
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
Action->WorldContextObject = InWorldContextObject;
Action->Descriptor = UCommonGameDialogDescriptor::CreateConfirmationOkCancel(Title, Message);
Action->RegisterWithGameInstance(InWorldContextObject);
return Action;
}
UAsyncAction_ShowConfirmation* UAsyncAction_ShowConfirmation::ShowConfirmationCustom(UObject* InWorldContextObject, UCommonGameDialogDescriptor* Descriptor)
{
UAsyncAction_ShowConfirmation* Action = NewObject<UAsyncAction_ShowConfirmation>();
Action->WorldContextObject = InWorldContextObject;
Action->Descriptor = Descriptor;
Action->RegisterWithGameInstance(InWorldContextObject);
return Action;
}
void UAsyncAction_ShowConfirmation::Activate()
{
if (WorldContextObject && !TargetLocalPlayer)
{
if (UUserWidget* UserWidget = Cast<UUserWidget>(WorldContextObject))
{
TargetLocalPlayer = UserWidget->GetOwningLocalPlayer<ULocalPlayer>();
}
else if (APlayerController* PC = Cast<APlayerController>(WorldContextObject))
{
TargetLocalPlayer = PC->GetLocalPlayer();
}
else if (UWorld* World = WorldContextObject->GetWorld())
{
if (UGameInstance* GameInstance = World->GetGameInstance<UGameInstance>())
{
TargetLocalPlayer = GameInstance->GetPrimaryPlayerController(false)->GetLocalPlayer();
}
}
}
if (TargetLocalPlayer)
{
if (UCommonMessagingSubsystem* Messaging = TargetLocalPlayer->GetSubsystem<UCommonMessagingSubsystem>())
{
FCommonMessagingResultDelegate ResultCallback = FCommonMessagingResultDelegate::CreateUObject(this, &UAsyncAction_ShowConfirmation::HandleConfirmationResult);
Messaging->ShowConfirmation(Descriptor, ResultCallback);
return;
}
}
// If we couldn't make the confirmation, just handle an unknown result and broadcast nothing
HandleConfirmationResult(ECommonMessagingResult::Unknown);
}
void UAsyncAction_ShowConfirmation::HandleConfirmationResult(ECommonMessagingResult ConfirmationResult)
{
OnResult.Broadcast(ConfirmationResult);
SetReadyToDestroy();
}

View File

@@ -0,0 +1,194 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonGameInstance.h"
#include "CommonLocalPlayer.h"
#include "CommonSessionSubsystem.h"
#include "CommonUISettings.h"
#include "CommonUserSubsystem.h"
#include "GameUIManagerSubsystem.h"
#include "ICommonUIModule.h"
#include "LogCommonGame.h"
#include "Messaging/CommonGameDialog.h"
#include "Messaging/CommonMessagingSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonGameInstance)
UCommonGameInstance::UCommonGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UCommonGameInstance::HandleSystemMessage(FGameplayTag MessageType, FText Title, FText Message)
{
ULocalPlayer* FirstPlayer = GetFirstGamePlayer();
// Forward severe ones to the error dialog for the first player
if (FirstPlayer && MessageType.MatchesTag(FCommonUserTags::SystemMessage_Error))
{
if (UCommonMessagingSubsystem* Messaging = FirstPlayer->GetSubsystem<UCommonMessagingSubsystem>())
{
Messaging->ShowError(UCommonGameDialogDescriptor::CreateConfirmationOk(Title, Message));
}
}
}
void UCommonGameInstance::HandlePrivilegeChanged(const UCommonUserInfo* UserInfo, ECommonUserPrivilege Privilege, ECommonUserAvailability OldAvailability, ECommonUserAvailability NewAvailability)
{
// By default show errors and disconnect if play privilege for first player is lost
if (Privilege == ECommonUserPrivilege::CanPlay && OldAvailability == ECommonUserAvailability::NowAvailable && NewAvailability != ECommonUserAvailability::NowAvailable)
{
UE_LOG(LogCommonGame, Error, TEXT("HandlePrivilegeChanged: Player %d no longer has permission to play the game!"), UserInfo->LocalPlayerIndex);
// TODO: Games can do something specific in subclass
// ReturnToMainMenu();
}
}
int32 UCommonGameInstance::AddLocalPlayer(ULocalPlayer* NewPlayer, FPlatformUserId UserId)
{
int32 ReturnVal = Super::AddLocalPlayer(NewPlayer, UserId);
if (ReturnVal != INDEX_NONE)
{
if (!PrimaryPlayer.IsValid())
{
UE_LOG(LogCommonGame, Log, TEXT("AddLocalPlayer: Set %s to Primary Player"), *NewPlayer->GetName());
PrimaryPlayer = NewPlayer;
}
GetSubsystem<UGameUIManagerSubsystem>()->NotifyPlayerAdded(Cast<UCommonLocalPlayer>(NewPlayer));
}
return ReturnVal;
}
bool UCommonGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer)
{
if (PrimaryPlayer == ExistingPlayer)
{
//TODO: do we want to fall back to another player?
PrimaryPlayer.Reset();
UE_LOG(LogCommonGame, Log, TEXT("RemoveLocalPlayer: Unsetting Primary Player from %s"), *ExistingPlayer->GetName());
}
GetSubsystem<UGameUIManagerSubsystem>()->NotifyPlayerDestroyed(Cast<UCommonLocalPlayer>(ExistingPlayer));
return Super::RemoveLocalPlayer(ExistingPlayer);
}
void UCommonGameInstance::Init()
{
Super::Init();
// After subsystems are initialized, hook them together
FGameplayTagContainer PlatformTraits = ICommonUIModule::GetSettings().GetPlatformTraits();
UCommonUserSubsystem* UserSubsystem = GetSubsystem<UCommonUserSubsystem>();
if (ensure(UserSubsystem))
{
UserSubsystem->SetTraitTags(PlatformTraits);
UserSubsystem->OnHandleSystemMessage.AddDynamic(this, &UCommonGameInstance::HandleSystemMessage);
UserSubsystem->OnUserPrivilegeChanged.AddDynamic(this, &UCommonGameInstance::HandlePrivilegeChanged);
}
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
if (ensure(SessionSubsystem))
{
SessionSubsystem->OnUserRequestedSessionEvent.AddUObject(this, &UCommonGameInstance::OnUserRequestedSession);
}
}
void UCommonGameInstance::ResetUserAndSessionState()
{
UCommonUserSubsystem* UserSubsystem = GetSubsystem<UCommonUserSubsystem>();
if (ensure(UserSubsystem))
{
UserSubsystem->ResetUserState();
}
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
if (ensure(SessionSubsystem))
{
SessionSubsystem->CleanUpSessions();
}
}
void UCommonGameInstance::ReturnToMainMenu()
{
// By default when returning to main menu we should reset everything
ResetUserAndSessionState();
Super::ReturnToMainMenu();
}
void UCommonGameInstance::OnUserRequestedSession(const FPlatformUserId& PlatformUserId, UCommonSession_SearchResult* InRequestedSession, const FOnlineResultInformation& RequestedSessionResult)
{
if (InRequestedSession)
{
SetRequestedSession(InRequestedSession);
}
else
{
HandleSystemMessage(FCommonUserTags::SystemMessage_Error, NSLOCTEXT("CommonGame", "Warning_RequestedSessionFailed", "Requested Session Failed"), RequestedSessionResult.ErrorText);
}
}
void UCommonGameInstance::SetRequestedSession(UCommonSession_SearchResult* InRequestedSession)
{
RequestedSession = InRequestedSession;
if (RequestedSession)
{
if (CanJoinRequestedSession())
{
JoinRequestedSession();
}
else
{
ResetGameAndJoinRequestedSession();
}
}
}
bool UCommonGameInstance::CanJoinRequestedSession() const
{
// Default behavior is always allow joining the requested session
return true;
}
void UCommonGameInstance::JoinRequestedSession()
{
if (RequestedSession)
{
if (ULocalPlayer* const FirstPlayer = GetFirstGamePlayer())
{
UCommonSessionSubsystem* SessionSubsystem = GetSubsystem<UCommonSessionSubsystem>();
if (ensure(SessionSubsystem))
{
// Clear our current requested session since we are now acting on it.
UCommonSession_SearchResult* LocalRequestedSession = RequestedSession;
RequestedSession = nullptr;
SessionSubsystem->JoinSession(FirstPlayer->PlayerController, LocalRequestedSession);
}
}
}
}
void UCommonGameInstance::ResetGameAndJoinRequestedSession()
{
// Default behavior is to return to the main menu. The game must call JoinRequestedSession when the game is in a ready state.
ReturnToMainMenu();
}
//void UCommonGameInstance::OnPreLoadMap(const FString& MapName)
//{
// if (!IsDedicatedServerInstance())
// {
// if (!bWasInLoadMap)
// {
// UGameUIManagerSubsystem* UIManager = GetSubsystem<UGameUIManagerSubsystem>();
// for (ULocalPlayer* LocalPlayer : LocalPlayers)
// {
// UIManager->NotifyPlayerAdded(Cast<UCommonLocalPlayer>(LocalPlayer));
// }
// }
// }
//}

View File

@@ -0,0 +1,32 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Modules/ModuleManager.h"
/**
* Implements the FCommonGameModule module.
*/
class FCommonGameModule : public IModuleInterface
{
public:
FCommonGameModule();
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
};
FCommonGameModule::FCommonGameModule()
{
}
void FCommonGameModule::StartupModule()
{
}
void FCommonGameModule::ShutdownModule()
{
}
IMPLEMENT_MODULE(FCommonGameModule, CommonGame);

View File

@@ -0,0 +1,81 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonLocalPlayer.h"
#include "Engine/GameInstance.h"
#include "GameFramework/PlayerController.h"
#include "GameUIManagerSubsystem.h"
#include "GameUIPolicy.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonLocalPlayer)
class APawn;
class APlayerState;
class FViewport;
struct FSceneViewProjectionData;
UCommonLocalPlayer::UCommonLocalPlayer()
: Super(FObjectInitializer::Get())
{
}
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerControllerSet(FPlayerControllerSetDelegate::FDelegate Delegate)
{
APlayerController* PC = GetPlayerController(GetWorld());
if (PC)
{
Delegate.Execute(this, PC);
}
return OnPlayerControllerSet.Add(Delegate);
}
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerStateSet(FPlayerStateSetDelegate::FDelegate Delegate)
{
APlayerController* PC = GetPlayerController(GetWorld());
APlayerState* PlayerState = PC ? PC->PlayerState : nullptr;
if (PlayerState)
{
Delegate.Execute(this, PlayerState);
}
return OnPlayerStateSet.Add(Delegate);
}
FDelegateHandle UCommonLocalPlayer::CallAndRegister_OnPlayerPawnSet(FPlayerPawnSetDelegate::FDelegate Delegate)
{
APlayerController* PC = GetPlayerController(GetWorld());
APawn* Pawn = PC ? PC->GetPawn() : nullptr;
if (Pawn)
{
Delegate.Execute(this, Pawn);
}
return OnPlayerPawnSet.Add(Delegate);
}
bool UCommonLocalPlayer::GetProjectionData(FViewport* Viewport, FSceneViewProjectionData& ProjectionData, int32 StereoViewIndex) const
{
if (!bIsPlayerViewEnabled)
{
return false;
}
return Super::GetProjectionData(Viewport, ProjectionData, StereoViewIndex);
}
UPrimaryGameLayout* UCommonLocalPlayer::GetRootUILayout() const
{
if (UGameUIManagerSubsystem* UIManager = GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
{
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
{
return Policy->GetRootLayout(this);
}
}
return nullptr;
}

View File

@@ -0,0 +1,72 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonPlayerController.h"
#include "CommonLocalPlayer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonPlayerController)
class APawn;
ACommonPlayerController::ACommonPlayerController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void ACommonPlayerController::ReceivedPlayer()
{
Super::ReceivedPlayer();
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
{
LocalPlayer->OnPlayerControllerSet.Broadcast(LocalPlayer, this);
if (PlayerState)
{
LocalPlayer->OnPlayerStateSet.Broadcast(LocalPlayer, PlayerState);
}
}
}
void ACommonPlayerController::SetPawn(APawn* InPawn)
{
Super::SetPawn(InPawn);
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
{
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, InPawn);
}
}
void ACommonPlayerController::OnPossess(APawn* APawn)
{
Super::OnPossess(APawn);
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
{
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, APawn);
}
}
void ACommonPlayerController::OnUnPossess()
{
Super::OnUnPossess();
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
{
LocalPlayer->OnPlayerPawnSet.Broadcast(LocalPlayer, nullptr);
}
}
void ACommonPlayerController::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
if (PlayerState)
{
if (UCommonLocalPlayer* LocalPlayer = Cast<UCommonLocalPlayer>(Player))
{
LocalPlayer->OnPlayerStateSet.Broadcast(LocalPlayer, PlayerState);
}
}
}

View File

@@ -0,0 +1,543 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonPlayerInputKey.h"
#include "CommonInputSubsystem.h"
#include "CommonInputTypeEnum.h"
#include "CommonLocalPlayer.h"
#include "CommonPlayerController.h"
#include "Fonts/FontMeasure.h"
#include "Framework/Application/SlateApplication.h"
#include "Materials/Material.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Rendering/SlateRenderer.h"
#include "TimerManager.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonPlayerInputKey)
class FPaintArgs;
class FSlateRect;
#define LOCTEXT_NAMESPACE "CommonKeybindWidget"
DECLARE_LOG_CATEGORY_EXTERN(LogCommonPlayerInput, Log, All);
DEFINE_LOG_CATEGORY(LogCommonPlayerInput);
struct FSlateDrawUtil
{
static void DrawBrushCenterFit(
FSlateWindowElementList& ElementList,
uint32 InLayer,
const FGeometry& InAllottedGeometry,
const FSlateBrush* InBrush,
const FLinearColor& InTint = FLinearColor::White)
{
DrawBrushCenterFitWithOffset
(
ElementList,
InLayer,
InAllottedGeometry,
InBrush,
InTint,
FVector2D(0, 0)
);
}
static void DrawBrushCenterFitWithOffset(
FSlateWindowElementList& ElementList,
uint32 InLayer,
const FGeometry& InAllottedGeometry,
const FSlateBrush* InBrush,
const FLinearColor& InTint,
const FVector2D InOffset)
{
if (!InBrush)
{
return;
}
const FVector2D AreaSize = InAllottedGeometry.GetLocalSize();
const FVector2D ProgressSize = InBrush->GetImageSize();
const float FitScale = FMath::Min(FMath::Min(AreaSize.X / ProgressSize.X, AreaSize.Y / ProgressSize.Y), 1.0f);
const FVector2D FinalSize = FitScale * ProgressSize;
const FVector2D Offset = (InAllottedGeometry.GetLocalSize() * 0.5f) - (FinalSize * 0.5f) + InOffset;
FSlateDrawElement::MakeBox
(
ElementList,
InLayer,
InAllottedGeometry.ToPaintGeometry(FinalSize, FSlateLayoutTransform(Offset)),
InBrush,
ESlateDrawEffect::None,
InTint
);
}
};
void FMeasuredText::SetText(const FText& InText)
{
CachedText = InText;
bTextDirty = true;
}
FVector2D FMeasuredText::UpdateTextSize(const FSlateFontInfo &InFontInfo, float FontScale) const
{
if (bTextDirty)
{
bTextDirty = false;
CachedTextSize = FSlateApplication::Get().GetRenderer()->GetFontMeasureService()->Measure(CachedText, InFontInfo, FontScale);
}
return CachedTextSize;
}
UCommonPlayerInputKey::UCommonPlayerInputKey(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, BoundKeyFallback(EKeys::Invalid)
, InputTypeOverride(ECommonInputType::Count)
{
FrameSize = FVector2D(0, 0);
}
void UCommonPlayerInputKey::NativePreConstruct()
{
Super::NativePreConstruct();
UpdateKeybindWidget();
if (IsDesignTime())
{
ShowHoldBackPlate();
RecalculateDesiredSize();
}
}
void UCommonPlayerInputKey::NativeConstruct()
{
Super::NativeConstruct();
}
void UCommonPlayerInputKey::NativeDestruct()
{
if (ProgressPercentageMID)
{
// Need to restore the material on the brush before we kill off the MID.
HoldProgressBrush.SetResourceObject(ProgressPercentageMID->GetMaterial());
ProgressPercentageMID->MarkAsGarbage();
ProgressPercentageMID = nullptr;
}
Super::NativeDestruct();
}
int32 UCommonPlayerInputKey::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
int32 MaxLayer = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
if (bDrawProgress)
{
FSlateDrawUtil::DrawBrushCenterFit
(
OutDrawElements,
++MaxLayer,
AllottedGeometry,
&HoldProgressBrush,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * HoldProgressBrush.GetTint(InWidgetStyle))
);
}
if (bDrawCountdownText)
{
const FVector2D CountdownTextOffset = (AllottedGeometry.GetLocalSize() - CountdownText.GetTextSize()) * 0.5f;
FSlateDrawElement::MakeText
(
OutDrawElements,
++MaxLayer,
AllottedGeometry.ToOffsetPaintGeometry(CountdownTextOffset),
CountdownText.GetText(),
CountdownTextFont,
ESlateDrawEffect::None,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint())
);
}
else if (bDrawBrushForKey)
{
// Draw Shadow
FSlateDrawUtil::DrawBrushCenterFitWithOffset
(
OutDrawElements,
++MaxLayer,
AllottedGeometry,
&CachedKeyBrush,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * FLinearColor::Black),
FVector2D(1, 1)
);
FSlateDrawUtil::DrawBrushCenterFit
(
OutDrawElements,
++MaxLayer,
AllottedGeometry,
&CachedKeyBrush,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * CachedKeyBrush.GetTint(InWidgetStyle))
);
}
else if (KeybindText.GetTextSize().X > 0)
{
const FVector2D FrameOffset = (AllottedGeometry.GetLocalSize() - FrameSize) * 0.5f;
FSlateDrawElement::MakeBox
(
OutDrawElements,
++MaxLayer,
AllottedGeometry.ToPaintGeometry(FrameSize, FSlateLayoutTransform(FrameOffset)),
&KeyBindTextBorder,
ESlateDrawEffect::None,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint() * KeyBindTextBorder.GetTint(InWidgetStyle))
);
const FVector2D ActionTextOffset = (AllottedGeometry.GetLocalSize() - KeybindText.GetTextSize()) * 0.5f;
FSlateDrawElement::MakeText
(
OutDrawElements,
++MaxLayer,
AllottedGeometry.ToOffsetPaintGeometry(ActionTextOffset),
KeybindText.GetText(),
KeyBindTextFont,
ESlateDrawEffect::None,
FLinearColor(InWidgetStyle.GetColorAndOpacityTint())
);
}
return MaxLayer;
}
void UCommonPlayerInputKey::StartHoldProgress(FName HoldActionName, float HoldDuration)
{
if (HoldActionName == BoundAction && ensureMsgf(HoldDuration > 0.0f, TEXT("Trying to perform hold action \"%s\" with no HoldDuration"), *BoundAction.ToString()))
{
HoldKeybindDuration = HoldDuration;
HoldKeybindStartTime = GetWorld()->GetRealTimeSeconds();
UpdateHoldProgress();
}
}
void UCommonPlayerInputKey::StopHoldProgress(FName HoldActionName, bool bCompletedSuccessfully)
{
if (HoldActionName == BoundAction)
{
HoldKeybindStartTime = 0.f;
HoldKeybindDuration = 0.f;
if (ensure(ProgressPercentageMID))
{
ProgressPercentageMID->SetScalarParameterValue(PercentageMaterialParameterName, 0.f);
}
if (bDrawCountdownText)
{
bDrawCountdownText = false;
Invalidate(EInvalidateWidget::Paint);
RecalculateDesiredSize();
}
}
}
void UCommonPlayerInputKey::SyncHoldProgress()
{
// If we had an active hold action, stop it
if (HoldKeybindStartTime > 0.f)
{
StopHoldProgress(BoundAction, false);
}
}
void UCommonPlayerInputKey::UpdateHoldProgress()
{
if (HoldKeybindStartTime != 0.f && HoldKeybindDuration > 0.f)
{
const float CurrentTime = GetWorld()->GetRealTimeSeconds();
const float ElapsedTime = FMath::Min(CurrentTime - HoldKeybindStartTime, HoldKeybindDuration);
const float RemainingTime = FMath::Max(0.0f, HoldKeybindDuration - ElapsedTime);
if (ElapsedTime < HoldKeybindDuration && ensure(ProgressPercentageMID))
{
const float HoldKeybindPercentage = ElapsedTime / HoldKeybindDuration;
ProgressPercentageMID->SetScalarParameterValue(PercentageMaterialParameterName, HoldKeybindPercentage);
// Schedule a callback for next tick to update the hold progress again.
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::UpdateHoldProgress);
}
if (bShowTimeCountDown)
{
FNumberFormattingOptions Options;
Options.MinimumFractionalDigits = 1;
Options.MaximumFractionalDigits = 1;
CountdownText.SetText(FText::AsNumber(RemainingTime, &Options));
bDrawCountdownText = true;
Invalidate(EInvalidateWidget::Paint);
RecalculateDesiredSize();
}
}
}
void UCommonPlayerInputKey::UpdateKeybindWidget()
{
if (!GetOwningPlayer<ACommonPlayerController>())
{
bWaitingForPlayerController = true;
return;
}
UCommonInputSubsystem* CommonInputSubsystem = GetInputSubsystem();
if (CommonInputSubsystem && !CommonInputSubsystem->ShouldShowInputKeys())
{
SetVisibility(ESlateVisibility::Collapsed);
return;
}
const bool bIsUsingGamepad = (InputTypeOverride == ECommonInputType::Gamepad) || ((CommonInputSubsystem != nullptr) && (CommonInputSubsystem->GetCurrentInputType() == ECommonInputType::Gamepad)) ;
if (!BoundKey.IsValid())
{
BoundKey = BoundKeyFallback;
}
UE_LOG(LogCommonPlayerInput, Verbose, TEXT("UCommonKeybindWidget::UpdateKeybindWidget: Action: %s Key: %s"), *(BoundAction.ToString()), *(BoundKey.ToString()));
// Must be called before Update, due to the creation of ProgressPercentageMID which will be used in Update
SetupHoldKeybind();
bool NewDrawBrushForKey = false;
bool NeedToRecalcSize = false;
if (BoundKey.IsValid())
{
SetVisibility(ESlateVisibility::HitTestInvisible);
ShowHoldBackPlate();
NeedToRecalcSize = true;
}
else
{
if (bShowUnboundStatus)
{
SetVisibility(ESlateVisibility::HitTestInvisible);
NewDrawBrushForKey = false;
KeybindText.SetText(LOCTEXT("Unbound", "Unbound"));
NeedToRecalcSize = true;
}
else
{
SetVisibility(ESlateVisibility::Collapsed);
}
}
if (bDrawBrushForKey != NewDrawBrushForKey)
{
bDrawBrushForKey = NewDrawBrushForKey;
Invalidate(EInvalidateWidget::Paint);
}
// As RecalculateDesiredSize relies on the bDrawBrushForKey
// we shouldn't call it until that value has been finalized
// for the update
if (NeedToRecalcSize)
{
RecalculateDesiredSize();
}
}
void UCommonPlayerInputKey::SetBoundKey(FKey NewKey)
{
if (NewKey != BoundKey)
{
BoundKeyFallback = NewKey;
BoundAction = NAME_None;
UpdateKeybindWidget();
}
}
void UCommonPlayerInputKey::SetBoundAction(FName NewBoundAction)
{
bool bUpdateWidget = true;
if (BoundAction != NewBoundAction)
{
BoundAction = NewBoundAction;
}
if (bUpdateWidget)
{
UpdateKeybindWidget();
}
}
void UCommonPlayerInputKey::NativeOnInitialized()
{
Super::NativeOnInitialized();
if (UCommonLocalPlayer* CommonLocalPlayer = GetOwningLocalPlayer<UCommonLocalPlayer>())
{
CommonLocalPlayer->OnPlayerControllerSet.AddUObject(this, &ThisClass::HandlePlayerControllerSet);
}
}
void UCommonPlayerInputKey::SetForcedHoldKeybind(bool InForcedHoldKeybind)
{
if (InForcedHoldKeybind)
{
SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus::ForcedHold);
}
else
{
SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus::NoForcedHold);
}
}
void UCommonPlayerInputKey::SetForcedHoldKeybindStatus(ECommonKeybindForcedHoldStatus InForcedHoldKeybindStatus)
{
ForcedHoldKeybindStatus = InForcedHoldKeybindStatus;
UpdateKeybindWidget();
}
void UCommonPlayerInputKey::SetShowProgressCountDown(bool bShow)
{
bShowTimeCountDown = bShow;
}
void UCommonPlayerInputKey::SetupHoldKeybind()
{
ACommonPlayerController* OwningCommonPlayer = Cast<ACommonPlayerController>(GetOwningPlayer());
// Setup the hold
if (ForcedHoldKeybindStatus == ECommonKeybindForcedHoldStatus::ForcedHold)
{
bIsHoldKeybind = true;
}
else if (ForcedHoldKeybindStatus == ECommonKeybindForcedHoldStatus::NeverShowHold)
{
bIsHoldKeybind = false;
}
if (ensure(OwningCommonPlayer))
{
if (bIsHoldKeybind)
{
// Setup the ProgressPercentageMID
if (ProgressPercentageMID == nullptr)
{
if (UMaterialInterface* Material = Cast<UMaterialInterface>(HoldProgressBrush.GetResourceObject()))
{
ProgressPercentageMID = UMaterialInstanceDynamic::Create(Material, this);
HoldProgressBrush.SetResourceObject(ProgressPercentageMID);
}
}
SyncHoldProgress();
}
}
}
void UCommonPlayerInputKey::ShowHoldBackPlate()
{
bool bDirty = false;
if (IsHoldKeybind())
{
float BrushSizeAsValue = 32.0f;
float DesiredBoxSize = BrushSizeAsValue + 10.0f;
if (!bDrawBrushForKey)
{
DesiredBoxSize += 14.0f;
}
const FVector2D NewDesiredBrushSize(DesiredBoxSize, DesiredBoxSize);
if (HoldProgressBrush.GetImageSize() != NewDesiredBrushSize)
{
HoldProgressBrush.SetImageSize(NewDesiredBrushSize);
bDirty = true;
}
if (!bDrawProgress)
{
bDrawProgress = true;
bDirty = true;
}
static const FName BackAlphaName = TEXT("BackAlpha");
static const FName OutlineAlphaName = TEXT("OutlineAlpha");
if (ProgressPercentageMID)
{
ProgressPercentageMID->SetScalarParameterValue(BackAlphaName, 0.2f);
ProgressPercentageMID->SetScalarParameterValue(OutlineAlphaName, 0.4f);
}
}
else
{
if (bDrawProgress)
{
bDrawProgress = false;
bDirty = true;
}
}
if (bDirty)
{
Invalidate(EInvalidateWidget::Paint);
}
}
void UCommonPlayerInputKey::HandlePlayerControllerSet(UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController)
{
if (bWaitingForPlayerController && GetOwningPlayer<ACommonPlayerController>())
{
UpdateKeybindWidget();
bWaitingForPlayerController = false;
}
}
void UCommonPlayerInputKey::RecalculateDesiredSize()
{
FVector2D MaximumDesiredSize(0, 0);
float LayoutScale = 1;
if (bDrawProgress)
{
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, HoldProgressBrush.GetImageSize());
}
if (bDrawCountdownText)
{
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, CountdownText.UpdateTextSize(CountdownTextFont, LayoutScale));
}
else if (bDrawBrushForKey)
{
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, CachedKeyBrush.GetImageSize());
}
else
{
const FVector2D KeybindTextSize = KeybindText.UpdateTextSize(KeyBindTextFont, LayoutScale);
FrameSize = FVector2D::Max(KeybindTextSize, KeybindFrameMinimumSize) + KeybindTextPadding.GetDesiredSize();
MaximumDesiredSize = FVector2D::Max(MaximumDesiredSize, FrameSize);
}
SetMinimumDesiredSize(MaximumDesiredSize);
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,171 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonUIExtensions.h"
#include "CommonInputSubsystem.h"
#include "CommonInputTypeEnum.h"
#include "CommonLocalPlayer.h"
#include "Engine/GameInstance.h"
#include "GameUIManagerSubsystem.h"
#include "GameUIPolicy.h"
#include "PrimaryGameLayout.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonUIExtensions)
int32 UCommonUIExtensions::InputSuspensions = 0;
ECommonInputType UCommonUIExtensions::GetOwningPlayerInputType(const UUserWidget* WidgetContextObject)
{
if (WidgetContextObject)
{
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
{
return InputSubsystem->GetCurrentInputType();
}
}
return ECommonInputType::Count;
}
bool UCommonUIExtensions::IsOwningPlayerUsingTouch(const UUserWidget* WidgetContextObject)
{
if (WidgetContextObject)
{
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
{
return InputSubsystem->GetCurrentInputType() == ECommonInputType::Touch;
}
}
return false;
}
bool UCommonUIExtensions::IsOwningPlayerUsingGamepad(const UUserWidget* WidgetContextObject)
{
if (WidgetContextObject)
{
if (const UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(WidgetContextObject->GetOwningLocalPlayer()))
{
return InputSubsystem->GetCurrentInputType() == ECommonInputType::Gamepad;
}
}
return false;
}
UCommonActivatableWidget* UCommonUIExtensions::PushContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, FGameplayTag LayerName, TSubclassOf<UCommonActivatableWidget> WidgetClass)
{
if (!ensure(LocalPlayer) || !ensure(WidgetClass != nullptr))
{
return nullptr;
}
if (UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
{
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
{
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
{
return RootLayout->PushWidgetToLayerStack(LayerName, WidgetClass);
}
}
}
return nullptr;
}
void UCommonUIExtensions::PushStreamedContentToLayer_ForPlayer(const ULocalPlayer* LocalPlayer, FGameplayTag LayerName, TSoftClassPtr<UCommonActivatableWidget> WidgetClass)
{
if (!ensure(LocalPlayer) || !ensure(!WidgetClass.IsNull()))
{
return;
}
if (UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
{
if (UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
{
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
{
const bool bSuspendInputUntilComplete = true;
RootLayout->PushWidgetToLayerStackAsync(LayerName, bSuspendInputUntilComplete, WidgetClass);
}
}
}
}
void UCommonUIExtensions::PopContentFromLayer(UCommonActivatableWidget* ActivatableWidget)
{
if (!ActivatableWidget)
{
// Ignore request to pop an already deleted widget
return;
}
if (const ULocalPlayer* LocalPlayer = ActivatableWidget->GetOwningLocalPlayer())
{
if (const UGameUIManagerSubsystem* UIManager = LocalPlayer->GetGameInstance()->GetSubsystem<UGameUIManagerSubsystem>())
{
if (const UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
{
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CastChecked<UCommonLocalPlayer>(LocalPlayer)))
{
RootLayout->FindAndRemoveWidgetFromLayer(ActivatableWidget);
}
}
}
}
}
ULocalPlayer* UCommonUIExtensions::GetLocalPlayerFromController(APlayerController* PlayerController)
{
if (PlayerController)
{
return Cast<ULocalPlayer>(PlayerController->Player);
}
return nullptr;
}
FName UCommonUIExtensions::SuspendInputForPlayer(APlayerController* PlayerController, FName SuspendReason)
{
return SuspendInputForPlayer(PlayerController ? PlayerController->GetLocalPlayer() : nullptr, SuspendReason);
}
FName UCommonUIExtensions::SuspendInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendReason)
{
if (UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(LocalPlayer))
{
InputSuspensions++;
FName SuspendToken = SuspendReason;
SuspendToken.SetNumber(InputSuspensions);
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::MouseAndKeyboard, SuspendToken, true);
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Gamepad, SuspendToken, true);
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Touch, SuspendToken, true);
return SuspendToken;
}
return NAME_None;
}
void UCommonUIExtensions::ResumeInputForPlayer(APlayerController* PlayerController, FName SuspendToken)
{
ResumeInputForPlayer(PlayerController ? PlayerController->GetLocalPlayer() : nullptr, SuspendToken);
}
void UCommonUIExtensions::ResumeInputForPlayer(ULocalPlayer* LocalPlayer, FName SuspendToken)
{
if (SuspendToken == NAME_None)
{
return;
}
if (UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(LocalPlayer))
{
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::MouseAndKeyboard, SuspendToken, false);
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Gamepad, SuspendToken, false);
CommonInputSubsystem->SetInputTypeFilter(ECommonInputType::Touch, SuspendToken, false);
}
}

View File

@@ -0,0 +1,76 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GameUIManagerSubsystem.h"
#include "Engine/GameInstance.h"
#include "GameUIPolicy.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GameUIManagerSubsystem)
class FSubsystemCollectionBase;
class UClass;
void UGameUIManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
if (!CurrentPolicy && !DefaultUIPolicyClass.IsNull())
{
TSubclassOf<UGameUIPolicy> PolicyClass = DefaultUIPolicyClass.LoadSynchronous();
SwitchToPolicy(NewObject<UGameUIPolicy>(this, PolicyClass));
}
}
void UGameUIManagerSubsystem::Deinitialize()
{
Super::Deinitialize();
SwitchToPolicy(nullptr);
}
bool UGameUIManagerSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
if (!CastChecked<UGameInstance>(Outer)->IsDedicatedServerInstance())
{
TArray<UClass*> ChildClasses;
GetDerivedClasses(GetClass(), ChildClasses, false);
// Only create an instance if there is no override implementation defined elsewhere
return ChildClasses.Num() == 0;
}
return false;
}
void UGameUIManagerSubsystem::NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer)
{
if (ensure(LocalPlayer) && CurrentPolicy)
{
CurrentPolicy->NotifyPlayerAdded(LocalPlayer);
}
}
void UGameUIManagerSubsystem::NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer)
{
if (LocalPlayer && CurrentPolicy)
{
CurrentPolicy->NotifyPlayerRemoved(LocalPlayer);
}
}
void UGameUIManagerSubsystem::NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer)
{
if (LocalPlayer && CurrentPolicy)
{
CurrentPolicy->NotifyPlayerDestroyed(LocalPlayer);
}
}
void UGameUIManagerSubsystem::SwitchToPolicy(UGameUIPolicy* InPolicy)
{
if (CurrentPolicy != InPolicy)
{
CurrentPolicy = InPolicy;
}
}

View File

@@ -0,0 +1,203 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "GameUIPolicy.h"
#include "Engine/GameInstance.h"
#include "Framework/Application/SlateApplication.h"
#include "GameUIManagerSubsystem.h"
#include "CommonLocalPlayer.h"
#include "PrimaryGameLayout.h"
#include "Engine/Engine.h"
#include "LogCommonGame.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GameUIPolicy)
// Static
UGameUIPolicy* UGameUIPolicy::GetGameUIPolicy(const UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
{
if (UGameInstance* GameInstance = World->GetGameInstance())
{
if (UGameUIManagerSubsystem* UIManager = UGameInstance::GetSubsystem<UGameUIManagerSubsystem>(GameInstance))
{
return UIManager->GetCurrentUIPolicy();
}
}
}
return nullptr;
}
UGameUIManagerSubsystem* UGameUIPolicy::GetOwningUIManager() const
{
return CastChecked<UGameUIManagerSubsystem>(GetOuter());
}
UWorld* UGameUIPolicy::GetWorld() const
{
return GetOwningUIManager()->GetGameInstance()->GetWorld();
}
UPrimaryGameLayout* UGameUIPolicy::GetRootLayout(const UCommonLocalPlayer* LocalPlayer) const
{
const FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer);
return LayoutInfo ? LayoutInfo->RootLayout : nullptr;
}
void UGameUIPolicy::NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer)
{
LocalPlayer->OnPlayerControllerSet.AddWeakLambda(this, [this](UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController)
{
NotifyPlayerRemoved(LocalPlayer);
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
{
AddLayoutToViewport(LocalPlayer, LayoutInfo->RootLayout);
LayoutInfo->bAddedToViewport = true;
}
else
{
CreateLayoutWidget(LocalPlayer);
}
});
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
{
AddLayoutToViewport(LocalPlayer, LayoutInfo->RootLayout);
LayoutInfo->bAddedToViewport = true;
}
else
{
CreateLayoutWidget(LocalPlayer);
}
}
void UGameUIPolicy::NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer)
{
if (FRootViewportLayoutInfo* LayoutInfo = RootViewportLayouts.FindByKey(LocalPlayer))
{
RemoveLayoutFromViewport(LocalPlayer, LayoutInfo->RootLayout);
LayoutInfo->bAddedToViewport = false;
if (LocalMultiplayerInteractionMode == ELocalMultiplayerInteractionMode::SingleToggle && !LocalPlayer->IsPrimaryPlayer())
{
UPrimaryGameLayout* RootLayout = LayoutInfo->RootLayout;
if (RootLayout && !RootLayout->IsDormant())
{
// We're removing a secondary player's root while it's in control - transfer control back to the primary player's root
RootLayout->SetIsDormant(true);
for (const FRootViewportLayoutInfo& RootLayoutInfo : RootViewportLayouts)
{
if (RootLayoutInfo.LocalPlayer->IsPrimaryPlayer())
{
if (UPrimaryGameLayout* PrimaryRootLayout = RootLayoutInfo.RootLayout)
{
PrimaryRootLayout->SetIsDormant(false);
}
}
}
}
}
}
}
void UGameUIPolicy::NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer)
{
NotifyPlayerRemoved(LocalPlayer);
LocalPlayer->OnPlayerControllerSet.RemoveAll(this);
const int32 LayoutInfoIdx = RootViewportLayouts.IndexOfByKey(LocalPlayer);
if (LayoutInfoIdx != INDEX_NONE)
{
UPrimaryGameLayout* Layout = RootViewportLayouts[LayoutInfoIdx].RootLayout;
RootViewportLayouts.RemoveAt(LayoutInfoIdx);
RemoveLayoutFromViewport(LocalPlayer, Layout);
OnRootLayoutReleased(LocalPlayer, Layout);
}
}
void UGameUIPolicy::AddLayoutToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
{
UE_LOG(LogCommonGame, Log, TEXT("[%s] is adding player [%s]'s root layout [%s] to the viewport"), *GetName(), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
Layout->SetPlayerContext(FLocalPlayerContext(LocalPlayer));
Layout->AddToPlayerScreen(1000);
OnRootLayoutAddedToViewport(LocalPlayer, Layout);
}
void UGameUIPolicy::RemoveLayoutFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
{
TWeakPtr<SWidget> LayoutSlateWidget = Layout->GetCachedWidget();
if (LayoutSlateWidget.IsValid())
{
UE_LOG(LogCommonGame, Log, TEXT("[%s] is removing player [%s]'s root layout [%s] from the viewport"), *GetName(), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
Layout->RemoveFromParent();
if (LayoutSlateWidget.IsValid())
{
UE_LOG(LogCommonGame, Log, TEXT("Player [%s]'s root layout [%s] has been removed from the viewport, but other references to its underlying Slate widget still exist. Noting in case we leak it."), *GetNameSafe(LocalPlayer), *GetNameSafe(Layout));
}
OnRootLayoutRemovedFromViewport(LocalPlayer, Layout);
}
}
void UGameUIPolicy::OnRootLayoutAddedToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
{
#if WITH_EDITOR
if (GIsEditor && LocalPlayer->IsPrimaryPlayer())
{
// So our controller will work in PIE without needing to click in the viewport
FSlateApplication::Get().SetUserFocusToGameViewport(0);
}
#endif
}
void UGameUIPolicy::OnRootLayoutRemovedFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
{
}
void UGameUIPolicy::OnRootLayoutReleased(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout)
{
}
void UGameUIPolicy::RequestPrimaryControl(UPrimaryGameLayout* Layout)
{
if (LocalMultiplayerInteractionMode == ELocalMultiplayerInteractionMode::SingleToggle && Layout->IsDormant())
{
for (const FRootViewportLayoutInfo& LayoutInfo : RootViewportLayouts)
{
UPrimaryGameLayout* RootLayout = LayoutInfo.RootLayout;
if (RootLayout && !RootLayout->IsDormant())
{
RootLayout->SetIsDormant(true);
break;
}
}
Layout->SetIsDormant(false);
}
}
void UGameUIPolicy::CreateLayoutWidget(UCommonLocalPlayer* LocalPlayer)
{
if (APlayerController* PlayerController = LocalPlayer->GetPlayerController(GetWorld()))
{
TSubclassOf<UPrimaryGameLayout> LayoutWidgetClass = GetLayoutWidgetClass(LocalPlayer);
if (ensure(LayoutWidgetClass && !LayoutWidgetClass->HasAnyClassFlags(CLASS_Abstract)))
{
UPrimaryGameLayout* NewLayoutObject = CreateWidget<UPrimaryGameLayout>(PlayerController, LayoutWidgetClass);
RootViewportLayouts.Emplace(LocalPlayer, NewLayoutObject, true);
AddLayoutToViewport(LocalPlayer, NewLayoutObject);
}
}
}
TSubclassOf<UPrimaryGameLayout> UGameUIPolicy::GetLayoutWidgetClass(UCommonLocalPlayer* LocalPlayer)
{
return LayoutClass.LoadSynchronous();
}

View File

@@ -0,0 +1,5 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LogCommonGame.h"
DEFINE_LOG_CATEGORY(LogCommonGame);

View File

@@ -0,0 +1,7 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(LogCommonGame, Log, All);

View File

@@ -0,0 +1,106 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Messaging/CommonGameDialog.h"
#include "Messaging/CommonMessagingSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonGameDialog)
#define LOCTEXT_NAMESPACE "Messaging"
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationOk(const FText& Header, const FText& Body)
{
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
Descriptor->Header = Header;
Descriptor->Body = Body;
FConfirmationDialogAction ConfirmAction;
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
ConfirmAction.OptionalDisplayText = LOCTEXT("Ok", "Ok");
Descriptor->ButtonActions.Add(ConfirmAction);
return Descriptor;
}
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationOkCancel(const FText& Header, const FText& Body)
{
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
Descriptor->Header = Header;
Descriptor->Body = Body;
FConfirmationDialogAction ConfirmAction;
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
ConfirmAction.OptionalDisplayText = LOCTEXT("Ok", "Ok");
FConfirmationDialogAction CancelAction;
CancelAction.Result = ECommonMessagingResult::Cancelled;
CancelAction.OptionalDisplayText = LOCTEXT("Cancel", "Cancel");
Descriptor->ButtonActions.Add(ConfirmAction);
Descriptor->ButtonActions.Add(CancelAction);
return Descriptor;
}
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationYesNo(const FText& Header, const FText& Body)
{
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
Descriptor->Header = Header;
Descriptor->Body = Body;
FConfirmationDialogAction ConfirmAction;
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
ConfirmAction.OptionalDisplayText = LOCTEXT("Yes", "Yes");
FConfirmationDialogAction DeclineAction;
DeclineAction.Result = ECommonMessagingResult::Declined;
DeclineAction.OptionalDisplayText = LOCTEXT("No", "No");
Descriptor->ButtonActions.Add(ConfirmAction);
Descriptor->ButtonActions.Add(DeclineAction);
return Descriptor;
}
UCommonGameDialogDescriptor* UCommonGameDialogDescriptor::CreateConfirmationYesNoCancel(const FText& Header, const FText& Body)
{
UCommonGameDialogDescriptor* Descriptor = NewObject<UCommonGameDialogDescriptor>();
Descriptor->Header = Header;
Descriptor->Body = Body;
FConfirmationDialogAction ConfirmAction;
ConfirmAction.Result = ECommonMessagingResult::Confirmed;
ConfirmAction.OptionalDisplayText = LOCTEXT("Yes", "Yes");
FConfirmationDialogAction DeclineAction;
DeclineAction.Result = ECommonMessagingResult::Declined;
DeclineAction.OptionalDisplayText = LOCTEXT("No", "No");
FConfirmationDialogAction CancelAction;
CancelAction.Result = ECommonMessagingResult::Cancelled;
CancelAction.OptionalDisplayText = LOCTEXT("Cancel", "Cancel");
Descriptor->ButtonActions.Add(ConfirmAction);
Descriptor->ButtonActions.Add(DeclineAction);
Descriptor->ButtonActions.Add(CancelAction);
return Descriptor;
}
UCommonGameDialog::UCommonGameDialog()
{
}
void UCommonGameDialog::SetupDialog(UCommonGameDialogDescriptor* Descriptor, FCommonMessagingResultDelegate ResultCallback)
{
}
void UCommonGameDialog::KillDialog()
{
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,46 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Messaging/CommonMessagingSubsystem.h"
#include "Engine/GameInstance.h"
#include "Engine/LocalPlayer.h"
#include "UObject/UObjectHash.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonMessagingSubsystem)
class FSubsystemCollectionBase;
class UClass;
void UCommonMessagingSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
}
void UCommonMessagingSubsystem::Deinitialize()
{
Super::Deinitialize();
}
bool UCommonMessagingSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
if (!CastChecked<ULocalPlayer>(Outer)->GetGameInstance()->IsDedicatedServerInstance())
{
TArray<UClass*> ChildClasses;
GetDerivedClasses(GetClass(), ChildClasses, false);
// Only create an instance if there is no override implementation defined elsewhere
return ChildClasses.Num() == 0;
}
return false;
}
void UCommonMessagingSubsystem::ShowConfirmation(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback)
{
}
void UCommonMessagingSubsystem::ShowError(UCommonGameDialogDescriptor* DialogDescriptor, FCommonMessagingResultDelegate ResultCallback)
{
}

View File

@@ -0,0 +1,132 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PrimaryGameLayout.h"
#include "CommonLocalPlayer.h"
#include "Engine/GameInstance.h"
#include "GameUIManagerSubsystem.h"
#include "GameUIPolicy.h"
#include "Kismet/GameplayStatics.h"
#include "LogCommonGame.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(PrimaryGameLayout)
class UObject;
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayoutForPrimaryPlayer(const UObject* WorldContextObject)
{
UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(WorldContextObject);
APlayerController* PlayerController = GameInstance->GetPrimaryPlayerController(false);
return GetPrimaryGameLayout(PlayerController);
}
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayout(APlayerController* PlayerController)
{
return PlayerController ? GetPrimaryGameLayout(Cast<UCommonLocalPlayer>(PlayerController->Player)) : nullptr;
}
/*static*/ UPrimaryGameLayout* UPrimaryGameLayout::GetPrimaryGameLayout(ULocalPlayer* LocalPlayer)
{
if (LocalPlayer)
{
const UCommonLocalPlayer* CommonLocalPlayer = CastChecked<UCommonLocalPlayer>(LocalPlayer);
if (const UGameInstance* GameInstance = CommonLocalPlayer->GetGameInstance())
{
if (UGameUIManagerSubsystem* UIManager = GameInstance->GetSubsystem<UGameUIManagerSubsystem>())
{
if (const UGameUIPolicy* Policy = UIManager->GetCurrentUIPolicy())
{
if (UPrimaryGameLayout* RootLayout = Policy->GetRootLayout(CommonLocalPlayer))
{
return RootLayout;
}
}
}
}
}
return nullptr;
}
UPrimaryGameLayout::UPrimaryGameLayout(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UPrimaryGameLayout::SetIsDormant(bool InDormant)
{
if (bIsDormant != InDormant)
{
const ULocalPlayer* LP = GetOwningLocalPlayer();
const int32 PlayerId = LP ? LP->GetControllerId() : -1;
const TCHAR* OldDormancyStr = bIsDormant ? TEXT("Dormant") : TEXT("Not-Dormant");
const TCHAR* NewDormancyStr = InDormant ? TEXT("Dormant") : TEXT("Not-Dormant");
const TCHAR* PrimaryPlayerStr = LP && LP->IsPrimaryPlayer() ? TEXT("[Primary]") : TEXT("[Non-Primary]");
UE_LOG(LogCommonGame, Display, TEXT("%s PrimaryGameLayout Dormancy changed for [%d] from [%s] to [%s]"), PrimaryPlayerStr, PlayerId, OldDormancyStr, NewDormancyStr);
bIsDormant = InDormant;
OnIsDormantChanged();
}
}
void UPrimaryGameLayout::OnIsDormantChanged()
{
//@TODO NDarnell Determine what to do with dormancy, in the past we treated dormancy as a way to shutoff rendering
//and the view for the other local players when we force multiple players to use the player view of a single player.
//if (UCommonLocalPlayer* LocalPlayer = GetOwningLocalPlayer<UCommonLocalPlayer>())
//{
// // When the root layout is dormant, we don't want to render anything from the owner's view either
// LocalPlayer->SetIsPlayerViewEnabled(!bIsDormant);
//}
//SetVisibility(bIsDormant ? ESlateVisibility::Collapsed : ESlateVisibility::SelfHitTestInvisible);
//OnLayoutDormancyChanged().Broadcast(bIsDormant);
}
void UPrimaryGameLayout::RegisterLayer(FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget)
{
if (!IsDesignTime())
{
LayerWidget->OnTransitioningChanged.AddUObject(this, &UPrimaryGameLayout::OnWidgetStackTransitioning);
// TODO: Consider allowing a transition duration, we currently set it to 0, because if it's not 0, the
// transition effect will cause focus to not transition properly to the new widgets when using
// gamepad always.
LayerWidget->SetTransitionDuration(0.0);
Layers.Add(LayerTag, LayerWidget);
}
}
void UPrimaryGameLayout::OnWidgetStackTransitioning(UCommonActivatableWidgetContainerBase* Widget, bool bIsTransitioning)
{
if (bIsTransitioning)
{
const FName SuspendToken = UCommonUIExtensions::SuspendInputForPlayer(GetOwningLocalPlayer(), TEXT("GlobalStackTransion"));
SuspendInputTokens.Add(SuspendToken);
}
else
{
if (ensure(SuspendInputTokens.Num() > 0))
{
const FName SuspendToken = SuspendInputTokens.Pop();
UCommonUIExtensions::ResumeInputForPlayer(GetOwningLocalPlayer(), SuspendToken);
}
}
}
void UPrimaryGameLayout::FindAndRemoveWidgetFromLayer(UCommonActivatableWidget* ActivatableWidget)
{
// We're not sure what layer the widget is on so go searching.
for (const auto& LayerKVP : Layers)
{
LayerKVP.Value->RemoveWidget(*ActivatableWidget);
}
}
UCommonActivatableWidgetContainerBase* UPrimaryGameLayout::GetLayerWidget(FGameplayTag LayerName)
{
return Layers.FindRef(LayerName);
}