I know the answer has something to do with delegates, but I have no idea what they are or how to set them up, and the documentation is pretty much nonexistant. Can someone point me in the right direction?
Here’s a similar question that was about setting up an SImage dynamically. The principle is the same, except your function would return FText rather than const FSlateBrush*: Manipulating Slate content during and after Construct() - UI - Epic Developer Community Forums
// Inside Construct
SNew(STextBlock).Text(this, &SMyWidget::GetText)
// Inside your widget class
FText SMyWidget::GetText() const
{
return MyText;
}
Well that helps enough, and answers my original question, but it doesn’t seem like I’m able to jump out of MyWidget with it. What do I have to do to get text from an actor elsewhere in the world, pass the text to the HUD, then go from MyWidget to HUD to get it?
Well a delegate is really just a bound function; it doesn’t have to use a function from within your widget, it can come from outside if you need it to.
Alternatively you could provide the actor you want to show the text for to your widget as a SLATE_ARGUMENT, and then use that inside the GetText function, or you could have GetText look up the actor from the world somehow (however bear in mind that GetText is called every frame).
From an encapsulation standpoint, it’s better to keep things local where possible, to improve modularity and avoid unexpected dependencies, but if you can write a function to do it, you can certainly provide that function to a widget as a delegate.
So a delegate is a concept rather than an actual… thing?
A delegate is a distinct type in C++, but it’s a type that can be used to bind and call any static or member function which matches the signature of the delegate.
We also have a TAttribute type, which can either hold a literal value, or a delegate which returns the correct type of value. Slate makes extensive use of these via its SLATE_ATTRIBUTE macro; the Text parameter to STextBlock is actually a TAttribute<FText>, which is why it can accept the function returning FText as shown above.
The following documentation provides more information about how delegates work in UE4: Delegates and Lamba Functions in Unreal Engine | Unreal Engine 5.1 Documentation
Are there any simple examples of delegate use out there? I’ve been poking around the ShooterGame code for a while now, but it’s still not clicking. Any plans to release some code examples in the same style as the blueprint content examples?
We use delegates extensively in UE4, none of them are really any more complex than the others. The example given in that documentation is about as simple as it gets.
They’re not too dissimilar to the C++11 std::function (if you’ve ever used, or are familiar with those); if I were to take the example from this documentation on std::function and translate it to use UE4 delegates, it would look like this:
struct Foo
{
Foo(int num) : num_(num) {}
void print_add(int i) const { /* do print */ }
int num_;
};
void print_num(int i)
{
/* do print */
}
void TestPrintDelegates()
{
// declare our delegate types (these are really just a typedef internally
DECLARE_DELEGATE(FReturnVoidDelegate);
DECLARE_DELEGATE_OneParam(FReturnVoidTakeIntDelegate, int);
// store a free function
FReturnVoidTakeIntDelegate f_display = FReturnVoidTakeIntDelegate::CreateStatic(&print_num);
f_display.Execute(-9);
// store a lambda
FReturnVoidDelegate f_display_42 = FReturnVoidDelegate::CreateStatic([]() { print_num(42); });
f_display_42.Execute();
// store the result of a call to with binding
FReturnVoidDelegate f_display_31337 = FReturnVoidDelegate::CreateStatic(print_num, 31337);
f_display_31337.Execute();
// store a call to a member function
// CreateRaw is used here because we're pretending the stack object is a pointer; this isn't very safe
Foo foo(314159);
FReturnVoidTakeIntDelegate f_add_display = FReturnVoidTakeIntDelegate::CreateRaw(&foo, &Foo::print_add);
f_add_display.Execute(1);
}
I had to remove some of those examples because UE4 delegates can’t bind to non-static member functions without an instance, and they also can’t bind to callable objects (with operator()).
In the context of Slate though, you’ll mostly come across delegates via SLATE_ATTRIBUTE,eg, this code from SEditableTextBox:
SLATE_ATTRIBUTE( FText, Text )
As I described above, this is a TAttribute internally, and can take either a literal value of FText, or a delegate that returns an FText.
The other place you’ll see delegates in Slate is with SLATE_EVENT, once again demonstrated with some code from SEditableTextBox:
SLATE_EVENT( FOnTextChanged, OnTextChanged )
In this case FOnTextChanged is an existing delegate type that takes an FText parameter, and is used to broadcast text changes to things that may be using an SEditableTextBox:
DECLARE_DELEGATE_OneParam( FOnTextChanged, const FText& );
As an example of using both of these (remember that the delegate can be anywhere, but it’s in the widget class here for convenience).
// Inside Construct
SNew(SEditableTextBox)
.Text(this, &SMyWidget::GetText)
.OnTextChanged(this, &SMyWidget::OnTextChanged)
// Inside your widget class
FText SMyWidget::GetText() const
{
return MyText;
}
void SMyWidget::OnTextChanged(const FText& InText)
{
MyText = InText;
}