SetFocus not working when called from Construct

I’m using UE4.27.

When my widget is constructed, I want to set the focus to one of the buttons. However, it seems that “SetFocus()” doesn’t actually focus on the widget when called on Construct or OnInitialize. I need to wait at least a frame (sometimes even 0.2 seconds) and then call SetFocus() before it actually focuses. This occurs in Blueprints and C++.

Upon further inspection, in UWidget::SetUserFocus(), note line 564-565:

TSharedPtr SafeWidget = GetCachedWidget();
if ( SafeWidget.IsValid() )

If SetFocus() is not delayed, IsValid() will return false. If it is delayed, then it returns true.

Is there a safe way of focusing during construction? I experimented with calling SetFocus() in a variety of virtual functions, but no success.

1 Like

Yes. this is an issue, and in c++ it will even crash the engine sometimes.
I ended up calling SetFocus() by the class that creates the widget, and it seems to work fine.

I love how it silently fails in a void returning method. It’s one of these things that will make you have to debug old code when it “randomly” starts to misbehave later in the project.

According to this post:

Something like this would work:

void USlateUtils::SetFocusOrDelayedFocus(APlayerController* InPlayerController, UWidget* InWidget) {
	if (!IsValid(InPlayerController) || !IsValid(InWidget)) {
		return;
	}

	InWidget->SetUserFocus(InPlayerController);
	if (!InWidget->HasUserFocus(InPlayerController)) {
		FLocalPlayerContext Context(InPlayerController);
		if (ULocalPlayer* LocalPlayer = Context.GetLocalPlayer()) {
			TOptional<int32> UserIndex = FSlateApplication::Get().GetUserIndexForController(LocalPlayer->GetControllerId());
			if (UserIndex.IsSet()) {
				LocalPlayer->GetSlateOperations().SetUserFocus(InWidget->GetCachedWidget().ToSharedRef());
			}
		}
	}
}

That is bloody ugly and messes with the next frame. Something tells me we can’t we just enforce the “SafeWidget” creation at that point without messing up something else. Thoughts?

Just make sure we set input mode before creating the UI:

UWidgetBlueprintLibrary::SetInputMode_GameAndUIEx(PlayerController);
PauseMenu = CreateWidget<UUserWidget>(PlayerController, PauseMenuClass.LoadSynchronous());
PauseMenu->AddToViewport();

That make SetFocus works from construct event for me in the blueprint.