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.
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.
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?