From e9fd6cb254a6c50ac140db84e0665c8adbfe5c5e Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 7 Mar 2023 18:10:25 +0000 Subject: [PATCH] Rich text input decorator can now reference enhanced input actions --- .../Private/StevesGameSubsystem.cpp | 35 +++++++++++++ .../Private/StevesPluginSettings.cpp | 4 ++ .../Private/StevesUEHelpers.cpp | 17 ++++++ .../Private/StevesUI/InputImage.cpp | 8 +-- .../RichTextBlockInputImageDecorator.cpp | 52 ++++++++++++++++--- .../Public/StevesGameSubsystem.h | 10 +++- .../Public/StevesPluginSettings.h | 23 ++++++++ doc/InputImage.md | 9 +++- doc/RichTextInputDecorator.md | 12 ++++- 9 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 Source/StevesUEHelpers/Private/StevesPluginSettings.cpp create mode 100644 Source/StevesUEHelpers/Public/StevesPluginSettings.h diff --git a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp index 1ed1ba4..c10d477 100644 --- a/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp +++ b/Source/StevesUEHelpers/Private/StevesGameSubsystem.cpp @@ -1,7 +1,9 @@ #include "StevesGameSubsystem.h" +#include "EngineUtils.h" #include "EnhancedInputSubsystems.h" #include "StevesGameViewportClientBase.h" +#include "StevesPluginSettings.h" #include "StevesUEHelpers.h" #include "Engine/AssetManager.h" #include "Engine/GameInstance.h" @@ -72,6 +74,39 @@ void UStevesGameSubsystem::NotifyEnhancedInputMappingsChanged() GetWorld()->GetTimerManager().SetTimer(TempHandle, FTimerDelegate::CreateLambda(DelayedFunc), 0.05, false); } +TSoftObjectPtr UStevesGameSubsystem::FindEnhancedInputAction(const FString& Name) +{ + if (FAssetRegistryModule* AssetRegistryModule = FModuleManager::LoadModulePtr(TEXT("AssetRegistry"))) + { + IAssetRegistry& AssetRegistry = AssetRegistryModule->Get(); + if (auto Settings = GetDefault()) + { + for (const auto& Dir : Settings->EnhancedInputActionSearchDirectories) + { + if (!FPackageName::IsValidPath(Dir.Path)) + { + continue; + } + + TArray 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(Asset.GetSoftObjectPath()); + } + } + } + + } + } + } + return nullptr; +} + void UStevesGameSubsystem::InitTheme() { DefaultUiTheme = LoadObject(nullptr, *DefaultUiThemePath, nullptr); diff --git a/Source/StevesUEHelpers/Private/StevesPluginSettings.cpp b/Source/StevesUEHelpers/Private/StevesPluginSettings.cpp new file mode 100644 index 0000000..cd627bd --- /dev/null +++ b/Source/StevesUEHelpers/Private/StevesPluginSettings.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "StevesPluginSettings.h" diff --git a/Source/StevesUEHelpers/Private/StevesUEHelpers.cpp b/Source/StevesUEHelpers/Private/StevesUEHelpers.cpp index 9245b1e..1e61638 100644 --- a/Source/StevesUEHelpers/Private/StevesUEHelpers.cpp +++ b/Source/StevesUEHelpers/Private/StevesUEHelpers.cpp @@ -1,5 +1,9 @@ #include "StevesUEHelpers.h" +#include "ISettingsModule.h" +#include "ISettingsSection.h" +#include "StevesPluginSettings.h" + #define LOCTEXT_NAMESPACE "FStevesUEHelpers" 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 UE_LOG(LogStevesUEHelpers, Log, TEXT("Steve's UE Helpers Module Started")) + + // register settings + ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); + + if (SettingsModule) + { + ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "Plugins", "StevesUEHelpers", + LOCTEXT("StevesUEHelpersSettingsName", "StevesUEHelpers"), + LOCTEXT("StevesUEHelpersSettingsDescription", "Configure the helpers plug-in."), + GetMutableDefault() + ); + } + } void FStevesUEHelpers::ShutdownModule() diff --git a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp index 5ddeff0..a7cd89b 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/InputImage.cpp @@ -114,10 +114,12 @@ void UInputImage::UpdateImage() if (GS) { UPaperSprite* Sprite = nullptr; - if (BindingType == EInputBindingType::EnhancedInputAction) + if (BindingType == EInputBindingType::EnhancedInputAction && !InputAction.IsNull()) { - auto IA = InputAction.LoadSynchronous(); - Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, GetOwningPlayer(), CustomTheme); + if (auto IA = InputAction.LoadSynchronous()) + { + Sprite = GS->GetInputImageSpriteFromEnhancedInputAction(IA, DevicePreference, PlayerIndex, GetOwningPlayer(), CustomTheme); + } } else { diff --git a/Source/StevesUEHelpers/Private/StevesUI/RichTextBlockInputImageDecorator.cpp b/Source/StevesUEHelpers/Private/StevesUI/RichTextBlockInputImageDecorator.cpp index 24420af..556fff2 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/RichTextBlockInputImageDecorator.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/RichTextBlockInputImageDecorator.cpp @@ -4,6 +4,7 @@ #include "StevesHelperCommon.h" #include "StevesUEHelpers.h" #include "Fonts/FontMeasure.h" +#include "Kismet/GameplayStatics.h" #include "Misc/DefaultValueHelper.h" #include "Widgets/Layout/SScaleBox.h" #include "Widgets/Images/SImage.h" @@ -18,6 +19,8 @@ struct FRichTextInputImageParams FName ActionOrAxisName; /// If BindingType is Key, the key FKey Key; + /// If binding type is EnhancedInputAction, a reference to an enhanced input action + TSoftObjectPtr InputAction; /// Player index, if binding type is action or axis int PlayerIndex; /// Where there are multiple mappings, which to prefer @@ -38,6 +41,8 @@ protected: FName ActionOrAxisName; /// If BindingType is Key, the key FKey Key; + /// If binding type is EnhancedInputAction, a reference to an enhanced input action + TSoftObjectPtr InputAction; /// Player index, if binding type is action or axis int PlayerIndex = 0; /// Where there are multiple mappings, which to prefer @@ -66,6 +71,7 @@ public: ActionOrAxisName = InParams.ActionOrAxisName; DevicePreference = InParams.DevicePreference; Key = InParams.Key; + InputAction = InParams.InputAction; PlayerIndex = InParams.PlayerIndex; Decorator = InParams.Decorator; RequestedWidth = Width; @@ -77,7 +83,7 @@ public: // We will need to do the work to update the brush from the main thread later // We can use static methods though - if (InParams.InitialSprite) + if (IsValid(InParams.InitialSprite)) UStevesGameSubsystem::SetBrushFromAtlas(&Brush, InParams.InitialSprite, true); TimeUntilNextSpriteCheck = 0.25f; @@ -128,7 +134,19 @@ public: if (GS) { // 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) { UStevesGameSubsystem::SetBrushFromAtlas(&Brush, Sprite, true); @@ -175,7 +193,8 @@ public: { return RunParseResult.MetaData.Contains(TEXT("key")) || RunParseResult.MetaData.Contains(TEXT("action")) || - RunParseResult.MetaData.Contains(TEXT("axis")); + RunParseResult.MetaData.Contains(TEXT("axis")) || + RunParseResult.MetaData.Contains(TEXT("eaction")); } return false; @@ -191,6 +210,8 @@ protected: Params.Key = EKeys::AnyKey; Params.Decorator = Decorator; + auto GS = GetStevesGameSubsystem(Decorator->GetWorld()); + if (const FString* PlayerStr = RunInfo.MetaData.Find(TEXT("player"))) { int PTemp; @@ -212,6 +233,15 @@ protected: Params.BindingType = EInputBindingType::Axis; 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"))) { @@ -240,11 +270,21 @@ protected: // Look up the initial sprite here // 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 - auto GS = GetStevesGameSubsystem(Decorator->GetWorld()); if (GS) { - // 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); + if (Params.BindingType == EInputBindingType::EnhancedInputAction && !Params.InputAction.IsNull()) + { + 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 { diff --git a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h index 3d44bb0..3f3f7a2 100644 --- a/Source/StevesUEHelpers/Public/StevesGameSubsystem.h +++ b/Source/StevesUEHelpers/Public/StevesGameSubsystem.h @@ -225,7 +225,11 @@ public: * @param Theme Optional explicit theme, if blank use the default theme * @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 @@ -292,4 +296,8 @@ public: */ void NotifyEnhancedInputMappingsChanged(); + /** Attempt to find an enhanced input action by name in the configured folders. + */ + TSoftObjectPtr FindEnhancedInputAction(const FString& Name); + }; diff --git a/Source/StevesUEHelpers/Public/StevesPluginSettings.h b/Source/StevesUEHelpers/Public/StevesPluginSettings.h new file mode 100644 index 0000000..b280001 --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesPluginSettings.h @@ -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 EnhancedInputActionSearchDirectories; + + UStevesPluginSettings() {} + +}; diff --git a/doc/InputImage.md b/doc/InputImage.md index 25006d8..bd7a974 100644 --- a/doc/InputImage.md +++ b/doc/InputImage.md @@ -17,10 +17,15 @@ InputImage requires a [UiTheme](UiTheme.md) to operate, which links to the image ### Binding Type -* "Action" if the image should display the current mapping for an input action -* "Axis" to look up an input axis +* "Enhanced Input Action" to specify an [Enhanced Input](https://docs.unrealengine.com/5.1/en-US/enhanced-input-in-unreal-engine/) action +* "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) +### Enhanced Input Action + +Pick an Enhanced Input action from the asset browser interface. + ### Action or Axis Name The name of the input action or axis that should be looked up to determine the diff --git a/doc/RichTextInputDecorator.md b/doc/RichTextInputDecorator.md index 2715cc0..af689a7 100644 --- a/doc/RichTextInputDecorator.md +++ b/doc/RichTextInputDecorator.md @@ -33,13 +33,21 @@ related to input controls. There are various options: ## Adding input images to rich text -### Input Actions +### Enhanced Input Actions + +`` + +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 `` This displays the image for a bound action input, as configured in project settings. -### Input Axes +### Legacy Input Axes ``