第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_AsyncAction_CreateWidget.h"
#include "Blueprint/UserWidget.h"
#include "Blueprint/WidgetBlueprintLibrary.h"
#include "Engine/AssetManager.h"
#include "Engine/Engine.h"
#include "Engine/GameInstance.h"
#include "Engine/StreamableManager.h"
#include "UI/GUIS_GameUIFunctionLibrary.h"
#include "UObject/Stack.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GUIS_AsyncAction_CreateWidget)
class UUserWidget;
static const FName InputFilterReason_Template = FName(TEXT("CreatingWidgetAsync"));
UGUIS_AsyncAction_CreateWidget::UGUIS_AsyncAction_CreateWidget(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bSuspendInputUntilComplete(true)
{
}
UGUIS_AsyncAction_CreateWidget* UGUIS_AsyncAction_CreateWidget::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);
UGUIS_AsyncAction_CreateWidget* Action = NewObject<UGUIS_AsyncAction_CreateWidget>();
Action->UserWidgetSoftClass = InUserWidgetSoftClass;
Action->OwningPlayer = InOwningPlayer;
Action->World = World;
Action->GameInstance = World->GetGameInstance();
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
Action->RegisterWithGameInstance(World);
return Action;
}
void UGUIS_AsyncAction_CreateWidget::Activate()
{
SuspendInputToken = bSuspendInputUntilComplete ? UGUIS_GameUIFunctionLibrary::SuspendInputForPlayer(OwningPlayer.Get(), InputFilterReason_Template) : NAME_None;
TWeakObjectPtr<UGUIS_AsyncAction_CreateWidget> 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]()
{
UGUIS_GameUIFunctionLibrary::ResumeInputForPlayer(OwningPlayer.Get(), SuspendInputToken);
})
);
}
void UGUIS_AsyncAction_CreateWidget::Cancel()
{
Super::Cancel();
if (StreamingHandle.IsValid())
{
StreamingHandle->CancelHandle();
StreamingHandle.Reset();
}
}
void UGUIS_AsyncAction_CreateWidget::OnWidgetLoaded()
{
if (bSuspendInputUntilComplete)
{
UGUIS_GameUIFunctionLibrary::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,130 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_AsyncAction_PushContentToUILayer.h"
#include "Engine/Engine.h"
#include "UI/GUIS_GameUILayout.h"
#include "UObject/Stack.h"
#include "Widgets/CommonActivatableWidgetContainer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GUIS_AsyncAction_PushContentToUILayer)
UGUIS_AsyncAction_PushContentToUILayer::UGUIS_AsyncAction_PushContentToUILayer(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UGUIS_AsyncAction_PushContentToUILayer* UGUIS_AsyncAction_PushContentToUILayer::PushContentToUILayer(UGUIS_GameUILayout* UILayout,
TSoftClassPtr<UCommonActivatableWidget> InWidgetClass, FGameplayTag InLayerName,
bool bSuspendInputUntilComplete)
{
if (!IsValid(UILayout))
{
FFrame::KismetExecutionMessage(TEXT("PushContentToUILayer was passed a invalid Layout"), ELogVerbosity::Error);
return nullptr;
}
if (InWidgetClass.IsNull())
{
FFrame::KismetExecutionMessage(TEXT("PushContentToUILayer was passed a null WidgetClass"), ELogVerbosity::Error);
return nullptr;
}
if (UWorld* World = GEngine->GetWorldFromContextObject(UILayout->GetWorld(), EGetWorldErrorMode::LogAndReturnNull))
{
UGUIS_AsyncAction_PushContentToUILayer* Action = NewObject<UGUIS_AsyncAction_PushContentToUILayer>();
Action->WidgetClass = InWidgetClass;
Action->RootLayout = UILayout;
Action->OwningPlayerPtr = UILayout->GetOwningPlayer();
Action->LayerName = InLayerName;
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
Action->RegisterWithGameInstance(World);
return Action;
}
return nullptr;
}
UGUIS_AsyncAction_PushContentToUILayer* UGUIS_AsyncAction_PushContentToUILayer::PushContentToUILayerForPlayer(APlayerController* PlayerController,
TSoftClassPtr<UCommonActivatableWidget> InWidgetClass,
FGameplayTag InLayerName, bool bSuspendInputUntilComplete)
{
if (!IsValid(PlayerController))
{
FFrame::KismetExecutionMessage(TEXT("PushContentToUILayerForPlayer was passed a invalid PlayerController"), ELogVerbosity::Error);
return nullptr;
}
if (InWidgetClass.IsNull())
{
FFrame::KismetExecutionMessage(TEXT("PushContentToUILayer was passed a null WidgetClass"), ELogVerbosity::Error);
return nullptr;
}
UGUIS_GameUILayout* UILayout = UGUIS_GameUIFunctionLibrary::GetGameUILayoutForPlayer(PlayerController);
if (UILayout == nullptr)
{
FFrame::KismetExecutionMessage(TEXT("PushContentToUILayerForPlayer failed to find UILayout for player."), ELogVerbosity::Error);
return nullptr;
}
if (UWorld* World = GEngine->GetWorldFromContextObject(UILayout->GetWorld(), EGetWorldErrorMode::LogAndReturnNull))
{
UGUIS_AsyncAction_PushContentToUILayer* Action = NewObject<UGUIS_AsyncAction_PushContentToUILayer>();
Action->WidgetClass = InWidgetClass;
Action->RootLayout = UILayout;
Action->OwningPlayerPtr = PlayerController;
Action->LayerName = InLayerName;
Action->bSuspendInputUntilComplete = bSuspendInputUntilComplete;
Action->RegisterWithGameInstance(World);
return Action;
}
return nullptr;
}
void UGUIS_AsyncAction_PushContentToUILayer::Cancel()
{
Super::Cancel();
if (StreamingHandle.IsValid())
{
StreamingHandle->CancelHandle();
StreamingHandle.Reset();
}
}
void UGUIS_AsyncAction_PushContentToUILayer::Activate()
{
// if (UGUIS_GameUILayout* RootLayout = UGUIS_GameUILayout::GetPrimaryGameLayout(OwningPlayerPtr.Get()))
if (RootLayout.IsValid())
{
TWeakObjectPtr<UGUIS_AsyncAction_PushContentToUILayer> WeakThis = this;
StreamingHandle = RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(LayerName, bSuspendInputUntilComplete, WidgetClass,
[this, WeakThis](EGUIS_AsyncWidgetLayerState State, UCommonActivatableWidget* Widget)
{
if (WeakThis.IsValid())
{
switch (State)
{
case EGUIS_AsyncWidgetLayerState::Initialize:
BeforePush.Broadcast(Widget);
break;
case EGUIS_AsyncWidgetLayerState::AfterPush:
AfterPush.Broadcast(Widget);
SetReadyToDestroy();
break;
case EGUIS_AsyncWidgetLayerState::Canceled:
SetReadyToDestroy();
break;
}
}
SetReadyToDestroy();
});
}
else
{
SetReadyToDestroy();
}
}

View File

@@ -0,0 +1,111 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_AsyncAction_ShowModel.h"
#include "GUIS_GenericUISystemSettings.h"
#include "Engine/GameInstance.h"
#include "UI/GUIS_GameUIFunctionLibrary.h"
#include "UI/GUIS_GameUILayout.h"
#include "UI/GUIS_GameplayTags.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GUIS_AsyncAction_ShowModel)
UGUIS_AsyncAction_ShowModel::UGUIS_AsyncAction_ShowModel(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
// UGUIS_AsyncAction_ShowModel* UGUIS_AsyncAction_ShowModel::ShowModal(UObject* InWorldContextObject, FGameplayTag ModalTag, UGUIS_ModalDefinition* ModalDefinition)
// {
// const UGUIS_GameUIData* UIData = UGUIS_GenericUISystemSettings::GetGameUIData();
// if (!UIData)
// return nullptr;
//
// const TSoftClassPtr<UGUIS_GameModalWidget> SoftModalWidgetClass = UIData->FindWidgetClassForModal(ModalTag);
// if (SoftModalWidgetClass.IsNull())
// return nullptr;
// const TSubclassOf<UGUIS_GameModalWidget> ModalWidgetClass = SoftModalWidgetClass.LoadSynchronous();
// if (ModalWidgetClass == nullptr)
// return nullptr;
//
// UGUIS_AsyncAction_ShowModel* Action = NewObject<UGUIS_AsyncAction_ShowModel>();
// Action->ModalWidgetClass = ModalWidgetClass;
// Action->WorldContextObject = InWorldContextObject;
// Action->ModalDefinition = ModalDefinition;
// Action->RegisterWithGameInstance(InWorldContextObject);
//
// return Action;
// }
UGUIS_AsyncAction_ShowModel* UGUIS_AsyncAction_ShowModel::ShowModal(UObject* InWorldContextObject, TSoftClassPtr<UGUIS_ModalDefinition> ModalDefinition)
{
if (ModalDefinition.IsNull())
{
return nullptr;
}
ModalDefinition.LoadSynchronous();
const UGUIS_ModalDefinition* Modal = ModalDefinition->GetDefaultObject<UGUIS_ModalDefinition>();
if (Modal == nullptr)
return nullptr;
if (Modal->ModalWidget.IsNull())
return nullptr;
const TSubclassOf<UGUIS_GameModalWidget> ModalWidgetClass = Modal->ModalWidget.LoadSynchronous();
if (ModalWidgetClass == nullptr)
return nullptr;
UGUIS_AsyncAction_ShowModel* Action = NewObject<UGUIS_AsyncAction_ShowModel>();
Action->ModalWidgetClass = ModalWidgetClass;
Action->WorldContextObject = InWorldContextObject;
Action->ModalDefinition = Modal;
Action->RegisterWithGameInstance(InWorldContextObject);
return Action;
}
void UGUIS_AsyncAction_ShowModel::Activate()
{
if (WorldContextObject && !TargetPlayerController)
{
if (UUserWidget* UserWidget = Cast<UUserWidget>(WorldContextObject))
{
TargetPlayerController = UserWidget->GetOwningPlayer<APlayerController>();
}
else if (APlayerController* PC = Cast<APlayerController>(WorldContextObject))
{
TargetPlayerController = PC;
}
else if (UWorld* World = WorldContextObject->GetWorld())
{
if (UGameInstance* GameInstance = World->GetGameInstance<UGameInstance>())
{
TargetPlayerController = GameInstance->GetPrimaryPlayerController(false);
}
}
}
if (TargetPlayerController)
{
if (UGUIS_GameUILayout* Layout = UGUIS_GameUIFunctionLibrary::GetGameUILayoutForPlayer(TargetPlayerController))
{
FGUIS_ModalActionResultSignature ResultCallback = FGUIS_ModalActionResultSignature::CreateUObject(this, &UGUIS_AsyncAction_ShowModel::HandleModalAction);
const UGUIS_ModalDefinition* TempDescriptor = ModalDefinition;
Layout->PushWidgetToLayerStack<UGUIS_GameModalWidget>(GUIS_GameUILayerTags::Modal, ModalWidgetClass, [TempDescriptor, ResultCallback](UGUIS_GameModalWidget& ModalInstance)
{
ModalInstance.SetupModal(TempDescriptor, ResultCallback);
});
}
}
// If we couldn't make the confirmation, just handle an unknown result and broadcast nothing
HandleModalAction(GUIS_GameModalActionTags::Unknown);
}
void UGUIS_AsyncAction_ShowModel::HandleModalAction(FGameplayTag ModalActionTag)
{
OnModalAction.Broadcast(ModalActionTag);
SetReadyToDestroy();
}

View File

@@ -0,0 +1,62 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_UIAction.h"
UGUIS_UIAction::UGUIS_UIAction(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
{
}
bool UGUIS_UIAction::IsCompatible(const UObject* Data) const
{
return IsCompatibleInternal(Data);
}
bool UGUIS_UIAction::CanInvokeInternal_Implementation(const UObject* Data, APlayerController* PlayerController) const
{
// 与其他验证不同, 这个默认不通过, Override里修改
return false;
}
bool UGUIS_UIAction::CanInvoke(const UObject* Data, APlayerController* PlayerController) const
{
return CanInvokeInternal(Data, PlayerController);
}
void UGUIS_UIAction::InvokeAction(const UObject* Data, APlayerController* PlayerController) const
{
// if (CanInvoke(Data, User))
{
InvokeActionInternal(Data, PlayerController);
}
}
FText UGUIS_UIAction::GetActionName() const
{
return DisplayName;
}
FName UGUIS_UIAction::GetActionID() const
{
return ActionID;
}
bool UGUIS_UIAction::IsCompatibleInternal_Implementation(const UObject* Data) const
{
return true;
}
void UGUIS_UIAction::InvokeActionInternal_Implementation(const UObject* Data, APlayerController* PlayerController) const
{
}
UWorld* UGUIS_UIAction::GetWorld() const
{
if (UObject* Outer = GetOuter())
{
return Outer->GetWorld();
}
return nullptr;
}

View File

@@ -0,0 +1,36 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_UIActionFactory.h"
#include "Misc/DataValidation.h"
#include "UI/Actions/GUIS_UIAction.h"
TArray<UGUIS_UIAction*> UGUIS_UIActionFactory::FindAvailableUIActionsForData(const UObject* Data) const
{
TArray<UGUIS_UIAction*> Ret;
for (UGUIS_UIAction* Action : PotentialActions)
{
if (Action != nullptr && Action->IsCompatible(Data))
{
Ret.Add(Action);
}
}
return Ret;
}
#if WITH_EDITOR
EDataValidationResult UGUIS_UIActionFactory::IsDataValid(FDataValidationContext& Context) const
{
FText ValidationMessage;
for (int32 i = 0; i < PotentialActions.Num(); i++)
{
if (PotentialActions[0] == nullptr)
{
Context.AddError(FText::FromString(FString::Format(TEXT("Invalid action on index:{0}"), {i})));
return EDataValidationResult::Invalid;
}
}
return Super::IsDataValid(Context);
}
#endif

View File

@@ -0,0 +1,139 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "UI/Actions/GUIS_UIActionWidget.h"
#include "GUIS_LogChannels.h"
#include "Input/CommonUIInputTypes.h"
#include "UI/GUIS_GameplayTags.h"
#include "UI/Actions/GUIS_AsyncAction_ShowModel.h"
#include "UI/Actions/GUIS_UIActionFactory.h"
void UGUIS_UIActionWidget::SetAssociatedData(UObject* Data)
{
if (Data == nullptr)
{
UnregisterActions();
}
AssociatedData = Data;
}
void UGUIS_UIActionWidget::RegisterActions()
{
if (!AssociatedData.IsValid())
{
return;
}
if (!IsValid(ActionFactory))
{
return;
}
TArray<UGUIS_UIAction*> Actions = ActionFactory->FindAvailableUIActionsForData(AssociatedData.Get());
for (const UGUIS_UIAction* Action : Actions)
{
if (Action->CanInvoke(AssociatedData.Get(), GetOwningPlayer()))
{
FBindUIActionArgs BindArgs(Action->GetInputActionData(), Action->GetShouldDisplayInActionBar(),
FSimpleDelegate::CreateLambda([this,Action]()
{
HandleUIAction(Action);
}));
ActionBindings.Add(RegisterUIActionBinding(BindArgs));
}
}
}
void UGUIS_UIActionWidget::RegisterActionsWithFactory(TSoftObjectPtr<UGUIS_UIActionFactory> InActionFactory)
{
if (InActionFactory.IsNull())
{
UE_LOG(LogGUIS, Warning, TEXT("Passed invalid action factory!"))
return;
}
UGUIS_UIActionFactory* Factory = InActionFactory.LoadSynchronous();
if (Factory == nullptr)
{
UE_LOG(LogGUIS, Warning, TEXT("Failed to load action factory!"))
return;
}
ActionFactory = Factory;
RegisterActions();
}
void UGUIS_UIActionWidget::UnregisterActions()
{
for (FUIActionBindingHandle& ActionBinding : ActionBindings)
{
ActionBinding.Unregister();
}
ActionBindings.Empty();
CancelAction();
}
void UGUIS_UIActionWidget::CancelAction()
{
if (ModalTask)
{
ModalTask->OnModalAction.RemoveDynamic(this, &ThisClass::HandleModalAction);
ModalTask->Cancel();
ModalTask = nullptr;
}
CurrentAction = nullptr;
}
#if WITH_EDITOR
const FText UGUIS_UIActionWidget::GetPaletteCategory()
{
return FText::FromString(TEXT("Generic UI"));
}
#endif
void UGUIS_UIActionWidget::HandleUIAction(const UGUIS_UIAction* Action)
{
if (ModalTask && ModalTask->IsActive())
{
return;
}
if (AssociatedData.IsValid())
{
if (Action->GetRequiresConfirmation() && !Action->GetConfirmationModalClass().IsNull())
{
ModalTask = UGUIS_AsyncAction_ShowModel::ShowModal(GetWorld(), Action->GetConfirmationModalClass());
CurrentAction = Action;
ModalTask->OnModalAction.AddDynamic(this, &ThisClass::HandleModalAction);
ModalTask->Activate();
}
else
{
if (Action->CanInvoke(AssociatedData.Get(), GetOwningPlayer()))
{
Action->InvokeAction(AssociatedData.Get(), GetOwningPlayer());
}
}
}
}
void UGUIS_UIActionWidget::HandleModalAction(FGameplayTag ActionTag)
{
if (ActionTag == GUIS_GameModalActionTags::Yes || ActionTag == GUIS_GameModalActionTags::Ok)
{
if (CurrentAction && CurrentAction->CanInvoke(AssociatedData.Get(), GetOwningPlayer()))
{
CurrentAction->InvokeAction(AssociatedData.Get(), GetOwningPlayer());
}
CancelAction();
}
if (ActionTag == GUIS_GameModalActionTags::No)
{
CancelAction();
}
}