Common UI's Triggering input action not working when set to controller D-pad

Hi,

I have an issue with common UI where if i set a CommonButtonBase’s triggering input action to either the Gamepad’s D-pad,thumbsticks or the Left/Right triggers, it doesn’t seem to trigger at all. However, It works fine when you set it to the shoulder keys or the gamepad face buttons. I’m not sure but it could be conflicting with the UI Navigation system. Its for settings button that sort of like structured Like this:

[Over all quality < Ultra > ]

It makes sense to map the button to the left/right D-pad or thumbstick to change the settings and not to the face button. Is there any workaround of fixes for this issue via BP?

I did attempt enhanced input method that seems to have a similar issue.

Thanks,
Iby

1 Like

So after playing around and doing some digging I haven’t found a direct fix, but I have found a workaround.

It seems in the engine code SWidget::OnKeyDown handles navigation events and stops the input from reaching CommonUI, but SWidget::OnKeyUp doesn’t.

So if you either modify or subclass CommonButtonBase (or implement a method to register an input action in another UCommonUserWidget), if you set the FBindUIActionArgs KeyEvent to IE_Released it bypasses this issue.

void UCommonButtonBase::BindTriggeringInputActionToClick()
{
	if (TriggeringInputAction.IsNull() || !TriggeredInputAction.IsNull())
	{
		return;
	}

	if (!TriggeringBindingHandle.IsValid())
	{
		FBindUIActionArgs BindArgs(TriggeringInputAction, false, FSimpleDelegate::CreateUObject(this, &UCommonButtonBase::HandleButtonClicked));
		BindArgs.OnHoldActionProgressed.BindUObject(this, &UCommonButtonBase::NativeOnActionProgress);
		BindArgs.bIsPersistent = bIsPersistentBinding;

		BindArgs.InputMode = InputModeOverride;
		
		TriggeringBindingHandle = RegisterUIActionBinding(BindArgs);
	}
}

As an example, if you added a line here
BindArgs.KeyEvent = IE_Released;
Then the issue no longer occurs.

1 Like

Hi there @Shader_Ghost,

Hope you’re well and having a great week so far!

This topic has been moved from International to Programming & Scripting: Blueprint.

When posting, please review the categories to ensure your topic is posted in the most relevant space.

Thanks and happy developing! :slight_smile:

Ahh I see. Thanks @AlexZilbersher. Will doing this disable navigation entirely? I need to still be able to navigate up and down to select other setting buttons but still have the side left and right navigation be overriden with the triggering input action.

I came across theOnPreviewKeyDown function the other day and that seems to override navigation. The drawback of using this is that i cant use the common UI’s triggering input action database. I’m having to manually check against hard coded values which isnt ideal because i need to be able to update this whenever i change the triggering input on the database.

It seems that Common UI provides something like what you are describing in the form of UCommonRotator. It’s used in the Unreal Engine Lyra Sample Project to make widgets like this.

Digging a bit into the code it seems that they override Navigation to call their own functions on Left/Right button presses

bool UCommonRotator::Initialize()
{
	if (Super::Initialize())
	{
		OnNavigation.BindUObject(this, &UCommonRotator::HandleNavigation);

		return true;
	}

	return false;
}

FNavigationReply UCommonRotator::NativeOnNavigation(const FGeometry& MyGeometry, const FNavigationEvent& InNavigationEvent, const FNavigationReply& InDefaultReply)
{
	switch (InNavigationEvent.GetNavigationType())
	{
	case EUINavigation::Left:
	case EUINavigation::Right:
		return FNavigationReply::Custom(OnNavigation);
	default:
		return InDefaultReply;
	}
}

TSharedPtr<SWidget> UCommonRotator::HandleNavigation(EUINavigation UINavigation)
{
	if (UINavigation == EUINavigation::Left)
	{
		ShiftTextLeftInternal(true);
	}
	else if (UINavigation == EUINavigation::Right)
	{
		ShiftTextRightInternal(true);
	}

	return nullptr;
}

Unfortunately, I couldn’t seem to figure out how to get gamepad triggers to also work in this manner. This seems to only work with Left/Right navigation such as dpad and analog stick.

2 Likes

Stumbled on this post while researching how to turn off navigation on the Dpad and ended up finding a solution that works and can be turned off and on at runtime.
Adding this comment for anyone in the future trying to find a solution to this.

So first of all in the constructor of the NavigationConfig.cpp a map called KeyEventRules is setup which is the list of inputs that are supposed to be navigation inputs

FNavigationConfig::FNavigationConfig()
	: bTabNavigation(true)
	, bKeyNavigation(true)
	, bAnalogNavigation(true)
	, AnalogNavigationHorizontalThreshold(0.50f)
	, AnalogNavigationVerticalThreshold(0.50f)
{
	AnalogHorizontalKey = EKeys::Gamepad_LeftX;
	AnalogVerticalKey = EKeys::Gamepad_LeftY;

	KeyEventRules.Emplace(EKeys::Left, EUINavigation::Left);
	KeyEventRules.Emplace(EKeys::Gamepad_DPad_Left, EUINavigation::Left);

	KeyEventRules.Emplace(EKeys::Right, EUINavigation::Right);
	KeyEventRules.Emplace(EKeys::Gamepad_DPad_Right, EUINavigation::Right);

	KeyEventRules.Emplace(EKeys::Up, EUINavigation::Up);
	KeyEventRules.Emplace(EKeys::Gamepad_DPad_Up, EUINavigation::Up);

	KeyEventRules.Emplace(EKeys::Down, EUINavigation::Down);
	KeyEventRules.Emplace(EKeys::Gamepad_DPad_Down, EUINavigation::Down);
}

With this in mind we can just add and remove inputs from this list at runtime to turn on/off navigation for some inputs. To do this you can just add this code to your project and with it you can manually turn on and off the navigation at will

UFUNCTION(BlueprintCallable)
virtual void TurnOffDPadNavigation();
	
UFUNCTION(BlueprintCallable)
virtual void TurnOnDpadNavigation();


void ASomeClass::TurnOffDPadNavigation()
{
	if (FSlateApplication::IsInitialized())
	{
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Remove(EKeys::Gamepad_DPad_Down);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Remove(EKeys::Gamepad_DPad_Left);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Remove(EKeys::Gamepad_DPad_Right);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Remove(EKeys::Gamepad_DPad_Up);
	}
}

void ASomeClass::TurnOnDpadNavigation()
{
	if (FSlateApplication::IsInitialized())
	{
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Emplace(EKeys::Gamepad_DPad_Down);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Emplace(EKeys::Gamepad_DPad_Left);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Emplace(EKeys::Gamepad_DPad_Right);
		FSlateApplication::Get().GetNavigationConfig()->KeyEventRules.Emplace(EKeys::Gamepad_DPad_Up);
	}
}
1 Like