Rich text input decorator can now reference enhanced input actions

This commit is contained in:
Steve Streeting 2023-03-07 18:10:25 +00:00
parent 0aea6933dc
commit e9fd6cb254
9 changed files with 156 additions and 14 deletions

View File

@ -1,7 +1,9 @@
#include "StevesGameSubsystem.h" #include "StevesGameSubsystem.h"
#include "EngineUtils.h"
#include "EnhancedInputSubsystems.h" #include "EnhancedInputSubsystems.h"
#include "StevesGameViewportClientBase.h" #include "StevesGameViewportClientBase.h"
#include "StevesPluginSettings.h"
#include "StevesUEHelpers.h" #include "StevesUEHelpers.h"
#include "Engine/AssetManager.h" #include "Engine/AssetManager.h"
#include "Engine/GameInstance.h" #include "Engine/GameInstance.h"
@ -72,6 +74,39 @@ void UStevesGameSubsystem::NotifyEnhancedInputMappingsChanged()
GetWorld()->GetTimerManager().SetTimer(TempHandle, FTimerDelegate::CreateLambda(DelayedFunc), 0.05, false); GetWorld()->GetTimerManager().SetTimer(TempHandle, FTimerDelegate::CreateLambda(DelayedFunc), 0.05, false);
} }
TSoftObjectPtr<UInputAction> UStevesGameSubsystem::FindEnhancedInputAction(const FString& Name)
{
if (FAssetRegistryModule* AssetRegistryModule = FModuleManager::LoadModulePtr<FAssetRegistryModule>(TEXT("AssetRegistry")))
{
IAssetRegistry& AssetRegistry = AssetRegistryModule->Get();
if (auto Settings = GetDefault<UStevesPluginSettings>())
{
for (const auto& Dir : Settings->EnhancedInputActionSearchDirectories)
{
if (!FPackageName::IsValidPath(Dir.Path))
{
continue;
}
TArray<FAssetData> Assets;
FString Package = FPaths::Combine(Dir.Path, Name);
if (AssetRegistry.GetAssetsByPackageName(FName(*Package), Assets, true))
{
for (const FAssetData& Asset : Assets)
{
if (Asset.GetClass() == UInputAction::StaticClass())
{
return TSoftObjectPtr<UInputAction>(Asset.GetSoftObjectPath());
}
}
}
}
}
}
return nullptr;
}
void UStevesGameSubsystem::InitTheme() void UStevesGameSubsystem::InitTheme()
{ {
DefaultUiTheme = LoadObject<UUiTheme>(nullptr, *DefaultUiThemePath, nullptr); DefaultUiTheme = LoadObject<UUiTheme>(nullptr, *DefaultUiThemePath, nullptr);

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "StevesPluginSettings.h"

View File

@ -1,5 +1,9 @@
#include "StevesUEHelpers.h" #include "StevesUEHelpers.h"
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "StevesPluginSettings.h"
#define LOCTEXT_NAMESPACE "FStevesUEHelpers" #define LOCTEXT_NAMESPACE "FStevesUEHelpers"
DEFINE_LOG_CATEGORY(LogStevesUEHelpers) DEFINE_LOG_CATEGORY(LogStevesUEHelpers)
@ -8,6 +12,19 @@ void FStevesUEHelpers::StartupModule()
{ {
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
UE_LOG(LogStevesUEHelpers, Log, TEXT("Steve's UE Helpers Module Started")) UE_LOG(LogStevesUEHelpers, Log, TEXT("Steve's UE Helpers Module Started"))
// register settings
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (SettingsModule)
{
ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "Plugins", "StevesUEHelpers",
LOCTEXT("StevesUEHelpersSettingsName", "StevesUEHelpers"),
LOCTEXT("StevesUEHelpersSettingsDescription", "Configure the helpers plug-in."),
GetMutableDefault<UStevesPluginSettings>()
);
}
} }
void FStevesUEHelpers::ShutdownModule() void FStevesUEHelpers::ShutdownModule()

View File

@ -114,10 +114,12 @@ void UInputImage::UpdateImage()
if (GS) if (GS)
{ {
UPaperSprite* Sprite = nullptr; UPaperSprite* Sprite = nullptr;
if (BindingType == EInputBindingType::EnhancedInputAction) if (BindingType == EInputBindingType::EnhancedInputAction && !InputAction.IsNull())
{ {
auto IA = InputAction.LoadSynchronous(); if (auto IA = InputAction.LoadSynchronous())
Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, GetOwningPlayer(), CustomTheme); {
Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, GetOwningPlayer(), CustomTheme);
}
} }
else else
{ {

View File

@ -4,6 +4,7 @@
#include "StevesHelperCommon.h" #include "StevesHelperCommon.h"
#include "StevesUEHelpers.h" #include "StevesUEHelpers.h"
#include "Fonts/FontMeasure.h" #include "Fonts/FontMeasure.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/DefaultValueHelper.h" #include "Misc/DefaultValueHelper.h"
#include "Widgets/Layout/SScaleBox.h" #include "Widgets/Layout/SScaleBox.h"
#include "Widgets/Images/SImage.h" #include "Widgets/Images/SImage.h"
@ -18,6 +19,8 @@ struct FRichTextInputImageParams
FName ActionOrAxisName; FName ActionOrAxisName;
/// If BindingType is Key, the key /// If BindingType is Key, the key
FKey Key; FKey Key;
/// If binding type is EnhancedInputAction, a reference to an enhanced input action
TSoftObjectPtr<UInputAction> InputAction;
/// Player index, if binding type is action or axis /// Player index, if binding type is action or axis
int PlayerIndex; int PlayerIndex;
/// Where there are multiple mappings, which to prefer /// Where there are multiple mappings, which to prefer
@ -38,6 +41,8 @@ protected:
FName ActionOrAxisName; FName ActionOrAxisName;
/// If BindingType is Key, the key /// If BindingType is Key, the key
FKey Key; FKey Key;
/// If binding type is EnhancedInputAction, a reference to an enhanced input action
TSoftObjectPtr<UInputAction> InputAction;
/// Player index, if binding type is action or axis /// Player index, if binding type is action or axis
int PlayerIndex = 0; int PlayerIndex = 0;
/// Where there are multiple mappings, which to prefer /// Where there are multiple mappings, which to prefer
@ -66,6 +71,7 @@ public:
ActionOrAxisName = InParams.ActionOrAxisName; ActionOrAxisName = InParams.ActionOrAxisName;
DevicePreference = InParams.DevicePreference; DevicePreference = InParams.DevicePreference;
Key = InParams.Key; Key = InParams.Key;
InputAction = InParams.InputAction;
PlayerIndex = InParams.PlayerIndex; PlayerIndex = InParams.PlayerIndex;
Decorator = InParams.Decorator; Decorator = InParams.Decorator;
RequestedWidth = Width; RequestedWidth = Width;
@ -77,7 +83,7 @@ public:
// We will need to do the work to update the brush from the main thread later // We will need to do the work to update the brush from the main thread later
// We can use static methods though // We can use static methods though
if (InParams.InitialSprite) if (IsValid(InParams.InitialSprite))
UStevesGameSubsystem::SetBrushFromAtlas(&Brush, InParams.InitialSprite, true); UStevesGameSubsystem::SetBrushFromAtlas(&Brush, InParams.InitialSprite, true);
TimeUntilNextSpriteCheck = 0.25f; TimeUntilNextSpriteCheck = 0.25f;
@ -128,7 +134,19 @@ public:
if (GS) if (GS)
{ {
// Can only support default theme, no way to edit theme in decorator config // Can only support default theme, no way to edit theme in decorator config
auto Sprite = GS->GetInputImageSprite(BindingType, ActionOrAxisName, Key, DevicePreference, PlayerIndex); UPaperSprite* Sprite = nullptr;
if (BindingType == EInputBindingType::EnhancedInputAction && !InputAction.IsNull())
{
if (auto IA = InputAction.LoadSynchronous())
{
auto PC = UGameplayStatics::GetPlayerController(Decorator->GetWorld(), PlayerIndex);
Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, PC);
}
}
else
{
Sprite = GS->GetInputImageSprite(BindingType, ActionOrAxisName, Key, DevicePreference, PlayerIndex);
}
if (Sprite && Brush.GetResourceObject() != Sprite) if (Sprite && Brush.GetResourceObject() != Sprite)
{ {
UStevesGameSubsystem::SetBrushFromAtlas(&Brush, Sprite, true); UStevesGameSubsystem::SetBrushFromAtlas(&Brush, Sprite, true);
@ -175,7 +193,8 @@ public:
{ {
return RunParseResult.MetaData.Contains(TEXT("key")) || return RunParseResult.MetaData.Contains(TEXT("key")) ||
RunParseResult.MetaData.Contains(TEXT("action")) || RunParseResult.MetaData.Contains(TEXT("action")) ||
RunParseResult.MetaData.Contains(TEXT("axis")); RunParseResult.MetaData.Contains(TEXT("axis")) ||
RunParseResult.MetaData.Contains(TEXT("eaction"));
} }
return false; return false;
@ -191,6 +210,8 @@ protected:
Params.Key = EKeys::AnyKey; Params.Key = EKeys::AnyKey;
Params.Decorator = Decorator; Params.Decorator = Decorator;
auto GS = GetStevesGameSubsystem(Decorator->GetWorld());
if (const FString* PlayerStr = RunInfo.MetaData.Find(TEXT("player"))) if (const FString* PlayerStr = RunInfo.MetaData.Find(TEXT("player")))
{ {
int PTemp; int PTemp;
@ -212,6 +233,15 @@ protected:
Params.BindingType = EInputBindingType::Axis; Params.BindingType = EInputBindingType::Axis;
Params.ActionOrAxisName = **AxisStr; Params.ActionOrAxisName = **AxisStr;
} }
else if (const FString* EInputStr = RunInfo.MetaData.Find(TEXT("eaction")))
{
Params.BindingType = EInputBindingType::EnhancedInputAction;
// Try to find the input action
if (GS)
{
Params.InputAction = GS->FindEnhancedInputAction(*EInputStr);
}
}
if (const FString* PreferStr = RunInfo.MetaData.Find(TEXT("prefer"))) if (const FString* PreferStr = RunInfo.MetaData.Find(TEXT("prefer")))
{ {
@ -240,11 +270,21 @@ protected:
// Look up the initial sprite here // Look up the initial sprite here
// The Slate widget can't do it in Construct because World pointer doesn't work (thread issues?) // The Slate widget can't do it in Construct because World pointer doesn't work (thread issues?)
// Also annoying: can't keep Brush on this class because this method is const. UGH // Also annoying: can't keep Brush on this class because this method is const. UGH
auto GS = GetStevesGameSubsystem(Decorator->GetWorld());
if (GS) if (GS)
{ {
// Can only support default theme, no way to edit theme in decorator config if (Params.BindingType == EInputBindingType::EnhancedInputAction && !Params.InputAction.IsNull())
Params.InitialSprite = GS->GetInputImageSprite(Params.BindingType, Params.ActionOrAxisName, Params.Key, Params.DevicePreference, Params.PlayerIndex); {
if (auto IA = Params.InputAction.LoadSynchronous())
{
auto PC = UGameplayStatics::GetPlayerController(Decorator->GetWorld(), Params.PlayerIndex);
Params.InitialSprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, Params.DevicePreference, Params.PlayerIndex, PC);
}
}
else
{
// Can only support default theme, no way to edit theme in decorator config
Params.InitialSprite = GS->GetInputImageSprite(Params.BindingType, Params.ActionOrAxisName, Params.Key, Params.DevicePreference, Params.PlayerIndex);
}
} }
else else
{ {

View File

@ -225,7 +225,11 @@ public:
* @param Theme Optional explicit theme, if blank use the default theme * @param Theme Optional explicit theme, if blank use the default theme
* @return * @return
*/ */
UPaperSprite* GetInputImageSpriteFromEnhancedInputAction(UInputAction* Action, EInputImageDevicePreference DevicePreference, int PlayerIdx, APlayerController* PC, UUiTheme* Theme); UPaperSprite* GetInputImageSpriteFromEnhancedInputAction(UInputAction* Action,
EInputImageDevicePreference DevicePreference,
int PlayerIdx,
APlayerController* PC,
UUiTheme* Theme = nullptr);
/** /**
* @brief Get an input button / key image from an action * @brief Get an input button / key image from an action
@ -292,4 +296,8 @@ public:
*/ */
void NotifyEnhancedInputMappingsChanged(); void NotifyEnhancedInputMappingsChanged();
/** Attempt to find an enhanced input action by name in the configured folders.
*/
TSoftObjectPtr<UInputAction> FindEnhancedInputAction(const FString& Name);
}; };

View File

@ -0,0 +1,23 @@
#pragma once
#include "CoreMinimal.h"
#include "StevesPluginSettings.generated.h"
/**
* Settings for the plug-in.
*/
UCLASS(config=Engine)
class STEVESUEHELPERS_API UStevesPluginSettings
: public UObject
{
GENERATED_BODY()
public:
/// Which directories to search for Enhanced Input Actions when referenced just by name in e.g. Rich Text Decorator
UPROPERTY(config, EditAnywhere, Category = StevesUEHelpers, meta = (DisplayName = "Directories to search for Enhanced Input Actions", RelativeToGameContentDir))
TArray<FDirectoryPath> EnhancedInputActionSearchDirectories;
UStevesPluginSettings() {}
};

View File

@ -17,10 +17,15 @@ InputImage requires a [UiTheme](UiTheme.md) to operate, which links to the image
### Binding Type ### Binding Type
* "Action" if the image should display the current mapping for an input action * "Enhanced Input Action" to specify an [Enhanced Input](https://docs.unrealengine.com/5.1/en-US/enhanced-input-in-unreal-engine/) action
* "Axis" to look up an input axis * "Action" if the image should display the current mapping for a legacy input action
* "Axis" to look up a legacy input axis
* "Key" to manually specify a key (which can be gamepad or mouse too) * "Key" to manually specify a key (which can be gamepad or mouse too)
### Enhanced Input Action
Pick an Enhanced Input action from the asset browser interface.
### Action or Axis Name ### Action or Axis Name
The name of the input action or axis that should be looked up to determine the The name of the input action or axis that should be looked up to determine the

View File

@ -33,13 +33,21 @@ related to input controls. There are various options:
## Adding input images to rich text ## Adding input images to rich text
### Input Actions ### Enhanced Input Actions
`<input eaction="IA_MyAction"/>`
This displays the image for a bound Enhanced Input action. The name of the action
should match the action name, which is relative to one of the directories
you specify in Project Settings > Plugins > StevesUEHelpers.
### Legacy Input Actions
`<input action="TheActionName"/>` `<input action="TheActionName"/>`
This displays the image for a bound action input, as configured in project settings. This displays the image for a bound action input, as configured in project settings.
### Input Axes ### Legacy Input Axes
`<input axis="TheAxisName"/>` `<input axis="TheAxisName"/>`