Bug with CommonUI button IsPressed state

Based on other forums this seems to be the general case with UMG but I haven’t extensively tested it

I’ve made a common button (derived from common button base), added 3 of it to my widget, enabled keyboard navigation

  • Pressing interact via keyboard then navigating to the next button without releasing the interact key makes the previous button stuck with isPressed being true
  • Doing the same thing with the next button makes the exact same thing happen
  • Holding pressed on another button, then navigating back to any of the buttons that are stuck with isPressed being true, then releasing it on them, causes the click action to happen

The problem seems to lie in the fact that isPressed isn’t reset when focus is given to another button e.g through navigation and that onRelease doesn’t trigger when I navigate to another button, this isn’t the case for the mouse

I’ve tried disabling and reenabling the button, setting focusable to on and off, setting visibility to hidden/non-hit testable then back to visible, but nothing seemed to fix it

Making a bool like “CanDoClick” that is set to false when the mouse is unhovered, and checking that in OnClick and OnRelease so that you need to click while hovering over a button was one of my ideas for fixing this, but the problem is this doesn’t affect the common button style

Is there no way to reset IsPressed?

It seems like there’s a mistake in the CommonUI code
For standard behaviour SButton calls Release() at the end of OnFocusLost:

void SButton::OnFocusLost( const FFocusEvent& InFocusEvent )
{
	SBorder::OnFocusLost(InFocusEvent);

	Release();
}

However, in SCommonButton, which overrides SButton::OnFocusLost, this call is missing:

void SCommonButton::OnFocusLost(const FFocusEvent& InFocusEvent)
{
	OnLostFocus.ExecuteIfBound();
}

In other overridden methods SCommonButton invokes the parent class implementation

like this
FReply SCommonButton::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent)
{
	FReply ReturnReply = SButton::OnFocusReceived(MyGeometry, InFocusEvent);
	OnReceivedFocus.ExecuteIfBound();

	return ReturnReply;
}

So, i believe they just “forgot” to add it in OnFocusLost

Of course, all these Slate methods are not exposed to Blueprints, so it seems like the only right solution is to create some C++ classes.
The easy way is to create a child of UCommonButtonBase and override HandleFocusLost() to something like this:

void UCustomCommonButtonBase::HandleFocusLost()
{
	Super::HandleFocusLost();

	/*
	 * GetRootWidget() - UCommonButtonInternalBase
	 * GetCachedWidget() - SBox (see UCommonButtonInternalBase::RebuildWidget())
	 * GetChildren()->GetChildAt(0) - SCommonButton
	 */
	const TSharedRef<SWidget> CommonButton = GetRootWidget()->GetCachedWidget()->GetChildren()->GetChildAt(0);
	const TSharedRef<SButton> Button = StaticCastSharedRef<SButton>(CommonButton);
	const FFocusEvent DummyFocusEvent(EFocusCause::SetDirectly, GetOwnerSlateUser()->GetUserIndex());
	Button->SButton::OnFocusLost(DummyFocusEvent);
}

The right way (I believe) is to also create a child of UCommonButtonInternalBase and SCommonButton, set up all these widgets and override SCommonButton::OnFocusLost