[CommonUI] Issue with SetFocus and CommonButtonBase Slate button

Hello!

I’m having a weird issue with CommonUI and the UCommonButtonBase when using the fonction “SetFocus” on it.

For what I’ve investigate, it seems like the UCommonButtonBase create with ConstructInternalButton() a UCommonButtonInternalBase and wraps itself in it to manage and override all the click, hover… etc events.

Using the SlateDebugger, I’ve track the focused elements when navigate in my interface and found some weird behavior.

My issue is that when I use SetFocus to try to focus a UCommonButtonBase (or any inherited class) it focus the CommonButtonBase instance, and not the InternalButton (called InternalRootButtonBase).
Therefore, it doesn’t fire any hover or click event, since it’s managed by the internalButton.

However, if I navigate in my list of buttons, I can see that Unreal default navigation focus the InternalButton, fireing any event I love :smiley:

So I have a list of button, Set focus on the first in the Activated Event of the Widget, can’t click it with Gamepad, doesn’t have the Hover event. Navigate to the one just bellow, everything works find, go back to my first button, once again, everything is find, meaning that the issue is around the SetFocus.

(I’ve tried to be a smart hacky boy, by focusing the InternalButton in the Focus event of the CommonButton, but it says that it’s not focusable… even after setting it to true litteraly one line above)

So does anyone had the issue and know what’s not working here?

Thanks for future help !

4 Likes

I am having the same exact issue. Have you figured out a workaround? I’ve been on this for a hot minute now.

Thanks in advance!

This problem still exists. After searching for a solution for hours, I gave up and just went back to using a normal Button widget and manually implementing any functionality I need. I hope this gets fixed eventually, especially since cross-platform input is one of CommonUI’s most touted features.

I spent a day looking into this same problem. I found something that works, but requires editing the CommonUI plugin code.

Simply add these to CommonButtonBase.h/cpp:

COMMONUI_API TSharedPtr<SButton> GetSlateButton() const;

UFUNCTION(BlueprintCallable, Category = "Common Button")
COMMONUI_API void SetFocusOnRootButton();

cpp file:

TSharedPtr<SButton> UCommonButtonInternalBase::GetSlateButton() const
{
return MyCommonButton;
}

void UCommonButtonBase::SetFocusOnRootButton()
{
FSlateApplication::Get().SetKeyboardFocus(RootButton->GetSlateButton());
}

Then call SetFocusOnRootButton on your CommonButtonBase instead of SetFocus.
If there’s some way to retrieve the Slate button from UMG then you could get around having to modify the plugin. Not sure how to do that though.

If anyone else runs into this issue, here’s how I worked around it. Example code is at the bottom.

We have 2 problems. The first is that RootButton is private inside UCommonButtonBase. The second is that MyCommonButton inside UCommonButtonInternalBase is protected. These two make it so you can’t access it directly from your own button class.

Here is how I fixed this:

  1. Subclass UCommonButtonInternalBase
  • In your subclass, create a getter function that returns MyCommonButton.
  • This lets you safely expose it for later use.
  1. Override ConstructInternalButton() in your button class (which inherits from UCommonButtonBase)
  • This function is protected and virtual, so you can create your own subclass of UCommonButtonInternalBase instead of the default internal button.
  • At the same time, you can cache a pointer to your custom internal button so you can use it later.
  1. Override NativeOnFocusReceived()
  • When your button receives focus, you can forward that focus directly to the actual Slate button (MyCommonButton) using the getter you exposed earlier.
  • This ensures the Slate widget behaves correctly when navigating with keyboard or controller.

This way you don’t need to touch the Common UI’s plugin code, which I really wanted to avoid.

Here’s what that looks like in code:

Header file:

UCLASS()
class UCustomCommonButtonInternalBase : public UCommonButtonInternalBase
{
	GENERATED_BODY()

public:
	TSharedPtr<SCommonButton> GetSlateButton() const { return MyCommonButton; }
};

UCLASS()
class UCustomCommonButton : public UCommonButtonBase
{
	GENERATED_BODY()
	
protected:
	UPROPERTY(Transient)
	TObjectPtr<UCustomCommonButtonInternalBase> CachedInternalButton{};
	
	virtual UCommonButtonInternalBase* ConstructInternalButton() override;

	virtual FReply NativeOnFocusReceived(const FGeometry& InGeometry, const FFocusEvent& InFocusEvent) override;
};

CPP file:

UCommonButtonInternalBase* UCustomCommonButton::ConstructInternalButton()
{
	CachedInternalButton = WidgetTree->ConstructWidget<UCustomCommonButtonInternalBase>(
		UCustomCommonButtonInternalBase::StaticClass(),
		FName(TEXT("InternalRootButtonBase"))
	);
	return Cast<UCommonButtonInternalBase>(CachedInternalButton);
}

FReply UCustomCommonButton::NativeOnFocusReceived(const FGeometry& InGeometry, const FFocusEvent& InFocusEvent)
{
	Super::NativeOnFocusReceived(InGeometry, InFocusEvent);
	
	if (CachedInternalButton->GetSlateButton())
	{
		FSlateApplication::Get().SetKeyboardFocus(CachedInternalButton->GetSlateButton());
		return FReply::Handled();
	}

	return FReply::Unhandled();
}