[4.10] Full screen widget not forwarding input

I have a fullscreen SWidget added as a child to game’s viewport that’s transparent everywhere and all of its mouse methods return FReply::Unhandled. Nevertheless, mouse events don’t bubble up to the game viewport and because of that ingame objects are not getting notified when the mouse hovers over them. More precisely, they only receive ActorBeginCursorOverlap and ActorEndCursorOverlap when they get clicked and they never receive ActorOnClicked. What baffles me is that the exact same project works perfectly fine in 4.9 and I could not find anything in the patch notes regarding the problem. Looking forward to your suggestions.

Here’s the relevant code:

class MyWidget : public SCompoundWidget
{
FReply SMyWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	return FReply::Unhandled();
}
FReply SMyWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	return FReply::Unhandled();
}
FReply SMyWidget::OnMouseButtonDoubleClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	return FReply::Unhandled();
}
FReply SMyWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	return FReply::Unhandled();
}
FReply SMyWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	return FReply::Unhandled();
}
};

// Later during initialization
MyWidget = SNew(SMyWidget).Owner(this);
if (GEngine->IsValidLowLevel())
{
    GEngine->GameViewport->AddViewportWidgetContent(SNew(SWeakWidget).PossiblyNullContent(MyWidget.ToSharedRef()));
}

And the blueprint event handlers attached to a simple actor:

Has any solution been found for this? I am having the same problem.

Sadly, no. We’ve also contacted Epic in the UDN but no solution has been found. I’ll let you know once we have more information.

Hello NikolaDimitroff-

As mentioned on the UDN post, this issue has been reported as UE-25033 for investigation.

Cheers

Hello NikolaDimitroff,

I have a potential workaround that may be viable depending on your needs. There was another user with a similar issue. I have provided a link below. I hope that this information helps.

Link: UMG eats all of our double-click events - Programming & Scripting - Epic Developer Community Forums

Make it a great day

Hello again and let me apologize for the incorrect tag - the problem has appeared in 4.9, not in 4.10 and here’s a detail description of a hacky workaround:

The breaking change was introduced in 4.9 and is the following:

Prior to 4.9 APlayerController::TickPlayerInput looked like this:

...
if (LocalPlayer->ViewportClient->GetMousePosition(MousePosition))
{
    bHit = GetHitResultAtScreenPosition(MousePosition, CurrentClickTraceChannel, true, /*out*/ HitResult);
}

UPrimitiveComponent* PreviousComponent = CurrentClickablePrimitive.Get();
UPrimitiveComponent* CurrentComponent = (bHit ? HitResult.Component.Get() : NULL);

UPrimitiveComponent::DispatchMouseOverEvents(PreviousComponent, CurrentComponent);

Post 4.9 the code is now:

// Only send mouse hit events if we're directly over the viewport.
if ( ViewportClient->GetGameViewportWidget().IsValid() && ViewportClient->GetGameViewportWidget()->IsDirectlyHovered() )
{
    if ( LocalPlayer->ViewportClient->GetMousePosition(MousePosition) )
    {
        bHit = GetHitResultAtScreenPosition(MousePosition, CurrentClickTraceChannel, true, /*out*/ HitResult);
    }
}

UPrimitiveComponent* PreviousComponent = CurrentClickablePrimitive.Get();
UPrimitiveComponent* CurrentComponent = (bHit ? HitResult.Component.Get() : NULL);

UPrimitiveComponent::DispatchMouseOverEvents(PreviousComponent, CurrentComponent);

This is a side-effect of a change listed in the release notes of 4.9:

We no longer ignore hit testing in APlayerController::GetHitResultAtScreenPosition
if the mouse isn’t directly over the viewport. That function can be used
regardless of the state of the mouse, so that logic has been moved elsewhere.

The change to APlayerController::TickPlayerInput is to accomodate for the fact that
APlayerController::GetHitResultAtScreenPosition no longer ignores hits outside the viewport.
Problem is, the new test is not equivalent to the previous one. Whereas the old one used
ULocalPlayer::CalcSceneView which checks whether the mouse coordinates are inside the viewport, the new one
(ViewportClient->GetGameViewportWidget()->IsDirectlyHovered()) instead checks whether the
viewport is the topmost widget under the mouse.

I was able to solve the issue by changing:

bool FSlateApplication::IsWidgetDirectlyHovered(const TSharedPtr<const SWidget> Widget) const
{
	for( auto LastWidgetIterator = WidgetsUnderCursorLastEvent.CreateConstIterator(); LastWidgetIterator; ++LastWidgetIterator )
	{
		const FWeakWidgetPath& WeakPath = LastWidgetIterator.Value();
		if( WeakPath.IsValid() && Widget == WeakPath.GetLastWidget().Pin() )
		{
			return true;
		}
	}
	return false;
}

to

bool FSlateApplication::IsWidgetDirectlyHovered(const TSharedPtr<const SWidget> Widget) const
{
	for( auto LastWidgetIterator = WidgetsUnderCursorLastEvent.CreateConstIterator(); LastWidgetIterator; ++LastWidgetIterator )
	{
		const FWeakWidgetPath& WeakPath = LastWidgetIterator.Value();
		if( WeakPath.IsValid() && 
			( Widget == WeakPath.GetLastWidget().Pin() ||
			  WeakPath.GetLastWidget().Pin()->GetTypeAsString() == "MyWidget") )
		{
			return true;
		}
	}
	return false;
}

This way checking if a widget is on top will return true if either it, or the invisible
widget is on top. This introduces an inconsistency as the function will return
true for multiple widget but generally having my widget on top is the same
as having none at all so it shouldn’t matter.

Besides, searching for it in the engine’s source revealed that this is its only usage
(although it’s ■■■■■ and clients may call it in other places)

Another working solution is to remove the check from PlayerController::TickPlayerInput:

// Change line 3842 in PlayerController.cpp as of 4.11 from
if ( ViewportClient && ViewportClient->GetGameViewportWidget().IsValid() && ViewportClient->GetGameViewportWidget()->IsDirectlyHovered() )
// to
if ( ViewportClient && ViewportClient->GetGameViewportWidget().IsValid())

Is there an update on when this issue will be fixed?

Unfortunately this seems to not be working anymore in UE 4.21. I’ve got the same setup:
An SWidget in the game’s viewport that’s transparent and returns FReply::Unhandled, plus the fix from above that makes the IsWidgetDirectlyHovered check pass.
What happens currently is that my in-world UMG widget is not receiving any mouse events (neither ActorBeginCursorOverlap nor ActorEndCursorOverlap seem to work).
Is there a change that could’ve introduced this behaviour? Do you have any suggestions as to how to fix this?