Losing keyboard focus on mouse click

  1. I made a custom player controller, where I disabled “show mouse cursor”, click events, mouse over events, generally all mouse input options.
  2. I made UMG Huds in which I can navigate just by keyboard.
  3. But when I click left mouse button whenever the umg is active, it loses keyboard focus and therefore it’s not responsive. I have to close this umg and open it again to navigate.

So how can I completely disable mouse input in my game? Even when I do recommended steps, it’s still present and disrupts my design.

I have the same question.

This affects me too. In my case, I just don’t want my focused widgets to lose focus when the mouse clicks somewhere that shouldn’t be clickable.

my work around has been to check if all the buttons have user focus (making them into an array and then checking if each one has focus, and if none have any focus set it to the correct button) but this workaround takes long to implement on complex menu systems… seriously there has to be a better way

i’m doing the same exact thing. it is not a great work around, but i can’t find a better solution :’(

are you doing this for single player or local multiplayer, the reason i ask is if you want it for the local multiplayer the logic only needs to follow the 1st players buttons, becausr if he clicks and the focus is lost everyones fpcus is also lost so just reset everyones buttons to their default focus if player 1 loses focus (that way you only make an array of p1’s buttons…)

Fixed that doing some voodoo magic in overriding
virtual void NativeOnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent) override;

in UserWidgetEx.h

#pragma once

#include "Blueprint/UserWidget.h"
#include "UserWidgetEx.generated.h"



UCLASS()
class UNREALENGINEEX_API UUserWidgetEx : public UUserWidget
{
	GENERATED_BODY()


public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction", meta = (Bitmask, BitmaskEnum = "EFocusCause"))
	int32 PreventFocusChange;


public:
	UUserWidgetEx(const FObjectInitializer& ObjectInitializer);


protected:
	virtual void NativeOnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent) override;
};

in UUserWidgetEx.cpp

#include "UnrealEngineExPrivatePCH.h"
#include "UserWidgetEx.h"

#include "Framework/Application/SlateApplication.h"



UUserWidgetEx::UUserWidgetEx(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UUserWidgetEx::NativeOnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent)
{
	if (((PreventFocusChange & 1 << InFocusEvent.GetCause()) != 0) && PreviousFocusPath.GetLastWidget() == TakeWidget())
	{
		int32 UserIndex;
		FSlateUser* SlateUser = nullptr;
		FSlateApplication::Get().ForEachUser([UserIndex, &SlateUser](FSlateUser* User) { if (UserIndex == User->GetUserIndex()) SlateUser = User; });

		if (SlateUser)
		{
			SlateUser->UpdateFocusVersion();
			return; // success on preventing focus switch.
		}
	}
	
	Super::NativeOnFocusChanging(PreviousFocusPath, NewWidgetPath, InFocusEvent);
}

subclass your widgets from this class. and set PreventFocusChange to Mouse

I tried this approach in 4.25. Notes:

  • UpdateFocusVersion() has been renamed to IncrementFocusVersion()
  • IncrementFocusVersion() is now a protected function (only accessible from within the slate module) so it can’t be called from within custom user widgets.
  • I got around this by implementing NativeOnFocusChanging() in my custom button-containing user widget and calling SetFocus() on the button when (InFocusEvent.GetCause() == EFocusCause::Mouse). This will ensure that the focused button doesn’t lose focus when clicking elsewhere on the screen.

In regards to focus being lost when clicking, Epic should really make this into a configurable project setting so that people don’t have to go through the extra legwork to achieve the desired behavior. Looking at other threads, most people seem to be implementing really ugly hacks to get this working correctly.

2 Likes

Thank you both for this, super helpful!

In my specific instance I went with the following solution:

BaseUserWidgetFocusHacked.h:

UCLASS()
class EXAMPLE_API UBaseUserWidgetFocusHacked : public UUserWidget
{
	GENERATED_BODY()

protected:

#if PLATFORM_DESKTOP || WITH_EDITOR
	// This is only relevant on platforms with mouse support
	virtual void NativeOnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent) override;
#endif

};

BaseUserWidgetFocusHacked.cpp:

#if PLATFORM_DESKTOP || WITH_EDITOR
// This is only relevant on platforms with mouse support
void UBaseUserWidgetFocusHacked::NativeOnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent)
{
	if (PreviousFocusPath.IsValid() && NewWidgetPath.IsValid())
	{
		if (InFocusEvent.GetCause() == EFocusCause::Mouse && PreviousFocusPath.GetLastWidget() == TakeWidget() && NewWidgetPath.GetLastWidget()->GetType() != FName("UBaseUserWidgetFocusHacked"))
		{
			SetFocus();
			return;
		}
	}
	Super::NativeOnFocusChanging(PreviousFocusPath, NewWidgetPath, InFocusEvent);
}
#endif

I had already setup every single button in the game to subclass a specific UserWidget blueprint, so all I had to do was to change that blueprint to subclass this class.

Basically all it does is detect whether or not the mouse-driven focus change would switch from a widget of this type to any other kind of widget. Since all my user focusable widgets subclass this class this works just fine.

Tested in 4.25.

Much appreciated these posts! Also using 4.25 and ran into the same issue. First had an ugly hack where I had an invisible element covering the whole background and would refocus a button when clicked. This is really annoying to maintain on every UI page though and always focuses the same button rather than the most recently selected.

Some notes on my approach, which may help others:

The way I have my UI set up the GetLastWidget()->GetType() would always return the class “SViewPort” when clicking outside any UI element, so I just check for that instead. Probably because I set every element visibility without interaction to “not hit testable”. Reparent all widgets which have clickable elements in them to the new subclass and you are good to go. Your mileage may vary though.

If you get any unresolved symbols error when building after adding the NativeOnFocusChanging override (like I had), make sure you enabled Slate and SlateCore in your build.cs file.

How did you manage to get a custom type in your widgets?

1 Like

I think its still relevant for many developers I found a solution and verified that it works in production. Hopes it will save a lot of time for somebody