Last active
April 3, 2024 04:18
-
-
Save LuviKunG/febd6b7c10c6de0e93d8cea6e598ad89 to your computer and use it in GitHub Desktop.
Unreal Engine: Display the input action prompt sprite in Rich Text Block depend on binding of input action key mapping of Enhanced input system.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright by Thanut Panichyotai (@LuviKunG) | |
// CC BY-SA 4.0 | |
// https://creativecommons.org/licenses/by-sa/4.0/ | |
#include "Widgets/RichTextBlockInputActionDecorator.h" | |
#include "EnhancedInputSubsystems.h" | |
#include "InputMappingContext.h" | |
#include "Fonts/FontMeasure.h" | |
#include "Kismet/GameplayStatics.h" | |
#include "UserSettings/EnhancedInputUserSettings.h" | |
#include "Widgets/Layout/SScaleBox.h" | |
class SRichInlineImageSet : public SCompoundWidget | |
{ | |
public: | |
SLATE_BEGIN_ARGS(SRichInlineImageSet) | |
{ | |
} | |
SLATE_END_ARGS() | |
void Construct(const FArguments& InArgs, const TArray<FSlateBrush*> Brushes, const FTextBlockStyle& TextStyle) | |
{ | |
const TSharedRef<SHorizontalBox> HorizontalBox = SNew(SHorizontalBox); | |
for (const FSlateBrush* Brush : Brushes) | |
{ | |
auto Slot = HorizontalBox->AddSlot(); | |
Slot.AutoWidth(); | |
Slot[CreateBox(InArgs, Brush, TextStyle)]; | |
} | |
ChildSlot[HorizontalBox]; | |
} | |
private: | |
TSharedRef<SBox> CreateBox(const FArguments& InArgs, const FSlateBrush* Brush, const FTextBlockStyle& TextStyle) | |
{ | |
check(Brush); | |
const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); | |
const float IconHeight = FMath::Min(static_cast<float>(FontMeasure->GetMaxCharacterHeight(TextStyle.Font, 1.0f)), Brush->ImageSize.Y); | |
const float IconWidth = Brush->ImageSize.X; | |
return SNew(SBox).HeightOverride(IconHeight).WidthOverride(IconWidth)[SNew(SScaleBox).Stretch(EStretch::ScaleToFit).StretchDirection(EStretchDirection::DownOnly).VAlign(VAlign_Center)[SNew(SImage).Image(Brush)]]; | |
} | |
}; | |
class FRichTextInputActionDecorator : public FRichTextDecorator | |
{ | |
public: | |
FRichTextInputActionDecorator(URichTextBlock* InOwner, URichTextBlockInputActionDecorator* InDecorator); | |
virtual bool Supports(const FTextRunParseResults& RunParseResult, const FString& Text) const override | |
{ | |
const FString TagName = TEXT("action"); | |
const FString TagAction = TEXT("name"); | |
return RunParseResult.Name == TagName && RunParseResult.MetaData.Contains(TagAction); | |
} | |
protected: | |
URichTextBlockInputActionDecorator* Decorator; | |
virtual TSharedPtr<SWidget> CreateDecoratorWidget(const FTextRunInfo& RunInfo, const FTextBlockStyle& TextStyle) const override | |
{ | |
const FString TagName = TEXT("name"); | |
constexpr bool bWarnIfMissing = true; | |
TArray<FSlateBrush*> Brushes; | |
if (Decorator->FindImageInputActions(*RunInfo.MetaData[TagName], Brushes, bWarnIfMissing); Brushes.Num() > 0) | |
{ | |
return SNew(SRichInlineImageSet, Brushes, TextStyle); | |
} | |
return TSharedPtr<SWidget>(); | |
} | |
}; | |
FRichTextInputActionDecorator::FRichTextInputActionDecorator(URichTextBlock* InOwner, URichTextBlockInputActionDecorator* InDecorator): FRichTextDecorator(InOwner) | |
{ | |
Decorator = InDecorator; | |
} | |
URichTextBlockInputActionDecorator::URichTextBlockInputActionDecorator(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) | |
{ | |
bUseDefaultImagePrompt = true; | |
} | |
TSharedPtr<ITextDecorator> URichTextBlockInputActionDecorator::CreateDecorator(URichTextBlock* InOwner) | |
{ | |
return MakeShareable(new FRichTextInputActionDecorator(InOwner, this)); | |
} | |
void URichTextBlockInputActionDecorator::FindImageInputActions(const FName& InName, TArray<FSlateBrush*>& OutBrushes, bool bWarnIfMissing) | |
{ | |
FindSlashBrushInputActions(InName, OutBrushes, bWarnIfMissing); | |
if (bUseDefaultImagePrompt && OutBrushes.IsEmpty()) | |
{ | |
OutBrushes.Add(&DefaultBrush); | |
} | |
} | |
void URichTextBlockInputActionDecorator::FindSlashBrushInputActions(const FName& InName, TArray<FSlateBrush*>& OutBrushes, bool bWarnIfMissing) const | |
{ | |
OutBrushes.Reset(); | |
if (InName.IsNone()) | |
return; | |
const UGameInstance* GameInstance = GetWorld()->GetGameInstance(); | |
if (!GameInstance) | |
return; | |
const ULocalPlayer* LocalPlayer = GameInstance->GetFirstGamePlayer(); | |
if (!LocalPlayer) | |
return; | |
const UEnhancedInputLocalPlayerSubsystem* InputLocalPlayerSubsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(); | |
if (!InputLocalPlayerSubsystem) | |
return; | |
const UEnhancedInputUserSettings* UserSettings = InputLocalPlayerSubsystem->GetUserSettings(); | |
if (!UserSettings) | |
return; | |
const UEnhancedPlayerMappableKeyProfile* CurrentKeyMap = UserSettings->GetCurrentKeyProfile(); | |
if (!CurrentKeyMap) | |
return; | |
const TMap<FName, FKeyMappingRow>& PlayerMappingRows = CurrentKeyMap->GetPlayerMappingRows(); | |
for (TTuple<FName, FKeyMappingRow> MappingRow : PlayerMappingRows) | |
{ | |
const FKeyMappingRow& KeyMappingRow = MappingRow.Value; | |
for (const FPlayerKeyMapping& KeyMapping : KeyMappingRow.Mappings) | |
{ | |
const UInputAction* InputAction = KeyMapping.GetAssociatedInputAction(); | |
if (!InputAction) | |
continue; | |
const FName ActionName = InputAction->GetFName(); | |
if (ActionName == InName) | |
{ | |
FKey Key = KeyMapping.GetCurrentKey(); | |
const FName KeyName = Key.GetFName(); | |
if (FRichImageRow* ImageRow = FindImageRow(KeyName, bWarnIfMissing)) | |
OutBrushes.AddUnique(&ImageRow->Brush); | |
} | |
} | |
} | |
if (OutBrushes.Num() > 0) | |
{ | |
// NOTE: There are already configured input mappings for the given action name. Skip checking the registered input mapping contexts. | |
return; | |
} | |
for (const TObjectPtr<const UInputMappingContext> InputMappingContext : UserSettings->GetRegisteredInputMappingContexts()) | |
{ | |
const TArray<FEnhancedActionKeyMapping>& Mappings = InputMappingContext->GetMappings(); | |
for (const FEnhancedActionKeyMapping& Mapping : Mappings) | |
{ | |
const FName ActionName = Mapping.Action.GetFName(); | |
if (ActionName == InName) | |
{ | |
FKey Key = Mapping.Key; | |
const FName KeyName = Key.GetFName(); | |
if (FRichImageRow* ImageRow = FindImageRow(KeyName, bWarnIfMissing)) | |
OutBrushes.AddUnique(&ImageRow->Brush); | |
} | |
} | |
} | |
} | |
FRichImageRow* URichTextBlockInputActionDecorator::FindImageRow(const FName& InName, bool bWarnIfMissing) const | |
{ | |
if (ImageSetArray.IsEmpty()) | |
return nullptr; | |
for (const TObjectPtr<UDataTable>& ImageSet : ImageSetArray) | |
{ | |
if (ImageSet) | |
{ | |
const FString ContextString; | |
if (FRichImageRow* Row = ImageSet->FindRow<FRichImageRow>(InName, ContextString, bWarnIfMissing)) | |
return Row; | |
} | |
} | |
return nullptr; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright by Thanut Panichyotai (@LuviKunG) | |
// CC BY-SA 4.0 | |
// https://creativecommons.org/licenses/by-sa/4.0/ | |
#pragma once | |
#include "CoreMinimal.h" | |
#include "Components/RichTextBlock.h" | |
#include "Components/RichTextBlockDecorator.h" | |
#include "Components/RichTextBlockImageDecorator.h" | |
#include "Framework/Text/ITextDecorator.h" | |
#include "UObject/Object.h" | |
#include "RichTextBlockInputActionDecorator.generated.h" | |
/** | |
* Allows you to setup an input action decorator that can be configured. | |
*/ | |
UCLASS(Abstract, Blueprintable) | |
class REPLACE_YOUR_API URichTextBlockInputActionDecorator : public URichTextBlockDecorator | |
{ | |
GENERATED_BODY() | |
public: | |
URichTextBlockInputActionDecorator(const FObjectInitializer& ObjectInitializer); | |
virtual TSharedPtr<ITextDecorator> CreateDecorator(URichTextBlock* InOwner) override; | |
/** | |
* Find the image input actions. | |
* @param InName Name of the input action. This is the same name of the enhanced input action file name. | |
* @param OutBrushes The brushes to output. | |
* @param bWarnIfMissing Whether to warn if the input action is missing. | |
*/ | |
void FindImageInputActions(const FName& InName, TArray<FSlateBrush*>& OutBrushes, bool bWarnIfMissing); | |
protected: | |
/** Determines whether to use the default image prompt. */ | |
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Configurations", DisplayName = "Use Default Image Prompt") | |
bool bUseDefaultImagePrompt; | |
/** The default image prompt slate brush. Must be set if 'bUseDefaultImagePrompt' is true. */ | |
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Configurations", DisplayName = "Default Image Prompt Slate Brush", meta = (EditCondition = "bUseDefaultImagePrompt")) | |
FSlateBrush DefaultBrush; | |
/** The image set array. The row structure must be 'RichImageRow'. */ | |
UPROPERTY(EditAnywhere, Category = "Appearance", meta = (RequiredAssetDataTags = "RowStructure=/Script/UMG.RichImageRow")) | |
TArray<TObjectPtr<UDataTable>> ImageSetArray; | |
/** | |
* Find the slash brush input actions. | |
* @param InName Name of the input action. This is the same name of the enhanced input action file name. | |
* @param OutBrushes The brushes to output. | |
* @param bWarnIfMissing Whether to warn if the input action is missing. | |
*/ | |
void FindSlashBrushInputActions(const FName& InName, TArray<FSlateBrush*>& OutBrushes, bool bWarnIfMissing) const; | |
/** | |
* Find the image row. | |
* This will search the image set array. | |
* @param TagOrId The tag or id of the image row. | |
* @param bWarnIfMissing Whether to warn if the image row is missing. | |
* @return The image row if found, otherwise nullptr. | |
*/ | |
FRichImageRow* FindImageRow(const FName& TagOrId, bool bWarnIfMissing) const; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment