Hello Cody,
Thank you for your explanation. I understand the goal with Enhanced Input support. To be honest, we tried to activate the binding between both systems earlier we our project, but ended disabling it because of the many problems we encountered. We may reconsider our decision when it is less experimental.
I checked and the second button on the modal below is also bound to an input action mapped to the virtual accept key (Data Table), but without On Clicked delegate, like for the first modal. Indeed, this CTA is only cosmetic in gamepad. In this modal, we also have a list of navigable buttons that we manually bind in c++ on UCommonButtonBase::NativeOnClicked(), so the expected behaviour is that we navigate between them and commit our action using the “A” key (in Mouse and Keyboard, we just click the wanted button/line to select our wanted option, fowarding to the same NativeOnClicked() delegate). In any case, the CTA button never has focus, nor is intended to have it, I checked to be sure and its “bIsFocusable” is correctly set to “false”.
I placed a breakpoint on our override of the NativeOnClicked() callback to understand in which callstack the Key Release Event is called, and here is what I obtain:
[UnrealEditor-Brimstone-Win64-DebugGame.dll] UReviveOptionLine::NativeOnClicked() ReviveOptionLine.cpp:58 [UnrealEditor-CommonUI.dll] UCommonButtonBase::HandleButtonClicked() CommonButtonBase.cpp:1300 [UnrealEditor-CoreUObject.dll] UFunction::Invoke(UObject *, FFrame &, void *const) Class.cpp:7192 [Blueprint] CommonButtonBase: HandleButtonClicked Function /Script/CommonUI.CommonButtonBase:HandleButtonClicked [UnrealEditor-CoreUObject.dll] UObject::ProcessEvent(UFunction *, void *) ScriptCore.cpp:2173 [Inlined] [UnrealEditor-UMG.dll] TScriptDelegate<FNotThreadSafeNotCheckedDelegateMode>::ProcessDelegate(void *) ScriptDelegates.h:448 [UnrealEditor-UMG.dll] TMulticastScriptDelegate<FNotThreadSafeDelegateMode>::ProcessMulticastDelegate<UObject>(void *) ScriptDelegates.h:918 [Inlined] [UnrealEditor-UMG.dll] FOnButtonClickedEvent::Broadcast() Button.h:17 [UnrealEditor-UMG.dll] UButton::SlateHandleClicked() Button.cpp:259 [UnrealEditor-CommonUI.dll] UCommonButtonInternalBase::SlateHandleClickedOverride() CommonButtonBase.cpp:295 [Inlined] [UnrealEditor-CommonUI.dll] Invoke(FReply (UCommonButtonInternalBase::*)(), UCommonButtonInternalBase *&) Invoke.h:66 [Inlined] [UnrealEditor-CommonUI.dll] UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int> >::ApplyAfter(FReply (UCommonButtonInternalBase::*&)(), UCommonButtonInternalBase *&) Tuple.h:317 [UnrealEditor-CommonUI.dll] TBaseUObjectMethodDelegateInstance<0, UCommonButtonInternalBase, FReply __cdecl(void), FDefaultDelegateUserPolicy>::Execute() DelegateInstancesImpl.h:650 [Inlined] [UnrealEditor-Slate.dll] TDelegate<FReply __cdecl(void), FDefaultDelegateUserPolicy>::Execute() DelegateSignatureImpl.inl:613 [UnrealEditor-Slate.dll] SButton::ExecuteOnClick() SButton.cpp:467 [UnrealEditor-Slate.dll] SButton::OnMouseButtonUp(const FGeometry &, const FPointerEvent &) SButton.cpp:392 [UnrealEditor-CommonUI.dll] SCommonButton::OnMouseButtonUp(const FGeometry &, const FPointerEvent &) CommonButtonTypes.cpp:61 [UnrealEditor-Slate.dll]
FSlateApplication::RoutePointerUpEvent’::8'::<lambda_2>::operator()(const FArrangedWidget &,const FPointerEvent &) SlateApplication.cpp:5346 [UnrealEditor-Slate.dll] FEventRouter::Route<FReply,FEventRouter::FToLeafmostPolicy,FPointerEvent,
FSlateApplication::RoutePointerUpEvent’::8'::<lambda_2> >(FSlateApplication *,FToLeafmostPolicy,FPointerEvent,const <lambda_2> &,ESlateDebuggingInputEvent) SlateApplication.cpp:456 [UnrealEditor-Slate.dll] FSlateApplication::RoutePointerUpEvent(const FWidgetPath &, const FPointerEvent &) SlateApplication.cpp:5332 [UnrealEditor-Slate.dll] FSlateApplication::ProcessMouseButtonUpEvent(const FPointerEvent &) SlateApplication.cpp:5917 [UnrealEditor-Slate.dll] FAnalogCursor::HandleKeyUpEvent(FSlateApplication &, const FKeyEvent &) AnalogCursor.cpp:118 [UnrealEditor-CommonUI.dll] FCommonAnalogCursor::HandleKeyUpEvent(FSlateApplication &, const FKeyEvent &) CommonAnalogCursor.cpp:298 [Inlined] [UnrealEditor-Slate.dll] UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::FFunctionRefStoragePolicy, bool __cdecl(IInputProcessor &)>::operator()(IInputProcessor &) Function.h:470 [UnrealEditor-Slate.dll] FSlateApplication::InputPreProcessorsHelper::PreProcessInput(ESlateDebuggingInputEvent, TFunctionRef<bool __cdecl(IInputProcessor &)>) SlateApplication.cpp:7596 [Inlined] [UnrealEditor-Slate.dll] FSlateApplication::InputPreProcessorsHelper::HandleKeyUpEvent(FSlateApplication &, const FKeyEvent &) SlateApplication.cpp:7420 [UnrealEditor-Slate.dll] FSlateApplication::ProcessKeyUpEvent(const FKeyEvent &) SlateApplication.cpp:4874 [UnrealEditor-Slate.dll] FSlateApplication::OnControllerButtonReleased(FName, FPlatformUserId, FInputDeviceId, bool) SlateApplication.cpp:6402 [UnrealEditor-XInputDevice.dll] XInputInterface::SendControllerEvents() XInputInterface.cpp:256 [UnrealEditor-ApplicationCore.dll] FWindowsApplication::PollGameDeviceState(const float) WindowsApplication.cpp:2860 [UnrealEditor-Slate.dll] FSlateApplication::PollGameDeviceState() SlateApplication.cpp:1443 [UnrealEditor-Win64-DebugGame.exe] FEngineLoop::Tick() LaunchEngineLoop.cpp:5861 [Inlined] [UnrealEditor-Win64-DebugGame.exe] EngineTick() Launch.cpp:69 [UnrealEditor-Win64-DebugGame.exe] GuardedMain(const wchar_t *) Launch.cpp:190 [UnrealEditor-Win64-DebugGame.exe] LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:266 [UnrealEditor-Win64-DebugGame.exe] WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:317
Basically, we call FAnalogCursor::HandleKeyUpEvent(SlateApp, InKeyEvent) for the Key up event in FCommonAnalogCursor::HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) (see [Content removed] for the custom modifications we made in the snippet):
`bool FCommonAnalogCursor::HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent)
{
if (IsRelevantInput(InKeyEvent))
{
#if !UE_BUILD_SHIPPING
const FKey& PressedKey = InKeyEvent.GetKey();
if (PressedKey == EKeys::Gamepad_LeftShoulder) { ShoulderButtonStatus ^= EShoulderButtonFlags::LeftShoulder; }
if (PressedKey == EKeys::Gamepad_RightShoulder) { ShoulderButtonStatus ^= EShoulderButtonFlags::RightShoulder; }
if (PressedKey == EKeys::Gamepad_LeftTrigger) { ShoulderButtonStatus ^= EShoulderButtonFlags::LeftTrigger; }
if (PressedKey == EKeys::Gamepad_RightTrigger) { ShoulderButtonStatus ^= EShoulderButtonFlags::RightTrigger; }
#endif
// We support binding actions to the virtual accept key, so it’s a special flower that gets processed right now
const bool bIsVirtualAccept = InKeyEvent.GetKey() == EKeys::Virtual_Accept;
if (bIsVirtualAccept && ActionRouter.ProcessInput(InKeyEvent.GetKey(), IE_Released) == ERouteUIInputResult::Handled)
{
//TA>
bHandledVirtualAcceptDown = false;
//<TA
return true;
}
else if (!bIsVirtualAccept || ShouldVirtualAcceptSimulateMouseButton(InKeyEvent, IE_Released))
{
//TA>
if (bIsVirtualAccept && bHandledVirtualAcceptDown)
{
UCommonInputSubsystem& InputSubsytem = ActionRouter.GetInputSubsystem();
InputSubsytem.SetIsGamepadSimulatedClick(bIsVirtualAccept);
bool bReturnValue = FAnalogCursor::HandleKeyDownEvent(SlateApp, InKeyEvent);
InputSubsytem.SetIsGamepadSimulatedClick(false);
bHandledVirtualAcceptDown = false;
}
//<TA
return FAnalogCursor::HandleKeyUpEvent(SlateApp, InKeyEvent);
}
}
return false;
}`So, if I understand what is happening correctly, we would expect/want this lone event to be absorbed and somehow handled in this line:
if (bIsVirtualAccept && ActionRouter.ProcessInput(InKeyEvent.GetKey(), IE_Released) == ERouteUIInputResult::Handled)
but we don’t get a ERouteUIInputResult::Handled as a result, thus falling back to the line
else if (!bIsVirtualAccept || ShouldVirtualAcceptSimulateMouseButton(InKeyEvent, IE_Released))
from which stems my original post.