Send bool from widget to character

In short, I want to press a button in my widget which enters a constructionmode, in constructionmode my character will linetrace and spawn the selected block into the world. The linetracing and spawning works. I need help with sending a boolean from my SCompoundWidget to my ACharacter so that the linetracing can start. As I understand I should use declare delegate. But there is so little information about it, been stuck for days.

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "SlateBasics.h"
#include "SlateExtras.h"

DECLARE_DELEGATE_OneParam(FBuildingClickedSignature, bool, EnteredConstructionMode);

class MyGame_API SConstructionWidget : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SConstructionWidget){}
	SLATE_ARGUMENT(TWeakObjectPtr<class AMenuHUD>, OwningHUD)
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs);

	FReply OnBuildingClicked() const;

	UPROPERTY(BlueprintAssignable)
	FBuildingClickedSignature OnBuildingClicked;

	TWeakObjectPtr<class AMenuHUD> OwningHUD;

	virtual bool SupportsKeyboardFocus() const override { return true; };};

In my cpp I basically have:

FReply SConstructionWidget::OnBuildingClicked() const
{
	if (OwningHUD.IsValid()) {
		//bool EnteredConstructionMode = true;
	}
	return FReply::Handled();
}

Widgets are not UObjects so you can’t use the UPROPERTY or UFUNCTION macros here.

You should take a look at SCheckBox (Slate) and UCheckBox (UObject)

First you Declare the delegate which you have done although you don’t have to name the bool parameter.

Then you declare the Delegate but without the UPROPERTY macro.

Then you pass the delegate as an argument to the slate constructor using the initializer list.

SLATE_BEGIN_ARGS(SConstructionWidget) : _OnBuildingClicked() {}

In the Construct implementation you assign the slate argument to your delegate member variable.

OnBuildingClicked = InArgs._OnBuildingClicked;

You then need to execute the delegate whenever you like with the appropriate value.

OnBuildingClicked.ExecuteIfBound(true);

The final task is to bind a UObject callback function to this delegate possibly in your Character class.

First you create the callback function that takes a bool as a parameter.

void SlateOnBuildingClickedCallback(bool EnteredConstructionMode);

Then when you create an instance of your widget you bind your callback functions to the widget delegate using the macro BIND_UOBJECT_DELEGATE

MyConstructionWidget = SNew(SConstructionWidget)
		.OnBuildingClicked( BIND_UOBJECT_DELEGATE(FBuildingClickedSignature, SlateOnBuildingClickedCallback) )

Again I recommend you look at SCheckBox (Slate) and the UCheckBox (UObject) code if you get lost.

I hope this helps.

1 Like

Hi, thank you so much. I have tried for the past 4 hours now, but I can’t solve it. I’m looking at different tutorials on docs.unrealengine, benui, forums.unrealengine, chatGPT and old forum posts. All have hints, all seemingly doing it different. I just can’t combine the hints to any logic.

This is what I managed to conjure, doesn’t work.

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "SlateBasics.h"
#include "SlateExtras.h"

DECLARE_DELEGATE_OneParam(FOnBuildingClickedDelegate, bool);

class MYGAME_API SConstructionWidget : public SCompoundWidget
{
public:

	SLATE_BEGIN_ARGS(SConstructionWidget) 
		: _OnBuildingClickedDelegate() 
	{}
	SLATE_ARGUMENT(TWeakObjectPtr<class AMenuHUD>, OwningHUD)
	SLATE_EVENT(FOnBuildingClickedDelegate, OnBuildingClickedDelegate)
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs);
	void HandlecanConstruct(bool EnteredConstructionMode) const;
	FReply OnBuildingClicked() const;

	FOnBuildingClickedDelegate OnBuildingClickedDelegate;

	TWeakObjectPtr<class AMenuHUD> OwningHUD;

	virtual bool SupportsKeyboardFocus() const override { return true; };};

And then the cpp file (the important parts):

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SConstructionWidget::Construct(const FArguments& InArgs)
{
	bCanSupportFocus = true;

	OwningHUD = InArgs._OwningHUD;
	OnBuildingClickedDelegate = InArgs._OnBuildingClickedDelegate;
	OnBuildingClickedDelegate.BindSP(this, &SConstructionWidget::HandlecanConstruct);
//...
//Building Button
				+ SVerticalBox::Slot()
					.Padding(ButtonPadding)
					[
						SNew(SButton)
						.OnClicked(this, &SConstructionWidget::OnBuildingClicked)
//...
}

FReply SConstructionWidget::OnBuildingClicked() const
{
	if (OwningHUD.IsValid()) {
		HandlecanConstruct(true);
	}
	return FReply::Handled();
}
void SConstructionWidget::HandlecanConstruct(bool EnteredConstructionMode) const
{
	OnBuildingClickedDelegate.ExecuteIfBound(true);
}

For my character whom is trying to receive the bool I have a function:

	void canConstruct(bool EnteredConstructionMode);

And then in my character cpp I have:

void AMyCharacter::canConstruct(bool EnteredConstructionMode){
	SConstructionWidget* ConstructionWidget = GetOwner<SConstructionWidget>();
	ConstructionWidget->OnBuildingClickedDelegate.Add(this,
		&AMyCharacter::FindConstructionSite);
}

Here you are binding the delegate to a function which also executes the delegate?
The point of the delegate is that it can trigger a callback function when the delegate is being executed but here you trigger the delegate when it is being triggered creating an infinite loop.

Here you are getting the owner of the Character and expecting it to be of type SConstructionWidget? The owner of a character should never be a widget and it will always fail.
Instead you should keep the shared pointer to the widget wherever it is created. If you are instantiating the widget within the Character then it also makes sense to keep the shared pointer here.

Character Declaration

TSharedPtr<SConstructionWidget> MyConstructionWidget;

Character Definition

MyConstructionWidget = SNew(SConstructionWidget)
		.OnBuildingClicked( BIND_UOBJECT_DELEGATE(FBuildingClickedSignature, SlateOnBuildingClickedCallback) )

The OnBuildingClicked function and binding to the OnClicked SButton event looks fine. You just need to remove the BindSP from within the SConstructionWidget class and add a binding to the Widget instance from the Character class instead.

I gave up for I while because I couldn’t solve it. I’m back again to see if I can find a solution this time (6h coding today and nothing, kinda sad).
I have both tried to follow your instruction as close as possible, as well as other tutorials. I moved the binding into the constructor of MyCharacter. I can’t find any information about the macro “BIND_OBJECT_DELEGATE”? it has error “identifier is unidentified” and everything after is marked in red.

	TSharedPtr<SConstructionWidget> MyConstructionWidget;
	MyConstructionWidget = SNew(SConstructionWidget).OnBuildingClickedDelegate(BIND_UOBJECT_DELEGATE(FBuildingClickedSignature, SlateOnBuildingClickedCallback));

I have also tried BindUObject. But nothing happens when the button is pressed (it’s not bound)

	TSharedPtr<SConstructionWidget> MyConstructionWidget = SNew(SConstructionWidget);
	MyConstructionWidget->OnBuildingClickedDelegate.BindUObject(this, &AMyCharacter::HandleOnBuildingClicked);

lastly I tried CreateUObject. Since what you described earlier is that SCompoundWidget is not an UObject. It didn’t work.

	TSharedPtr<SConstructionWidget> MyConstructionWidget = SNew(SConstructionWidget);
	MyConstructionWidget->SetOnBuildingClickedDelegate(FOnBuildingClickedSignature::CreateUObject(this, &AMyCharacter::HandleOnBuildingClicked));

Within MyConstructionWidget I have code as follow. I Don’t know what you mean by callback function, I tried two different ways. But neither worked:

DECLARE_DELEGATE_OneParam(FOnBuildingClickedSignature, bool /*EnteredConstructionMode*/);

class MY_API SConstructionWidget : public SCompoundWidget
{
public:

	SLATE_BEGIN_ARGS(SConstructionWidget)
		: _OnBuildingClickedDelegate()
	{}
	SLATE_ARGUMENT(TWeakObjectPtr<class AMenuHUD>, OwningHUD)
	SLATE_EVENT(FOnBuildingClickedSignature, OnBuildingClickedDelegate)
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs);

	FReply OnBuildingClicked() const;
	FReply OnReturnClicked() const;

	void SlateOnBuildingClickedCallback(bool EnteredConstructionMode);

	//void SetOnBuildingClickedDelegate(FOnBuildingClickedSignature Callback) {
	//	OnBuildingClickedDelegate = Callback;
	//}
	
	FOnBuildingClickedSignature OnBuildingClickedDelegate;

	TWeakObjectPtr<class AMenuHUD> OwningHUD;

	virtual bool SupportsKeyboardFocus() const override { return true; };
};

In the cpp file I have combined previous function into one. When pressing my building button I only get the log “Building button is pressed”. So the binding doesn’t work.

FReply SConstructionWidget::OnBuildingClicked() const
{
	if (OwningHUD.IsValid()) {
		UE_LOG(LogTemp, Warning, TEXT("Building button is pressed"));
		if (OnBuildingClickedDelegate.IsBound()) {
			UE_LOG(LogTemp, Warning, TEXT("Is Bound"));
			OnBuildingClickedDelegate.Execute(true);
			OwningHUD->RemoveAllMenus();
		}
	}
	return FReply::Handled();
}