Skip to main content

CommonUI EnhancedInput

As you know, it’s possible to use Enhanced Input for keys with Common UI since a few versions of UE5.

However, as mentioned, some personal work is likely needed to fully integrate it because, by default, you need to activate an InputMappingContext in the UCommonActivatableWidget.

It works, but you’ll run into issues in certain cases, for example:

  • You have two UCommonActivatableWidget instances. If you activate the second one, the InputMappingContext from the first one will be removed, so you’ll lose the InputMappingContext, even if you specified it in the second UCommonActivatableWidget.

My Solution

For now, in my plugin, I inherit from UCommonActivatableWidget to add functionality, including managing a global IMC (InputMappingContext) with a pooling system to avoid the problem mentioned earlier.

Code Explain

To briefly explain the code: I have a Subsystem that retrieves your IMC through your DefaultGame.ini file and includes functions like PullGlobalMappingContext and PopGlobalMappingContext.

Struct for pull

USTRUCT(BlueprintType)
struct SOMNDUSGAME_API FSSInputMappingContextPullToken
{
GENERATED_BODY()

UPROPERTY(EditAnywhere, BlueprintReadWrite)
const UInputMappingContext* MappingContext;

UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Token;
};

Subsystem with the pull array

UCLASS(config = Game)
class SOMNDUSGAME_API USSInputLocalPlayerSubsystem : public ULocalPlayerSubsystem
{
GENERATED_BODY()

public:

...

UPROPERTY()
TArray<FSSInputMappingContextPullToken> InputMappingContextPullTokens;

Global IMC for CommonUI

In my USSInputLocalPlayerSubsystem

/**
* To use in every activatable widget instead specific one by one
*/
UPROPERTY(config, EditAnywhere)
TSoftObjectPtr<UInputMappingContext> GenericUIMappingContextSoft;

Example in DefaultGame.ini

[/Script/SomndusGame.SSInputLocalPlayerSubsystem]
GenericUIMappingContextSoft=/Game/Input/IMC_CommonMenu.IMC_CommonMenu

How USSGameActivatableWidget use it

NativeOnActivated

I’ll briefly explain how my ActivatableWidget works. In short, it pulls using a token (which will be the name of the instantiated widget). The pulling system checks whether to add or remove the global IMC based on whether any tokens remain in the pull array.

void USSGameActivatableWidget::NativeOnActivated()
{
Super::NativeOnActivated();
if (CommonUI::IsEnhancedInputSupportEnabled())
{
if (const ULocalPlayer* LocalPlayer = GetOwningLocalPlayer())
{
if (USSInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<USSInputLocalPlayerSubsystem>())
{
InputSystem->PullGlobalMappingContext(GetInputToken());
}
}
}
}

NativeOnDeactivated

void USSGameActivatableWidget::NativeOnDeactivated()
{
Super::NativeOnDeactivated();
if (CommonUI::IsEnhancedInputSupportEnabled())
{
if (const ULocalPlayer* LocalPlayer = GetOwningLocalPlayer())
{
if (USSInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<USSInputLocalPlayerSubsystem>())
{
InputSystem->PopGlobalMappingContext(GetInputToken());
}
}
}
}