201 lines
6.5 KiB
C++
201 lines
6.5 KiB
C++
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
||
|
#include "GameUIPolicy.h"
|
||
|
#include "CommonActivatableWidget.h"
|
||
|
#include "Engine/LocalPlayer.h"
|
||
|
#include "GameUIManagerSubsystem.h"
|
||
|
#include "CommonLocalPlayer.h"
|
||
|
#include "PrimaryGameLayout.h"
|
||
|
#include "Engine/Engine.h"
|
||
|
#include "LogCommonGame.h"
|
||
|
|
||
|
// 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->RemoveFromViewport();
|
||
|
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();
|
||
|
}
|