Compare commits

..

12 Commits

Author SHA1 Message Date
Jamie Greunbaum
26b506eb0e Merge pull request 'Created the necessary changes for Unreal Engine 5.4' (#1) from update-to-5.4 into main
Reviewed-on: BattyBovine/ComboInput#1
2024-06-28 17:28:10 -04:00
Jamie Greunbaum
67c0126322 Created the necessary changes for Unreal Engine 5.4 2024-06-28 17:26:45 -04:00
Jamie Greunbaum
a9a841911a Fixed a bug that can cause buttons not mapped to combo inputs to block later combo inputs when they aren't part of a multi-press. 2023-10-11 01:20:29 -04:00
Jamie Greunbaum
b77a9bd2ed - Release triggers only fire if one is available in the current node's children.
- Crashes related to PreviousNode occasionally becoming null have been fixed.
2023-10-07 19:41:23 -04:00
Jamie Greunbaum
08b89b9d84 Fixed some grammar issues with the variable names that were annoying me. 2023-10-07 18:58:26 -04:00
Jamie Greunbaum
b7d299f8b1 Display a validation window when validating a graph, and also a message stating that auto-arrange does not currently work. 2023-10-05 13:13:37 -04:00
Jamie Greunbaum
6261a7eddc Added a popup class for graph validation. 2023-10-05 13:13:03 -04:00
Jamie Greunbaum
9dec4a1c91 Disabled verification on save. 2023-10-04 23:32:07 -04:00
Jamie Greunbaum
0feb21fefc Components were reworked to no longer need a separate list of valid inputs to listen for. 2023-10-04 18:52:53 -04:00
Jamie Greunbaum
3d0fedabf6 Combo graph code now compiles and functions properly in shipping builds. 2023-10-04 13:05:53 -04:00
Jamie Greunbaum
c50c9c6bf7 Components now use InitializeComponent instead of BeginPlay. 2023-10-04 13:05:15 -04:00
Jamie Greunbaum
13172b3b60 - ComboManagerComponent accepts a list of actions to unlock.
- Removed more unused functions.
2023-10-03 22:52:48 -04:00
27 changed files with 457 additions and 192 deletions

View File

@ -6,7 +6,7 @@
"Description": "A set of components and classes for capturing Enhanced Input actions for buffering actions and stringing them into complex combos.",
"Category": "Input",
"CreatedBy": "Jamie Greunbaum",
"EngineVersion": "5.3.0",
"EngineVersion": "5.4.0",
"CanContainContent": true,
"IsBetaVersion": true,
"IsExperimentalVersion": true,

View File

@ -26,24 +26,6 @@ UComboActionGraph::UComboActionGraph()
#endif
}
bool UComboActionGraph::CanStartDialogueGraph() const
{
if (this->AllNodes.Num() == 0)
{
return false;
}
for (const UComboActionGraphNode *Itr : this->AllNodes)
{
if (!Itr || !Itr->ValidateNodeRuntime())
{
return false;
}
}
return true;
}
void UComboActionGraph::CreateGraph()
{
#if WITH_EDITOR
@ -69,7 +51,7 @@ void UComboActionGraph::ClearGraph()
for (UComboActionGraphNode *Node : this->AllNodes)
{
Node->ParentNodes.Empty();
Node->ChildrenNodes.Empty();
Node->ChildNodes.Empty();
Node->Edges.Empty();
}
@ -126,9 +108,10 @@ bool UComboActionGraph::ValidateGraph(TArray<FText> &ValidationErrors, bool Rich
EDataValidationResult UComboActionGraph::IsDataValid(TArray<FText> &ValidationErrors)
{
return this->ValidateGraph(ValidationErrors, false)
? EDataValidationResult::Valid
: EDataValidationResult::Invalid;
//return this->ValidateGraph(ValidationErrors, false)
// ? EDataValidationResult::Valid
// : EDataValidationResult::Invalid;
return EDataValidationResult::NotValidated;
}
#endif

View File

@ -19,14 +19,28 @@ UComboManagerComponent::UComboManagerComponent()
this->PrimaryComponentTick.bStartWithTickEnabled = false;
this->PrimaryComponentTick.bTickEvenWhenPaused = false;
this->PrimaryComponentTick.bCanEverTick = false;
this->bWantsInitializeComponent = true;
}
void UComboManagerComponent::BeginPlay()
void UComboManagerComponent::InitializeComponent()
{
Super::BeginPlay();
Super::InitializeComponent();
checkf(this->ComboGraph, TEXT("No combo graph is set for %s in actor %s"), *UComboManagerComponent::StaticClass()->GetName(), *this->GetOwner()->GetName());
this->ActiveNode = this->ComboGraph->StartNode;
this->PreviousNode = this->ActiveNode = this->ComboGraph->StartNode;
}
void UComboManagerComponent::SetComboGraph(const UComboActionGraph *Graph)
{
checkf(Graph, TEXT("Attempting to set a null combo graph."));
this->ComboGraph = Graph;
this->FoundInputsCache.Empty();
this->FindAllUsedInputs();
this->ResetCombo();
}
@ -110,11 +124,40 @@ void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtr<const UComboInputAss
void UComboManagerComponent::ReleaseComboAction(const class UComboInputAsset *Input)
{
if (this->LastComboAction)
if (this->LastComboAction && this->ActiveNode)
{
this->BroadcastDelegates(this->LastComboAction, EComboActionTriggerEvent::Released);
this->LastComboAction = nullptr;
// See if we have a fallback we can release.
const TObjectPtr<const UComboAction> *FallbackAction = this->FallbackActions.Find(Input);
if (FallbackAction && *FallbackAction == this->LastComboAction)
{
this->ResetCombo();
this->BroadcastDelegates(*FallbackAction, EComboActionTriggerEvent::Released);
UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s released"), *(*FallbackAction)->ActionName.ToString());
}
else
{
// Find a node that matches the release action.
const UComboAction *ComboAction = nullptr;
for (const UComboActionGraphNode *Node : this->ActiveNode->ChildNodes)
{
if (const UComboActionGraphNode_ActionNode *ActionNode = StaticCast<const UComboActionGraphNode_ActionNode *>(Node))
{
if (ActionNode->GetTriggerEvent() == EComboActionTriggerEvent::Released)
{
ComboAction = ActionNode->GetComboAction();
break;
}
}
}
if (ComboAction)
{
this->BroadcastDelegates(ComboAction, EComboActionTriggerEvent::Released);
UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s released"), *ComboAction->ActionName.ToString());
}
}
}
this->LastComboAction = nullptr;
}
@ -135,25 +178,73 @@ void UComboManagerComponent::FinishTransition()
void UComboManagerComponent::ResetCombo()
{
this->GetWorld()->GetTimerManager().ClearTimer(this->DEBUG__ResetComboTimer);
this->PreviousNode = this->ActiveNode;
this->ActiveNode = this->ComboGraph->StartNode;
this->PreviousNode = this->ActiveNode = this->ComboGraph->StartNode;
APlayerController *Controller = UGameplayStatics::GetPlayerController(this, 0);
if (UInputBufferComponent *InputComponent = Cast<UInputBufferComponent>(Controller->InputComponent))
{
for (const UComboInputAsset *Unlock : this->FoundInputsCache)
{
InputComponent->UnlockComboInput(Unlock);
}
}
}
TSet<const UComboInputAsset *> &UComboManagerComponent::FindAllUsedInputs()
{
if (this->FoundInputsCache.IsEmpty())
{
// First check the graph for inputs to respond to.
this->FindAllUsedInputs_RecurseGraph(this->ComboGraph->StartNode, this->FoundInputsCache);
// Next check the fallback actions for supplementary inputs to respond to.
TArray<TObjectPtr<const UComboInputAsset>> FallbackInputs;
this->FallbackActions.GetKeys(FallbackInputs);
for (const TObjectPtr<const UComboInputAsset> &FallbackInput : FallbackInputs)
{
this->FoundInputsCache.Add(FallbackInput);
}
// Finally check the offset action for the final input to respond to.
if (this->OffsetMap.ComboInput)
{
this->FoundInputsCache.Add(this->OffsetMap.ComboInput);
}
}
return this->FoundInputsCache;
}
void UComboManagerComponent::FindAllUsedInputs_RecurseGraph(const UComboActionGraphNode *CurrentNode, TSet<const UComboInputAsset *> &FoundInputs)
{
for (const UComboActionGraphNode *NextNode : CurrentNode->ChildNodes)
{
// It should be safe to assume all of our nodes are action nodes. If at some point
// this becomes less reliable, this should be changed. But just for the sake of
// saving a bit of performance on start-up, we're doing this right now.
checkf(NextNode->GetClass() == UComboActionGraphNode_ActionNode::StaticClass(), TEXT("This graph contains non-action nodes. The cast in this function will fail."));
FoundInputs.Add(StaticCast<const UComboActionGraphNode_ActionNode *>(NextNode)->GetComboInput());
this->FindAllUsedInputs_RecurseGraph(NextNode, FoundInputs);
}
}
const UComboActionGraphNode *UComboManagerComponent::FindActiveNodeData(const UComboActionGraphNode *CurrentNode, const UComboInputAsset *Input, const EComboActionTriggerEvent TriggerEvent, const UComboAction *&ComboAction)
{
checkf(CurrentNode, TEXT("Attempting to find an active node from a null node."));
// Find a node that matches both the combo input and the trigger action.
const UComboActionGraphNode *NextNode = nullptr;
for (const UComboActionGraphNode *GraphNode : CurrentNode->ChildrenNodes)
for (const UComboActionGraphNode *GraphNode : CurrentNode->ChildNodes)
{
if (const UComboActionGraphNode_ActionNode *ActionNode = Cast<UComboActionGraphNode_ActionNode>(GraphNode))
{
if (ActionNode->GetComboInput() == Input && ActionNode->GetTriggerEvent() == EComboActionTriggerEvent::Activated)
{
// If we found the right node, only acknowledge it if it's enabled. Otherwise just skip it.
if (ActionNode->bEnabled)
// If we found the right node, only acknowledge it if it's enabled.
const UComboAction *CheckAction = ActionNode->GetComboAction();
if (ActionNode->bEnabled || this->ComboGraph->UnlockedActions.Contains(CheckAction))
{
ComboAction = ActionNode->GetComboAction();
ComboAction = CheckAction;
NextNode = ActionNode;
}
break;

View File

@ -12,9 +12,14 @@
DEFINE_LOG_CATEGORY(LogInputBufferComponent);
void UInputBufferComponent::BeginPlay()
UInputBufferComponent::UInputBufferComponent()
{
Super::BeginPlay();
this->bWantsInitializeComponent = true;
}
void UInputBufferComponent::InitializeComponent()
{
Super::InitializeComponent();
if (APlayerController *PlayerController = UGameplayStatics::GetPlayerController(this, 0))
{
@ -23,14 +28,15 @@ void UInputBufferComponent::BeginPlay()
// Get the player character and try to connect to its combo manager.
this->OnNewComboInput.BindUObject(ComboManager, &UComboManagerComponent::HandleComboInput);
const TSet<const UComboInputAsset *> &ComboInputs = ComboManager->FindAllUsedInputs();
// Get all unique EnhancedInput actions bound to combo input actions.
const UInputBufferGlobalSettings *Settings = GetDefault<UInputBufferGlobalSettings>();
TSet<const UInputAction *> InputActionsToBind;
for (TSoftObjectPtr<const UComboInputAsset> ComboInput : Settings->ComboInputs)
for (const UComboInputAsset *ComboInput : ComboInputs)
{
if (ComboInput.IsValid())
if (ComboInput)
{
this->ComboInputList.Emplace(ComboInput.Get());
this->ComboInputList.Emplace(ComboInput);
for (const UInputAction *InputAction : ComboInput->ActionGroup)
{
InputActionsToBind.Add(InputAction);
@ -38,7 +44,7 @@ void UInputBufferComponent::BeginPlay()
}
else
{
UE_LOG(LogInputBufferComponent, Verbose, TEXT("Invalid combo action found in Combo Actions list in %s"), *Settings->GetClass()->GetName());
UE_LOG(LogInputBufferComponent, Verbose, TEXT("Invalid combo input found"));
}
}
for (const UInputAction *InputAction : InputActionsToBind)
@ -53,20 +59,47 @@ void UInputBufferComponent::BeginPlay()
void UInputBufferComponent::AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action)
{
this->MostRecentActions.Add(Action);
this->MultiPressActions.Add(Action);
this->ExpiringActions.Remove(Action);
// Find any combo input that matches this action, plus buffered actions.
bool bComboInputFound = false;
for (TObjectPtr<const UComboInputAsset> ComboInput : this->ComboInputList)
{
if (ComboInput->MatchesInputActions(this->MostRecentActions))
if (ComboInput->MatchesInputActions(this->MultiPressActions))
{
this->ActivateComboInput(ComboInput.Get());
bComboInputFound = true;
break;
}
}
// If we haven't found any matching inputs, check if anything was somehow unhandled.
// This can happen if, for example, a jump input can combine with an attack input to
// create a combo action, but jump by itself has no action on its own.
if (!bComboInputFound)
{
if (!this->UnhandledActions.IsEmpty())
{
TSet<const UInputAction*> HandledActions = this->MultiPressActions;
for (const UInputAction *UnhandledAction : this->UnhandledActions)
{
HandledActions.Remove(UnhandledAction);
}
for (TObjectPtr<const UComboInputAsset> ComboInput : this->ComboInputList)
{
if (ComboInput->MatchesInputActions(HandledActions))
{
this->ActivateComboInput(ComboInput);
bComboInputFound = true;
break;
}
}
}
this->UnhandledActions.Add(Action);
}
const UInputBufferGlobalSettings *Settings = GetDefault<UInputBufferGlobalSettings>();
this->GetWorld()->GetTimerManager().SetTimer(this->MultiPressTimerHandle, this, &UInputBufferComponent::ClearMultiPresses, Settings->MultiPressTimerLength);
}
@ -74,13 +107,14 @@ void UInputBufferComponent::ClearMultiPresses()
{
#if WITH_EDITOR
TArray<FString> ActionNames;
for (const UInputAction *Action : this->MostRecentActions)
for (const UInputAction *Action : this->MultiPressActions)
{
ActionNames.Add(Action->GetName());
}
UE_LOG(LogInputBufferComponent, Verbose, TEXT("Multi-press buffer cleared (%s)"), *FString::Join(ActionNames, TEXT(" | ")));
#endif
this->MostRecentActions.Empty();
this->MultiPressActions.Empty();
this->UnhandledActions.Empty();
}
void UInputBufferComponent::ActivateComboInput(const UComboInputAsset *ComboInput)

View File

@ -15,11 +15,12 @@ UComboActionGraphNode::UComboActionGraphNode()
{
this->NodeGUID = FGuid::NewGuid();
this->bEnabled = true;
#if WITH_EDITORONLY_DATA
this->CompatibleGraphType = UComboActionGraph::StaticClass();
this->BackgroundColor = FLinearColor::Black;
this->bEnabled = true;
this->bAllowInputNodes = true;
this->bAllowOutputNodes = true;
@ -37,11 +38,11 @@ UComboActionGraphNode::UComboActionGraphNode()
void UComboActionGraphNode::SetNewWorld(UWorld *NewWorld)
{
if (!NewWorld) return;
if (NewWorld == this->OwningWorld) return;
if (NewWorld)
{
this->OwningWorld = NewWorld;
}
}
void UComboActionGraphNode::InitializeNode_Implementation(UWorld *InWorld)
{
@ -103,7 +104,7 @@ bool UComboActionGraphNode::CanCreateConnection(UComboActionGraphNode *Other, en
ErrorMessage = FText::FromString("Invalid Other Node!");
}
if (Other->GetMaxChildNodes() > -1 && Other->ChildrenNodes.Num() >= Other->GetMaxChildNodes())
if (Other->GetMaxChildNodes() > -1 && Other->ChildNodes.Num() >= Other->GetMaxChildNodes())
{
const FString TextReturn =
FString(Other->GetNodeTitle().ToString()).
@ -142,7 +143,7 @@ bool UComboActionGraphNode::ValidateNode(TArray<FText> &ValidationsMessages, con
{
bool bResult = true;
if (this->ParentNodes.Num() == 0 && this->ChildrenNodes.Num() == 0)
if (this->ParentNodes.Num() == 0 && this->ChildNodes.Num() == 0)
{
bResult = false;
@ -186,7 +187,7 @@ void UComboActionGraphNode::OnPasted()
this->NodeGUID = FGuid::NewGuid();
this->ParentNodes.Empty();
this->ChildrenNodes.Empty();
this->ChildNodes.Empty();
this->Edges.Empty();
}

View File

@ -19,7 +19,7 @@ UComboActionGraphNode_ActionNode::UComboActionGraphNode_ActionNode()
this->AllowedInputClasses.Add(UComboActionGraphNode_StartNode::StaticClass());
this->AllowedInputClasses.Add(UComboActionGraphNode_ActionNode::StaticClass());
this->MaxChildrenNodes = -1;
this->MaxChildNodes = -1;
}
void UComboActionGraphNode_ActionNode::PreProcessNode(const TScriptInterface<IComboActionGraphManagerInterface> &Manager)

View File

@ -45,7 +45,7 @@ bool UComboActionGraphNode_ActionNodeBase::ValidateNodeRuntime_Implementation()
return false;
}
if (this->MaxChildrenNodes > -1 && this->ChildrenNodes.Num() > this->MaxChildrenNodes)
if (this->MaxChildNodes > -1 && this->ChildNodes.Num() > this->MaxChildNodes)
{
return false;
}
@ -113,7 +113,7 @@ bool UComboActionGraphNode_ActionNodeBase::ValidateNode(TArray<FText> &Validatio
ValidationsMessages.Add(FText::FromString(RichFormat ? RichTextReturn : TextReturn));
}
if (this->MaxChildrenNodes > -1 && this->ChildrenNodes.Num() > this->MaxChildrenNodes)
if (this->MaxChildNodes > -1 && this->ChildNodes.Num() > this->MaxChildNodes)
{
const FString RichTextReturn = FString("* ")
.Append("<RichTextBlock.Bold>")
@ -121,12 +121,12 @@ bool UComboActionGraphNode_ActionNodeBase::ValidateNode(TArray<FText> &Validatio
.Append("</>")
.Append(": Has more than ")
.Append("<RichTextBlock.Bold>")
.Append(FString::FromInt(this->MaxChildrenNodes))
.Append(FString::FromInt(this->MaxChildNodes))
.Append("</>")
.Append(" child nodes!");
const FString TextReturn = FString(this->NodeTitle.ToString())
.Append(": Has more than ").Append(FString::FromInt(this->MaxChildrenNodes)).Append(" child nodes!");
.Append(": Has more than ").Append(FString::FromInt(this->MaxChildNodes)).Append(" child nodes!");
ValidationsMessages.Add(FText::FromString(RichFormat ? RichTextReturn : TextReturn));
}
@ -151,7 +151,8 @@ void UComboActionGraphNode_ActionNodeBase::PostEditChangeProperty(FPropertyChang
}
}
FLinearColor UComboActionGraphNode_ActionNodeBase::GetBackgroundColor() const
#if WITH_EDITOR
FLinearColor UComboActionGraphNode_ActionNodeBase::GetBackgroundColour() const
{
if (this->ComboAction && this->ComboAction->NodeColor != FLinearColor())
{
@ -161,8 +162,9 @@ FLinearColor UComboActionGraphNode_ActionNodeBase::GetBackgroundColor() const
{
return this->ComboInput->NodeColor;
}
return Super::GetBackgroundColor();
return Super::GetBackgroundColour();
}
#endif
FText UComboActionGraphNode_ActionNodeBase::GetDescription_Implementation() const
{

View File

@ -23,7 +23,7 @@ UComboActionGraphNode_StartNode::UComboActionGraphNode_StartNode()
this->NodeTooltipText = LOCTEXT("ComboActionGraphNode_CompleteTooltip", "* This Node will be added to the graph automatically.\n* This Node cannot be created manually.\n* This Node cannot be deleted.\n* Does not implement any logic.");
#endif
this->MaxChildrenNodes = -1;
this->MaxChildNodes = -1;
}
#if WITH_EDITOR
@ -37,7 +37,7 @@ bool UComboActionGraphNode_StartNode::ValidateNode(TArray<FText>& ValidationsMes
{
bool bResult = Super::ValidateNode(ValidationsMessages, RichFormat);
if (ChildrenNodes.Num() == 0)
if (this->ChildNodes.Num() == 0)
{
bResult = false;

View File

@ -43,17 +43,17 @@ public:
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
class UComboActionGraphNode *StartNode = nullptr;
/**
* The class of the dialogue node represented by this instance.
* The class of the action node represented by this instance.
*/
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
TSubclassOf<UComboActionGraphNode> NodeType;
/**
* The class of the dialogue edge represented by this instance.
* The class of the action edge represented by this instance.
*/
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
TSubclassOf<UComboActionGraphEdge> EdgeType;
/**
* An array of root nodes in the dialogue graph. These are the nodes that do not have any incoming connections.
* An array of root nodes in the action graph. These are the nodes that do not have any incoming connections.
*/
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
TArray<UComboActionGraphNode*> RootNodes;
@ -62,6 +62,13 @@ public:
*/
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
TArray<UComboActionGraphNode*> AllNodes;
/**
* Set containing actions to be unlocked if their associated nodes are currently locked.
*/
UPROPERTY(BlueprintReadWrite, Category="Combo Input|Action")
TSet<const class UComboAction*> UnlockedActions;
// Flag indicating whether an edge is enabled
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action")
bool bEdgeEnabled;
@ -88,18 +95,12 @@ public:
UFUNCTION(BlueprintCallable, Category="Combo Input|Action")
TArray<UComboActionGraphNode*> GetRootNodes() const { return this->RootNodes; }
/**
* Returns the root nodes of the dialogue graph.
* Returns the first node in the graph.
*
* @return An array of all root nodes.
* @return The start node of this graph.
*/
UFUNCTION(BlueprintCallable, Category="Combo Input|Action")
UComboActionGraphNode *GetStartNode() const { return this->StartNode; }
/**
* Determines whether the dialogue graph can be started.
*
* @return true if the graph can be started, false otherwise.
*/
bool CanStartDialogueGraph() const;
public:
void CreateGraph();
@ -115,7 +116,7 @@ public:
public:
UPROPERTY()
class UEdGraph *EdGraph;
TObjectPtr<class UEdGraph> EdGraph;
UPROPERTY(BlueprintReadOnly, Category="Combo Input|Action|Editor")
bool bCanRenameNode;

View File

@ -42,8 +42,10 @@ class COMBOINPUT_API UComboManagerComponent : public UActorComponent
public:
UComboManagerComponent();
virtual void InitializeComponent() override;
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable)
void SetComboGraph(const class UComboActionGraph *Graph);
UFUNCTION(BlueprintCallable)
void HandleComboInput(const class UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent);
@ -56,6 +58,9 @@ public:
this->ComboActionEventBindings.Emplace(Key.Key, MoveTemp(Delegate));
}
// Recursively search the graph for all inputs used by the graph.
TSet<const UComboInputAsset *> &FindAllUsedInputs();
protected:
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
TObjectPtr<const class UComboActionGraph> ComboGraph;
@ -87,6 +92,7 @@ private:
void ResetCombo();
const UComboActionGraphNode *FindActiveNodeData(const UComboActionGraphNode *CurrentNode, const UComboInputAsset *Input, const EComboActionTriggerEvent TriggerEvent, const UComboAction *&ComboAction);
void FindAllUsedInputs_RecurseGraph(const UComboActionGraphNode *CurrentNode, TSet<const UComboInputAsset *> &FoundInputs);
void BroadcastDelegates(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent);
@ -101,6 +107,9 @@ private:
TObjectPtr<class UInputBufferComponent> AttachedInputBuffer;
// Cache of combo inputs found in the current graph.
TSet<const UComboInputAsset *> FoundInputsCache;
FTimerHandle FinishTransitionTimer;
FTimerHandle DEBUG__ResetComboTimer;

View File

@ -21,7 +21,8 @@ class COMBOINPUT_API UInputBufferComponent : public UEnhancedInputComponent
GENERATED_BODY()
public:
virtual void BeginPlay() override;
UInputBufferComponent();
virtual void InitializeComponent() override;
UFUNCTION(BlueprintCallable)
void LockComboInput(const class UComboInputAsset *Input);
@ -52,7 +53,8 @@ private:
// A local backup of the global setting containing the list of combo inputs to respond to.
TSet<TObjectPtr<const UComboInputAsset>> ComboInputList;
TSet<const class UInputAction*> MostRecentActions;
TSet<const class UInputAction*> MultiPressActions;
TSet<const class UInputAction*> UnhandledActions;
TSet<const class UInputAction*> ExpiringActions;
FNewComboInput OnNewComboInput;

View File

@ -16,12 +16,6 @@ class COMBOINPUT_API UInputBufferGlobalSettings : public UDeveloperSettingsBacke
GENERATED_BODY()
public:
// List of possible combo inputs that can be taken. A combo input is selected from this list
// either if an action is made while the current combo is inactive, or when the previous
// action expires.
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly)
TSet<TSoftObjectPtr<const class UComboInputAsset>> ComboInputs;
// Length of time after releasing an input to keep the associated combo action buffered before clearing it.
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="0.0", UIMax="0.5", ClampMin="0.0", ClampMax="0.5"))
float InputReleaseExpirationTimerLength = 0.15f;

View File

@ -38,7 +38,7 @@ public:
* The array of child nodes of the current dialogue node.
*/
UPROPERTY(BlueprintReadOnly, Category = "Private")
TArray<class UComboActionGraphNode*> ChildrenNodes;
TArray<class UComboActionGraphNode *> ChildNodes;
/**
* Map of edges connecting this Node in the Combo Action graph.
* The key of the map is the source node, and the value is the edge connecting it to its target node.
@ -94,7 +94,7 @@ public:
* Can be used to enforce a maximum number of connections for certain types of nodes.
*/
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category="Combo Input|Action")
int32 MaxChildrenNodes = -1;
int32 MaxChildNodes = -1;
/**
* The array of allowed input classes for this Dialogue Node.
@ -126,10 +126,10 @@ public:
* Returns how many Children Nodes this Node allows to have.
* -1 means no limits.
*
* @return MaxChildrenNodes
* @return Max child nodes
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
int32 GetMaxChildNodes() const { return MaxChildrenNodes; }
int32 GetMaxChildNodes() const { return this->MaxChildNodes; }
/**
@ -164,23 +164,6 @@ public:
class UComboActionGraph *GetGraph() const { return this->Graph; }
/**
* Gets children Nodes this one has,
* Might be empty
*
* @return Amount of children Nodes
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
FORCEINLINE TArray<UComboActionGraphNode*> GetChildrenNodes() const { return ChildrenNodes; }
/**
* Gets how many parent Nodes point to this one
* Might be empty
*
* @return Amount of how parent Nodes
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
FORCEINLINE TArray<UComboActionGraphNode*> GetParentNodes() const { return ParentNodes; }
/**
* Serves purpose of validating Node before Dialogue gets Started.
* Any broken Node results in non-starting Dialogue to avoid crashes.
@ -325,11 +308,11 @@ public:
virtual FString GetNodeDocumentationLink_Implementation() const;
/**
* Returns the Background Colour for this graph node.
* Returns the background colour for this graph node.
*
* @return The Background Colour for this node.
* @return The background colour for this node.
*/
virtual FLinearColor GetBackgroundColor() const { return this->BackgroundColor; }
virtual FLinearColor GetBackgroundColour() const { return this->BackgroundColor; }
FText GetInternalName() const { return this->NodeTypeName; }
// Allows setting up the Node Title

View File

@ -39,9 +39,9 @@ public:
virtual bool ValidateNodeRuntime_Implementation() const override;
virtual FLinearColor GetBackgroundColor() const override;
public:
#if WITH_EDITOR
virtual FLinearColor GetBackgroundColour() const override;
#endif
#if WITH_EDITORONLY_DATA

View File

@ -95,7 +95,7 @@ public:
virtual UEdGraphNode *PerformAction(class UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode = true) override;
virtual void AddReferencedObjects(FReferenceCollector &Collector) override;
class UEdComboActionGraphNode *NodeTemplate;
TObjectPtr<class UEdComboActionGraphNode> NodeTemplate;
};
USTRUCT()
@ -114,7 +114,7 @@ public:
virtual UEdGraphNode *PerformAction(class UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode = true) override;
virtual void AddReferencedObjects(FReferenceCollector &Collector) override;
class UEdComboActionGraphEdge *NodeTemplate;
TObjectPtr<class UEdComboActionGraphEdge> NodeTemplate;
};

View File

@ -234,7 +234,6 @@ FComboInputSlateStyle::FComboInputSlateStyle() : FSlateStyleSet("ComboInputEdito
this->Set("MDSStyleSet.Node.Icon.small", new IMAGE_BRUSH(TEXT("DialogueNodeIcon"), Icon12));
this->Set("MDSStyleSet.Icon.Close", new IMAGE_BRUSH(TEXT("CloseIcon"), Icon12));
this->Set("MDSStyleSet.Icon.SupportDiscord", new IMAGE_BRUSH(TEXT("DiscordIcon"), Icon12));
this->Set("MDSStyleSet.Icon.HeartIcon", new IMAGE_BRUSH(TEXT("HeartIcon"), Icon12));
this->Set("MDSStyleSet.Icon.UBIcon", new IMAGE_BRUSH(TEXT("UnrealBucketIcon"), Icon12));
this->Set("MDSStyleSet.Icon.MoneyIcon", new IMAGE_BRUSH(TEXT("MoneyIcon"), Icon12));

View File

@ -2,28 +2,26 @@
#include "AssetEditor_ComboActionGraph.h"
#include "GraphEditorActions.h"
#include "Framework/Commands/GenericCommands.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "HAL/PlatformApplicationMisc.h"
#include "EdGraphUtilities.h"
#include "ComboInputEditor.h"
#include "ComboActionGraph.h"
#include "Nodes/ComboActionGraphNode.h"
#include "ComboActionGraphSchema.h"
#include "ComboInputEditor.h"
#include "EdGraphUtilities.h"
#include "GraphEditorActions.h"
#include "Ed/EdComboActionGraph.h"
#include "Ed/EdComboActionGraphEdge.h"
#include "Ed/EdComboActionGraphNode.h"
#include "Ed/FAssetEditorToolbarComboActionGraph.h"
#include "Ed/FComboActionGraphEditorCommands.h"
#include "ComboActionGraphSchema.h"
//#include "Helpers/ComboActionGraphHelpers.h"
//#include "Helpers/ComboActionSystemEditorBFC.h"
#include "Framework/Commands/GenericCommands.h"
#include "HAL/PlatformApplicationMisc.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Layout/AssetEditorTabs.h"
#include "Layout/ComboActionGraphLayoutStrategy.h"
#include "Layout/ComboActionForceDirectedSolveLayoutStrategy.h"
#include "Layout/ComboActionTreeSolveLayoutStrategy.h"
//#include "Popups/MDSPopup_GraphValidation.h"
#include "Nodes/ComboActionGraphNode.h"
#include "Popups/ComboInputPopup_GraphValidation.h"
#include "Search/ComboActionSearchUtils.h"
#include "Search/SComboActionSearch.h"
#include "Settings/ComboActionGraphEditorSettings.h"
@ -742,35 +740,41 @@ bool FAssetEditor_ComboActionGraph::CanPasteNodes() const
void FAssetEditor_ComboActionGraph::AutoArrange()
{
UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
check(EdGraph != nullptr);
// For now, auto-arrange is broken. Just in case it can be activated by accident,
// show a popup saying as much if an auto-arrange is attempted.
TArray<FText> ValidationMessages;
ValidationMessages.Add(LOCTEXT("AssetEditor_ComboActionGraph_AutoArrangeDisabled", "Auto-arrange is currently broken."));
ComboInputPopup_GraphValidation::Open(ValidationMessages);
const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorAutoArrange", "Combo Action Graph Editor: Auto Arrange all Nodes"));
//UEdComboActionGraph *EdGraph = Cast<UEdComboActionGraph>(this->EditingGraph->EdGraph);
//check(EdGraph != nullptr);
EdGraph->Modify(true);
//const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorAutoArrange", "Combo Action Graph Editor: Auto Arrange all Nodes"));
UComboActionGraphLayoutStrategy *LayoutStrategy = nullptr;
switch (this->ComboActionGraphEditorSettings->GetAutoLayoutStrategy())
{
case EComboActionAutoLayoutStrategyType::Tree:
LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionTreeSolveLayoutStrategy::StaticClass());
break;
case EComboActionAutoLayoutStrategyType::ForceDirected:
LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionForceDirectedSolveLayoutStrategy::StaticClass());
break;
default:
break;
}
//EdGraph->Modify(true);
if (LayoutStrategy != nullptr)
{
LayoutStrategy->Layout(EdGraph);
LayoutStrategy->ConditionalBeginDestroy();
}
else
{
UE_LOG(LogAssetEditorComboActionGraph, Error, TEXT("[AutoArrange] LayoutStrategy is null."));
}
//UComboActionGraphLayoutStrategy *LayoutStrategy = nullptr;
//switch (this->ComboActionGraphEditorSettings->GetAutoLayoutStrategy())
//{
// case EComboActionAutoLayoutStrategyType::Tree:
// LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionTreeSolveLayoutStrategy::StaticClass());
// break;
// case EComboActionAutoLayoutStrategyType::ForceDirected:
// LayoutStrategy = NewObject<UComboActionGraphLayoutStrategy>(EdGraph, UComboActionForceDirectedSolveLayoutStrategy::StaticClass());
// break;
// default:
// break;
//}
//if (LayoutStrategy != nullptr)
//{
// LayoutStrategy->Layout(EdGraph);
// LayoutStrategy->ConditionalBeginDestroy();
//}
//else
//{
// UE_LOG(LogAssetEditorComboActionGraph, Error, TEXT("[AutoArrange] LayoutStrategy is null."));
//}
}
bool FAssetEditor_ComboActionGraph::CanAutoArrange() const
@ -798,12 +802,12 @@ void FAssetEditor_ComboActionGraph::ValidateGraph()
TArray<FText> ValidationMessages;
if (ComboActionGraph->ValidateGraph(ValidationMessages, true) == false)
{
//ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
this->ValidationWindow = ComboInputPopup_GraphValidation::Open(ValidationMessages);
}
else
{
ValidationMessages.Empty();
//ValidationWindow = MDSPopup_GraphValidation::Open(ValidationMessages);
this->ValidationWindow = ComboInputPopup_GraphValidation::Open(ValidationMessages);
}
}

View File

@ -146,7 +146,7 @@ private:
class UComboActionGraphEditorSettings *ComboActionGraphEditorSettings;
class UComboActionGraph *EditingGraph;
TObjectPtr<class UComboActionGraph> EditingGraph;
//Toolbar
TSharedPtr<FAssetEditorToolbarComboActionGraph> ToolbarBuilder;

View File

@ -63,7 +63,7 @@ void UEdComboActionGraph::RebuildComboActionGraph()
if (ChildNode != nullptr)
{
ComboActionGraphNode->ChildrenNodes.Add(ChildNode);
ComboActionGraphNode->ChildNodes.Add(ChildNode);
ChildNode->ParentNodes.Add(ComboActionGraphNode);
}
@ -168,7 +168,7 @@ void UEdComboActionGraph::Clear()
{
UComboActionGraphNode *MounteaDialogueGraphNode = EdNode->ComboActionGraphNode;
MounteaDialogueGraphNode->ParentNodes.Reset();
MounteaDialogueGraphNode->ChildrenNodes.Reset();
MounteaDialogueGraphNode->ChildNodes.Reset();
MounteaDialogueGraphNode->Edges.Reset();
}
}
@ -194,12 +194,12 @@ void UEdComboActionGraph::SortNodes(UComboActionGraphNode *RootNode)
return EdNode_LNode->NodePosX < EdNode_RNode->NodePosX;
};
Node->ChildrenNodes.Sort(Comp);
Node->ChildNodes.Sort(Comp);
Node->ParentNodes.Sort(Comp);
for (int j = 0; j < Node->ChildrenNodes.Num(); ++j)
for (int j = 0; j < Node->ChildNodes.Num(); ++j)
{
NextLevelNodes.Add(Node->ChildrenNodes[j]);
NextLevelNodes.Add(Node->ChildNodes[j]);
}
}

View File

@ -82,11 +82,6 @@ void UEdComboActionGraphNode::AutowireNewNode(UEdGraphPin* FromPin)
}
}
FLinearColor UEdComboActionGraphNode::GetBackgroundColor() const
{
return this->ComboActionGraphNode ? this->ComboActionGraphNode->GetBackgroundColor() : FLinearColor::Black;
}
UEdGraphPin* UEdComboActionGraphNode::GetInputPin() const
{
return Pins[0];
@ -151,12 +146,19 @@ FText UEdComboActionGraphNode::GetTooltipText() const
return NSLOCTEXT("UEdComboActionGraphNode", "DefaultToolTip", "Combo Action Node");
}
#if WITH_EDITOR
FLinearColor UEdComboActionGraphNode::GetBackgroundColor() const
{
return this->ComboActionGraphNode ? this->ComboActionGraphNode->GetBackgroundColour() : FLinearColor::Black;
}
FSlateIcon UEdComboActionGraphNode::GetIconAndTint(FLinearColor& OutColor) const
{
static const FSlateIcon Icon = FSlateIcon(FComboInputSlateStyle::GetAppStyleSetName(), "MDSStyleSet.Node.Icon.small");
OutColor = this->ComboActionGraphNode->GetBackgroundColor();
OutColor = this->ComboActionGraphNode->GetBackgroundColour();
return Icon;
}
#endif
void UEdComboActionGraphNode::PostEditUndo()
{

View File

@ -31,7 +31,6 @@ public:
virtual void PrepareForCopying() override;
virtual void AutowireNewNode(UEdGraphPin *FromPin) override;
virtual FLinearColor GetBackgroundColor() const;
virtual UEdGraphPin *GetInputPin() const;
virtual UEdGraphPin *GetOutputPin() const;
@ -41,11 +40,13 @@ public:
virtual bool CanUserPasteNodes() const;
virtual FText GetTooltipText() const override;
virtual FSlateIcon GetIconAndTint(FLinearColor &OutColor) const override;
#if WITH_EDITOR
virtual void PostEditUndo() override;
virtual void PostEditChangeProperty(FPropertyChangedEvent &PropertyChangedEvent) override;
virtual FLinearColor GetBackgroundColor() const;
virtual FSlateIcon GetIconAndTint(FLinearColor &OutColor) const override;
#endif
UPROPERTY(VisibleAnywhere, Instanced, Category="Combo Action Graph")

View File

@ -113,11 +113,11 @@ FBox2D UComboActionForceDirectedSolveLayoutStrategy::LayoutOneTree(UComboActionG
UEdComboActionGraphNode *EdNode_ParentNode = this->EdGraph->NodeMap[Node];
for (int32 j = 0; j < Node->ChildrenNodes.Num(); j++)
for (int32 j = 0; j < Node->ChildNodes.Num(); j++)
{
NextLevelNodes.Add(Node->ChildrenNodes[j]);
NextLevelNodes.Add(Node->ChildNodes[j]);
UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Node->ChildrenNodes[j]];
UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Node->ChildNodes[j]];
Diff.X = EdNode_ChildNode->NodePosX - EdNode_ParentNode->NodePosY;
Diff.Y = EdNode_ChildNode->NodePosY - EdNode_ParentNode->NodePosY;

View File

@ -52,7 +52,7 @@ FBox2D UComboActionGraphLayoutStrategy::GetActualBounds(UComboActionGraphNode *R
Rtn += GetNodeBound(this->EdGraph->NodeMap[Node]);
for (UComboActionGraphNode *ChildNode : Node->ChildrenNodes)
for (UComboActionGraphNode *ChildNode : Node->ChildNodes)
{
NextLevelNodes.Add(ChildNode);
}
@ -82,7 +82,7 @@ void UComboActionGraphLayoutStrategy::RandomLayoutOneTree(UComboActionGraphNode
EdNode_Node->NodePosX = UKismetMathLibrary::RandomFloatInRange(Bound.Min.X, Bound.Max.X);
EdNode_Node->NodePosY = UKismetMathLibrary::RandomFloatInRange(Bound.Min.Y, Bound.Max.Y);
for (UComboActionGraphNode *ChildNode : Node->ChildrenNodes)
for (UComboActionGraphNode *ChildNode : Node->ChildNodes)
{
NextLevelNodes.Add(ChildNode);
}

View File

@ -66,13 +66,13 @@ void UComboActionTreeSolveLayoutStrategy::InitPass(UComboActionGraphNode *RootNo
UEdComboActionGraphNode *EdNode_RootNode = this->EdGraph->NodeMap[RootNode];
FVector2D ChildAnchor(FVector2D(0.0f, this->GetNodeHeight(EdNode_RootNode) + this->OptimalDistance + Anchor.Y));
for (int32 i = 0; i < RootNode->ChildrenNodes.Num(); i++)
for (int32 i = 0; i < RootNode->ChildNodes.Num(); i++)
{
UComboActionGraphNode *Child = RootNode->ChildrenNodes[i];
UComboActionGraphNode *Child = RootNode->ChildNodes[i];
UEdComboActionGraphNode *EdNode_ChildNode = this->EdGraph->NodeMap[Child];
if (i > 0)
{
UComboActionGraphNode *PreChild = RootNode->ChildrenNodes[i - 1];
UComboActionGraphNode *PreChild = RootNode->ChildNodes[i - 1];
UEdComboActionGraphNode *EdNode_PreChildNode = this->EdGraph->NodeMap[PreChild];
ChildAnchor.X += this->OptimalDistance + this->GetNodeWidth(EdNode_PreChildNode) / 2;
}
@ -83,7 +83,7 @@ void UComboActionTreeSolveLayoutStrategy::InitPass(UComboActionGraphNode *RootNo
float NodeWidth = this->GetNodeWidth(EdNode_RootNode);
EdNode_RootNode->NodePosY = Anchor.Y;
if (RootNode->ChildrenNodes.Num() == 0)
if (RootNode->ChildNodes.Num() == 0)
{
EdNode_RootNode->NodePosX = Anchor.X - NodeWidth / 2;
}
@ -96,9 +96,9 @@ void UComboActionTreeSolveLayoutStrategy::InitPass(UComboActionGraphNode *RootNo
bool UComboActionTreeSolveLayoutStrategy::ResolveConflictPass(UComboActionGraphNode *Node)
{
bool HasConflict = false;
for (int32 i = 0; i < Node->ChildrenNodes.Num(); ++i)
for (int32 i = 0; i < Node->ChildNodes.Num(); ++i)
{
UComboActionGraphNode *Child = Node->ChildrenNodes[i];
UComboActionGraphNode *Child = Node->ChildNodes[i];
if (this->ResolveConflictPass(Child))
{
HasConflict = true;
@ -107,7 +107,7 @@ bool UComboActionTreeSolveLayoutStrategy::ResolveConflictPass(UComboActionGraphN
for (const UComboActionGraphNode *ParentNode : Node->ParentNodes)
{
for (UComboActionGraphNode *LeftSibling : ParentNode->ChildrenNodes)
for (UComboActionGraphNode *LeftSibling : ParentNode->ChildNodes)
{
if (LeftSibling == Node)
{
@ -183,7 +183,7 @@ void UComboActionTreeSolveLayoutStrategy::GetLeftContour(UComboActionGraphNode *
Contour[Level] = EdNode_Node;
}
for (UComboActionGraphNode *Child : RootNode->ChildrenNodes)
for (UComboActionGraphNode *Child : RootNode->ChildNodes)
{
this->GetLeftContour(Child, Level + 1, Contour);
}
@ -201,7 +201,7 @@ void UComboActionTreeSolveLayoutStrategy::GetRightContour(UComboActionGraphNode
Contour[Level] = EdNode_Node;
}
for (UComboActionGraphNode *Child : RootNode->ChildrenNodes)
for (UComboActionGraphNode *Child : RootNode->ChildNodes)
{
this->GetRightContour(Child, Level + 1, Contour);
}
@ -213,7 +213,7 @@ void UComboActionTreeSolveLayoutStrategy::ShiftSubTree(UComboActionGraphNode *Ro
EdNode_Node->NodePosX += Offset.X;
EdNode_Node->NodePosY += Offset.Y;
for (UComboActionGraphNode *Child : RootNode->ChildrenNodes)
for (UComboActionGraphNode *Child : RootNode->ChildNodes)
{
if (Child->ParentNodes[0] == RootNode)
{
@ -225,17 +225,17 @@ void UComboActionTreeSolveLayoutStrategy::ShiftSubTree(UComboActionGraphNode *Ro
void UComboActionTreeSolveLayoutStrategy::UpdateParentNodePosition(UComboActionGraphNode *RootNode)
{
UEdComboActionGraphNode *EdNode_ParentNode = this->EdGraph->NodeMap[RootNode];
if (RootNode->ChildrenNodes.Num() % 2 == 0)
if (RootNode->ChildNodes.Num() % 2 == 0)
{
UEdComboActionGraphNode *FirstChild = this->EdGraph->NodeMap[RootNode->ChildrenNodes[0]];
UEdComboActionGraphNode *LastChild = this->EdGraph->NodeMap[RootNode->ChildrenNodes.Last()];
UEdComboActionGraphNode *FirstChild = this->EdGraph->NodeMap[RootNode->ChildNodes[0]];
UEdComboActionGraphNode *LastChild = this->EdGraph->NodeMap[RootNode->ChildNodes.Last()];
float LeftBound = FirstChild->NodePosX;
float RightBound = LastChild->NodePosX + this->GetNodeWidth(LastChild);
EdNode_ParentNode->NodePosX = (LeftBound + RightBound) / 2 - this->GetNodeWidth(EdNode_ParentNode) / 2;
}
else
{
UEdComboActionGraphNode *MidChild = this->EdGraph->NodeMap[RootNode->ChildrenNodes[RootNode->ChildrenNodes.Num() / 2]];
UEdComboActionGraphNode *MidChild = this->EdGraph->NodeMap[RootNode->ChildNodes[RootNode->ChildNodes.Num() / 2]];
EdNode_ParentNode->NodePosX = MidChild->NodePosX + this->GetNodeWidth(MidChild) / 2 - this->GetNodeWidth(EdNode_ParentNode) / 2;
}
}

View File

@ -0,0 +1,147 @@
#include "Popups/ComboInputPopup_GraphValidation.h"
#include "ComboInputEditor.h"
#include "Helpers/ComboActionGraphColors.h"
#include "Interfaces/IPluginManager.h"
#include "Settings/ComboActionGraphEditorSettings.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Text/SRichTextBlock.h"
#define LOCTEXT_NAMESPACE "ComboInputPopup_GraphValidation"
void ComboInputPopup_GraphValidation::OnBrowserLinkClicked(const FSlateHyperlinkRun::FMetadata &Metadata)
{
const FString *URL = Metadata.Find(TEXT("href"));
if (URL)
{
FPlatformProcess::LaunchURL(**URL, nullptr, nullptr);
}
}
TSharedPtr<SWindow> ComboInputPopup_GraphValidation::Open(const TArray<FText> ValidationMessages)
{
if (!FSlateApplication::Get().CanDisplayWindows())
{
return nullptr;
}
const FComboInputEditorModule &Module = FModuleManager::GetModuleChecked<FComboInputEditorModule>("ComboInputEditor");
const TSharedPtr<FSlateStyleSet> &EditorStyle = Module.Get().GetComboInputEditorStyleSet();
const TSharedRef<SBorder> WindowContent = SNew(SBorder)
.BorderImage(EditorStyle->GetBrush("MDSStyleSet.Node.TextSoftEdges"))
.BorderBackgroundColor
(
ComboActionGraphColors::ValidationGraph::DarkTheme
)
.Padding(FMargin(8.0f, 8.0f));
TSharedPtr<SWindow> Window = SNew(SWindow)
.AutoCenter(EAutoCenter::PreferredWorkArea)
.SupportsMaximize(false)
.SupportsMinimize(false)
.SizingRule(ESizingRule::FixedSize)
.ClientSize(FVector2D(650, 750))
.Title(FText::FromString("Combo Action Graph Validation"))
.IsTopmostWindow(true)
[
WindowContent
];
const FSlateFontInfo Heading1Font = FCoreStyle::GetDefaultFontStyle("Bold", 24);
const FSlateFontInfo Heading2Font = FCoreStyle::GetDefaultFontStyle("Bold", 18);
const FSlateFontInfo NormalFont = FCoreStyle::GetDefaultFontStyle("Regular", 12);
const TSharedRef<SScrollBox> ListOfMessages = SNew(SScrollBox);
for (auto Itr : ValidationMessages)
{
ListOfMessages->AddSlot()
[
SNew(SBox)
.Padding(FMargin(0.f, 3.5f, 0.f, 3.5f))
[
SNew(SRichTextBlock)
.Text(Itr)
.TextStyle(FAppStyle::Get(), "NormalText")
.DecoratorStyleSet(&FAppStyle::Get())
.AutoWrapText(true)
]
];
}
if (ValidationMessages.Num() == 0)
{
ListOfMessages->AddSlot()
[
SNew(STextBlock)
.Text(FText::FromString("There are no issues with your graph. You can close this window."))
.TextStyle(FAppStyle::Get(), "NormalText")
.AutoWrapText(true)
];
}
const TSharedRef<SVerticalBox> InnerContent =
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0)
.Padding(10)
[
SNew(SBorder)
.Padding(10)
.BorderImage(FAppStyle::GetBrush("ToolPanel.DarkGroupBorder"))
[
ListOfMessages
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(10)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.0f)
[
SNew(SButton)
.HAlign(HAlign_Center)
.OnClicked_Lambda([Window]()
{
Window->RequestDestroyWindow();
return FReply::Handled();
})
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock)
.Text(FText::FromString("Close window"))
]
+ SHorizontalBox::Slot()
[
SNew(SSpacer)
.Size(FVector2D(5, 0))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SImage)
.ColorAndOpacity(FLinearColor::Red)
.Image(EditorStyle->GetBrush("MDSStyleSet.Icon.Close"))
]
]
]
];
const int32 A = InnerContent.Get().GetAllChildren()->Num();
WindowContent->SetContent(InnerContent);
Window = FSlateApplication::Get().AddWindow(Window.ToSharedRef());
return Window;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,13 @@
#pragma once
#include "Framework/Text/SlateHyperlinkRun.h"
class SScrollBox;
class ComboInputPopup_GraphValidation
{
public:
static TSharedPtr<SWindow> Open(const TArray<FText> ValidationMessages);
static void OnBrowserLinkClicked(const FSlateHyperlinkRun::FMetadata &Metadata);
};

View File

@ -95,7 +95,6 @@ private:
TArray<TSharedPtr<IAssetTypeActions>> CreatedAssetTypeActions;
TSharedPtr<class FSlateStyleSet> ComboInputEditorStyleSet;
TSharedPtr<class FSlateStyleSet> ComboActionGraphEditorStyleSet;
TSharedPtr<struct FGraphPanelNodeFactory> ComboActionGraphPanelNodeFactory;
TSharedPtr<class FAssetTypeActions_ComboActionGraph> ComboActionGraphAssetActions;
};