After a very long several hours pounding my head into the desk I found a workaround. There are a few of these questions floating around with some moderately useful hints but I’m going to give you the step by step for anyone else that has the same issue I ran into (and will likely make this a youtube channel lesson once I am done with my milestone because this one was pretty high on the irritating scale)
Images that you want to block
This example is from my HUD blueprint. Here we have a UUserWidget I call a UUnitPowersDialWidget. What I have gathered is that only a scant few UMG elements actually will block the mouse despite what properties you set or what lies the editor tells you about visibility. (that sneaky editor)
Here is the hierarchy for the dial:
Dial Image is at the bottom of that stack in the overlay so gets drawn last. As such it will be the first thing to get hit by the mouse cursor. So I need to make this thing block the mouse on click. If you do this on say “UnitImage”, it won’t work. Its like after it hits that first item if that did not block it, it just zooms right on through your UI element. At least - thats what it was doing for me. Anyhow I digress.
DialImage is made a variable. Now lets look in my C++ header file for this widget:
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "UnitPowersDialWidget.generated.h"
/**
* Dial showing the unit from a render image as well as any powers that are linked to the unit can be fired here.
*/
UCLASS()
class MyGame_API UUnitPowersDialWidget : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
protected:
/* Image containing the dial's frame - also the topmost item to block mouse events from */
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
class UImage* DialImage;
UFUNCTION()
FEventReply ImageOnButtonDown(FGeometry MyGeometry, const FPointerEvent& MouseEvent);
};
I need to have the bound widget DialImage (note its BindWidget directive. This means that there has to be an Image control called DialImage on the widget in the editor or it will throw an exception.
I also am overriding Native Construct as well as creating a function that is going to handle the OnMouseDown event of the image.
Now in the cpp:
#include "UI/UnitPowersDialWidget.h"
#include "Components/Image.h"
void UUnitPowersDialWidget::NativeConstruct()
{
Super::NativeConstruct();
// IMPORTANT
// The UnitOrdersImage is being used because it is the highest image in the hierarchy. Trapping Mouse Button Down below this hierarchy results in nothing
// The goal is to catch the event and handle it so the mouse does not continue below the UI element.
DialImage->OnMouseButtonDownEvent.BindUFunction(this, "ImageOnButtonDown");
}
FEventReply UUnitPowersDialWidget::ImageOnButtonDown(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
{
return FEventReply(true);
}
All this does is grab the mouse down event (the only thing that the image traps for without going into the engine and modifying it yourself) and then just handle it.
The end result is that this works great and clicking on the unit portrait no longer fires through that widget into the ground below.
A more complicated scenario
This is an image on an image with UButtons on top of it. Buttons work out of the box… except for when they are disabled.
When they are disabled they do what every other element does…your click fires right through them causing the same problem.
I couldn’t find a way around this at all so I had to basically not use the ability to disable the button and instead its always enabled, but I have code that swaps the image out for a disabled image when its “disabled” and my C++ will handle what it should do on disabled (nothing, just return out).
I hope that this could save you a few hairs and the sanctity of your work station without head to desk over and over for a few hours.
Cheers.
EDIT: as an extension to illustrate just how silly this can get:
Here is another of my HUD widgets that I need to block the mouse for. The background image is set to now block mouse down as shown above and that works great, until the user clicks on any of the labels (and this thing will be full of labels - its kind of empty right now).
The thing is textblocks do NOT have a mouse down event… so you have to basically arrange a set of empty transparent images around your control that block the mouse event. For this example I’m looking at one image over the stats box, but then because I have a button that needs clicked in the top box with Proto Infantry and Medium Infantry, I’m going to have to create a series of images that leave the button exposed to click.
Alternatively which is what a lot of bigger studios will do - they will get into the engine itself and create a mouse down for the textblock or will find a way to rig these elements to block mouse on a channel so that your controllers can do a raycast and see if they hit UI elements and if so - bounce out.
Sadly - I am not yet at that level where I can easily do something like that as my project is not working with the engine source but thats just another aside thrown out there for you to consider should you run into this issue.