Capturing mouse input in a Slate widget

Hey everyone.

I’m having an issue where I’ve hooked up Slate to my HUD class and got a widget to render.

This widget consists of a TextBlock and an SImage, side-by-side.
I want the SImage to act more or less like a button, so when clicked it will expand a submenu and the SImage will change appearance.

Here’s the Construct code for the SImage:

 + SHorizontalBox::Slot()
	.AutoWidth()
	[
		SAssignNew(ToggleButton, SImage)
		.Image(&PlanetStatusStyle->ToggleButtonCollapsed)
		.OnMouseButtonDown(FPointerEventHandler(ToggleClickEvent))
	]

As you can see, I’ve hooked up an event delegate that calls a function on trigger.
Here’s the declaration:

TBaseDelegate_RetVal_TwoParams<FReply, const FGeometry&, const FPointerEvent&> ToggleClickEvent;

And here’s the function:

FReply SPlanetStatusWidget::OnToggleClick(const FGeometry& Geometry, const FPointerEvent& Event)
{
	// Toggle dropdown menu visibility.
	MenuVisibility = MenuVisibility == EVisibility::Collapsed ? EVisibility::Visible : EVisibility::Collapsed;

	// Notify UE event system that event was handled.
	return FReply::Handled();
}

I’ve bound the function to the delegate like so:

ToggleClickEvent.BindRaw(this, &SPlanetStatusWidget::OnToggleClick);

Now with all this out of the way, I was hoping I’d get a triggered breakpoint inside the function when the image was clicked. Alas, no luck. So I studied on, mainly in StrategyGame sample.
I found out that there was a virtual function returning whether or not keyboard focus is supported by the widget. As far as I understand keyboard and mouse focus goes hand-in-hand (and I didn’t find anything related to mouse focus), so I made an override of that and returned true.

Still nothing… My face is now starting to adopt a red/blue hue.
So I plowed on - found, inside the playercontroller, that there are a few bools there that could be related to it.
So I set bEnableClickEvents to true and bEnableTouchEvents to true.

Still nothing… And, frankly, now I’m out of ideas…

So if anyone have any input, I’d love to hear it!

Thanks in advance guys - firstly for taking the time reading this wall of text and secondly for any useful info :smiley:

Hey, I haven’t code the code in front of me at the moment, but it looks like you want to bind the on mouse button down to a delegate that invokes SPlanetStatusWidget::OnToggleClick.

If so, I think this code should do what you want:

SAssignNew(ToggleButton, SImage)
.Image(&PlanetStatusStyle->ToggleButtonCollapsed)
.OnMouseButtonDown(this, &SPlanetStatusWidget::OnToggleClick)

(Assuming ‘this’ is an SPlanetStatusWidget*)

With regard to attribute binding, you can just do: OnMouseButtonDown(this, &SPlanetStatusWidget::OnToggleClick)

Hi both, and thanks for your answers!

That’s more or less exactly what I’ve done, though I’ve used an iseparate delegate instead…

Just to make sure that wasn’t the issue, I’ve now tried your method of just passing the function as a binding, without the separate delegate, but it isn’t triggered.

I’m wondering what I could’ve missed?

I noticed that if I hold down the left mouse button over the image, the rest of the scene stops updating/rendering.

Given you want something that behaves like a button, is there a reason you don’t just use an SButton with an SImage as the content?

You can set the button style to “NoBorder” from FCoreStyle if you don’t want the gradient on the button .ButtonStyle(FCoreStyle::Get(), “NoBorder”)

Hi Jamie,

no there’s no real reason, I’m just still trying to get my bearings with Slate and didn’t want to overcomplicate things by having to fight a default button layout as well as trying to get functionality to work - but as you said, I may have overcomplicated things by NOT going that direction.

I will try using a button instead and see how it works out :slight_smile:

If you want something to act like a button you should use an actual button widget. The button widget’s content and style can be completely customized to get the look you desire.

You’ll want something like the following.

	   SNew( SButton )
		.ContentPadding(0)
		.OnClicked( this, &SPlanetStatusWidget::OnToggleClicked )
		.ButtonStyle( FCoreStyle::Get(), "NoBorder" )
		[
			SNew(SImage)
			.Image( &PlanetStatusStyle->ToggleButtonCollapsed )
		]


        FReply SPlanetStatusWidget::OnToggleClicked ()
        {
            // Toggle dropdown menu visibility.
            MenuVisibility = MenuVisibility == EVisibility::Collapsed ? EVisibility::Visible : EVisibility::Collapsed;
	        return FReply::Handled();
        }

Well I just tried replacing the SImage with a button and there’s no difference - the result is exactly identical.
The function isn’t called, and when holding down the mouse button over the button, it just stops the rendering/update.

One thing I did notice though, is that the hover and clicked brushes aren’t being shown either, so it’s like the mouse cursor is completely ignored by the widget.

Hi Sarge, and thanks for your input!

I’m having somewhat of a problem forcing the no border style through since I’m grabbing styles from a style set.

My code for getting the style looks like this:
.ButtonStyle(&PlanetStatusStyle->ToggleButtonStyle)

If I were to “merge” the grabbed style from the editor’s WidgetStyle with the no border, how would I go about that?

Well you have a couple options. One super easy one is to consider whether or not being able to style this particular aspect of your UI is important. If it isn’t then referencing FCoreStyle in the way i did above is perfectly legit.

Another option would be to create your own version of “NoBorder” in your style.

const FButtonStyle NoBorder = FButtonStyle()
	.SetNormal(FSlateNoResource())
	.SetHovered(FSlateNoResource())
	.SetPressed(FSlateNoResource())
	.SetNormalPadding(FMargin(0,0,0,1))
	.SetPressedPadding(FMargin(0,1,0,0));

Style->Set( "NoBorder", NoBorder );

This has the benefit of not being dependent on FCoreStyle, so if in the future FCoreStyle isn’t always around all your stuff will continue to work.

Okay, I feel severely stupid now…

I just remembered that it was stated in the docs that Slate will not work in PIE - it needs to be launched in a standalone for Slate functionality for work.

So I started the thing in a standalone game and lo and behold - it works!

Sorry to have wasted so much of your guys time because I can’t think worth a penny :frowning:

Thank you to you all for all your help!