UMG - NativeConstruct + SetUserFocus

Hello,

SetUserFocus calls are ignored when trying to focus a Widget in NativeConstruct() in code (or OnConstruct in Blueprint). If you add any amount of delay to the call, the call will succeed, though this isn’t a good solution. Setting focus in NativeConstruct() is a common operation since that’s where screens initialize. One thing to note is that setting focus through setting the input mode always works, though this is also not a great solution since we might not always want to reset the input mode mid-stream.

Digging around the code, the issue seems to be a discrepancy in how focus is set internally. The current code in UWidget::SetUserFocus() is:



if ( ULocalPlayer* LocalPlayer = Context.GetLocalPlayer() )
{
	// HACK: We use the controller Id as the local player index for focusing widgets in Slate.
	int32 UserIndex = LocalPlayer->GetControllerId();

	FSlateApplication::Get().SetUserFocus(UserIndex, SafeWidget);
}


Changing this to the same mechanism that Set Input Mode UI uses, the code becomes:



if ( ULocalPlayer* LocalPlayer = Context.GetLocalPlayer() )
{
	LocalPlayer->GetSlateOperations().SetUserFocus( SafeWidget.ToSharedRef() );
}


With that code, everything works great.

Is this the right solution? Am I “doing it wrong” by trying to set focus this way?

The UWidget code is technically correct. The problem is that you’re calling SetFocus in Construct - the problem is that Construct is happening while the widget is being created. It doesn’t exist in the tree yet, so it’s not possible to focus it because SlateApplication can’t construct a path to it. Doing it via SlateOperations actually delays it by a frame, which is why it works, so 1 frame later, it happens to be in the tree now, so a path can be constructed and focus can be set.

This problem has been annoying me for some time, I don’t want to delay everything by a frame, but I also don’t want it to fail when the obvious place to set focus is during construct.

The thoughts I’ve had on fixing it are what you have, or more drastically don’t require it be important to build a path to set focus. Maybe it attempts to build a path for a few frames before giving up when it needs it, rather than trying it as soon as it’s set.

I think I’m going to change SlateApp to return if the SetUserFocus was a success, if it fails, fallback to trying to set it next frame.

That sounds reasonable, thanks for your help!