Adding my own buttons to BPW_MapMenu
I'm looking for some help from somebody who knows about UI/widget modding. I am attempting to add some buttons to the BPW_MapMenu hierarchy. Specifically, the map menu has the "Show Stamps on Map" checkbox button at the bottom and I'm trying to add some of my own buttons alongside it. I've attached an unreal editor screenshot of the hierarchy I'm talking about, with the button I'm trying to create siblings for highlighted.
My approach has been to create a UVerticalBox containing my button (starting with one) and add it to the [Overlay] widget, so the vbox is itself a sibling to the mShowStampsButton. But nothing I added to the vbox shows up. I am inexperienced with UI programming, so I don't know if I'm doing something wrong or just falling into common pitfalls. Here's some code to show what I'm doing:
I looked at Cartograph's code to add these buttons next to the show/hide menu button using an HBox and that worked fine, so I know the buttons actually get created and work. It's getting them into the BPW_MapMenu hierarchy I need help with.

Solution:Jump to solution
To recap the solution, rather than piece together widgets in C++, I made the whole widget in UE and then used Bind on BP Function: Construct to find and insert the widget at runtime, but only because widget blueprint hooks didn't work (the widget only got inserted on the first map screen open... there's a quirk there that somebody might want to sort out... I'd do it if I had more time right now). Then I had to sort out button layout and alignment, which yeshjho helped with.
69 Replies
I highly recommend you do widget injection stuff like this in blueprint because the iteration time is way faster (you can even test in editor with most widgets, looks even better if you have a complete project dump) and because you won't need to juggle as many bp asset refs on the c++ side
it's possible to do in C++ as I assume cartograph is doing, but I don't think it's a good idea, especially because you mentioned you're inexperienced with UI programming - making the faster iteration time all that much more valuable
check out Mam Enhancer's source code for some examples of doing it in blueprint. it uses both the data-asset-hook method in the game instance module, and in blueprint event graphs for the more precise ones
a useful way to test stuff like this is to make a copy of the widget you're trying to modify in the editor, then try to actually make the change yourself in the widget editor. you can very quickly find out what kind of slot alignment and padding settings you need
and you can test your hooks in the editor by making an editor utility widget, adding a copy of the target widget to it, then calling your "Bind on BP Function: Construct" hook function with a reference to that widget
I recognize some of these words! j/k That’s super helpful and I’ll play around with that when I have some time later. I started by looking for an example of how to edit the map screen and I knew Cartogtaph did it so I started there. I DO feel like I’m really close since I can manipulate the widget tree - I just don’t understand what its final state needs to be.
You really want to use the widget editor in UE to know what the final state you want is
I would highly recommend widget blueprint hooks unless those won't cut it for you (in which case we might want to consider making the API more flexible?)
I dislike widget blueprint hooks because there's a ton of noise trying to follow them later (from the properties I don't care about) + they are "always on" (I can conditionally decide on hook trigger if I actually want to modify something). but they work for most purposes
You can alleviate the pain of iterating of using C++ by trying modifying the widget blueprint you're editing as Robb said.
I guess you're trying to wrap the
mShowStampButton
with a vertical box and add your button below it?
You could modify the widget blueprint and preview the result like this:
It might be hard to see, but the second button got outside of screen. So in this case, you should check
Size to Content
checkbox of the Overlay, and it'll look like this:
Apparently,
Image_195
's position doesn't depend on the overlay so you'll need to modify it manually in this case.Thank you, yeshjho! Even little pointers like "Size to Content" are super helpful for me. I had figured out how to move the image and resize the scrollbox because I wondered if the buttons needed space to be shown, but that hadn't worked so I'm sure it's a confluence of little things I'm messing up. I will play with the widget editor and see if I can work backwards from there.
Saw your code again, and tried to remember the time I was writing the UI code of Cartograph...
It might not be true, but IIRC,
You can't just
vbox->Slot = overlayWidget->Slot
, because a slot and the widget contained in the slot references each other and the parent referenced it somehow, I think. The point is, it was better to just create a new slot object. That's why I'm creating a new slot and manually setting the references with
and then inserting it back with
Thanks, yeah, I'm still playing with it but part of my gap was understanding what slots were even for and was having trouble finding a complete explanation. It eventually became clear that the slots are decoupling layout options from the widget type but that the slot type is determined by the containing widget because it's the containing widget that controls what layout options are possible. I'm still not positive whether slots are 1:1 with contained widgets (I think they are) but, instead of just setting a slot or making a new one, I tried configuring the slot returned by AddChild (or, for an Hbox, you can skip a cast with AddChildToHorizontalBox) and that seems to be working better - I can see the one button I'm adding now, though I have yet to sort out its full styling. So I think AddChild creates the slot for you, which I think makes some sense, as the containing widget needs a slot per widget and is the one that knows what slot type to use.
Aha! Getting closer:

Good that you figured out, yeah using the slot returned by AddChild would be a better solution.
You're right slots are 1:1 with the contained widgets. Widgets that can have multiple children widgets such as HBox, VBox, and Overlay have slots for their children, and a slot contains one child widget. You can see the slot-related data appearing at the top for the child widgets in the editor

In this screenshot, you can see that the buttons and labels are too tall and overlapping. The layout there is a vbox of hboxes and when I try wrapping the hboxes in sizeboxes and setting the height override, it works in the editor but not in-game. I was able to set the padding and text alignment on the "Resources on *" TextBlocks, but the sizeboxes don't seem to affect what I see there in-game. Here's what I configured in the editor just to prototype what I was trying to do:

It looks like this:

With all sizeboxes configured like so (though I've tried different alignments and Auto/Fill in both the editor and in-game - nothing seems to change behavior in this case)

For the actual code that's running in-game, here's how I'm creating the vbox:
And the first hbox row:
BTW, I'm not intentionally ignoring the advice to do this in blueprint. It's just that when I realized how close I was and how I should be using slots, I realized it would likely be more work to completely reset. The only serious hiccup I've hit since then is why SetHeightOverride (called at line 3 of the first hbox row C++) doesn't seem to do anything for me in C++. I tried setting it as low as 5.0 and it didn't seem to make a difference.
In case it matters, buttonTemplateWidget is currently nullptr (I was using a template before but it doesn't seem to be necessary to get the default styling I want). And CreateResourceVisibilityButton is pretty straightforward:
Where SRMResourceVisibilityButtonClass is just my own blueprint class that extends Widget_StandardButton per what Cartograph does to make its Show/Hide Cartograph Menu button.
you may still want to try and do it in blueprint to compare anyway as a learning experience, to see how the two approaches compare in length, ease of following the logic, and documentation obscurity
I know I dumped several walls of text there but buried in it was a question - does anybody know how to use sizebox to limit the height of a vbox row in C++? My approach in C++ above calling SetHeightOverride doesn't seem to be having any effect, even though I used what I thought was the same approach in a blueprint prototype and it DID work.
Other things in the layout can sometimes cause a size box height override to be ignored. That's just an observation I've made. I don't have anything more specific, sorry
Try changing its alignment within the slot
That's still helpful to know. I noticed that there was no vbox there at all before and my next experiment is to remove it and just put the widgets directly and see what happens.
Try to make as much of the the widget you're adding in blueprint as possible so you only have one element to add and one set of slots properties to worry about changing
Good call, that also makes sense
Especially because UIs can change across updates
Although that's less of a concern now that we're in full release land
Ooh seeing that you're putting your buttons above the vanilla one, not below it, it would have been much much simpler to do it with blueprint hooking, adding a widget under the overlay and manually adjusting the position. That's how I've added Cartograph's "Cartograph: Initializing..." text at the top right corner. I didn't use C++ for that. The only reason I've done that way to add the Show/Hide Cartograph menu button was that I needed that horizontal box because the width of the vanilla button changes depending on its state (the widths of "Show" and "Hide" are different). But I don't think that's the case for you, so manually adding would have alleviated the headache a lot 😅
Anyway, about the size box, the size box doesn't actually "sizes" its contents. It just changes the size it reports to its parent. If you want its child to automatically scale depending on the size(height) of the size box, you should use scale box.



without scalebox:

So I wrapped all the UI I want to add into a single widget whose tree looks like this:

Then I tried adding it to BPW_MapMenu using a widget blueprint hook configured like so:

And that appears to (mostly) work until the SECOND time I open the map screen - then the buttons are just gone. Is there something I'm doing wrong that they aren't being added every time?
Also, this is just a general layout question, but I can't seem to get the buttons truly centered in their hbox slot. They look like this:

Even though all of their slots are configured like this:

Now, the buttons themselves are custom buttons I made that extend Widget_StandardButton so could it be that the standard button is simply inherently not centered? And what can I do to center it?
The data hooks shoooould mean that they get added every time. But it really sounds like the hook just isn't triggering again. That's weird. My next step would be trying to do it with Bind on BP Function: Construct instead, since that can tell me if the function is actually getting called or not, and give me some kind of error reporting
Do you see anything in the logs?
There is a decent chance it is standard button's fault. Try with stock ue buttons and see if they have the same problem.
If it is standard button's fault, you can use code in your own widget subclass to change the button's layout to do what you need, but it will be some trial and error
What functionality did you need that required subclassing standard button?
From LogSatisfactoryModLoader? Not that I can tell
Just following what I saw done in Cartograph, but it also has appropriately-themed styling/hover behavior/slick button sounds built in
Are widget blueprint hooks supposed to log their behavior? Do I need to change a log level to see them?
Possibly try with plain ol standard button too then in case the subclassing breaks something
Ah, I've encountered it before. For me, I was adding a widget to
mMapObjectPanel
in Widget_Map
, but the widget disappeared the second time I open the map.
My guess was the game was detaching all the children and recreating the children every time(or the first time) I open the map, so I attached my widget to a different widget, in this case mMapScrollContainer
, and it didn't get disappear anymore.
Try different parent widget.Good to know, thanks. Unfortunately, there was only one parent widget available in BPW_MapMenu in the hook dropdown, but I will look into Bind on BP Function Construct as well as finding a different place for them.
You could choose any widget if you change the
Parent Widget Typ
to Direct(Any)
, but I guess it doesn't make sense to attach your widget to anywhere else since otherwise it'll hide other widgets... So yeah, going with Construct would be your best bet, I think.
How are you initializing the buttons? i.e., how are you setting the texts?
Horizontal alignment being centered there doesn't mean the children of the widget the slot contains are also centered horizontally.
It simply means the button's position inside the hbox is aligned at center.With this approach, I’m just setting the mText property in the editor UI.
You should use the
SetText
function of the Widget_StandardButton, since it'll deal with the visibility of the mLeftSlot
and the IconContainer
. Otherwise they'll remain visible even though they contain nothing, pushing the text to the right.
This one
Hmm, I thought that was going to work but no change. I even tried creating my own text property and passing it to SetText in the Construct event but it's not fixing the alignment at all.
Huh, I might try subsequently calling
SetTextStyle
and SetButtonAlignment(true)
too, then (or try calling one only). If it's still not centered after calling those 2 functions, I have no idea.Unfortunately, neither of those solved the button alignment, so I still need to figure that out. But I got the buttons added to the map menu regularly using Bind on BP Function: Construct... combined with a LOT more glue work than normal widget blueprint hooks.
I was able to sort of solve the button issues by wrapping them all in individual scale boxes. That centered them but also shrunk them a bit more than I'd like. Shouldn't there be some way to center the button but also hide/clip off the parts that would extend outside of the hbox cell?
Ah, yes, there it is and it was simpler than I realized. Just set the alignments to Center (NOT Fill) and Clipping to Clip to Bounds.
I just tested with a fresh subclass, simple SetText call should work, so I'm not sure what's going on there

But I guess that's all problem solved, right?
When did you call SetText?
In Construct

Curious - do you also have that in an hbox slot?
Yeah that's the only difference I noticed so I just tested it and it seems like it's fine

Is your HBox's
Size To Content
turned off?
That's the only way I can think ofI don't even see a
Size to Content
option on my HBoxes. But, here, let me describe what I did to get it to work. Here's the final result:
And here's the full widget tree:

The size boxes are set like so (with a height override of 36)

Text:

Scale boxes:

And buttons:

I had to "Clip to Bounds" on BOTH the scale boxes AND the buttons to get it to look right. I don't really know why; I tried without the scale boxes and it reverted to funky alignment.
Ah, I see what's going on now. First of all, since it works, don't touch anything 😉
If you want to know why...
- HBox and VBox have their own sizes, too. So if the size of the children gets bigger than its size, it'll clip their content. That was what's happening here: https://discord.com/channels/555424930502541343/1353175379455774811/1354168117038944438
- If you don't want the clipping and want only the alignment from the HBox/VBox, you can check the
SizeToContent
checkbox so their size gets dependent on their children.
- The SizeToContent
option disappeared here(https://discord.com/channels/555424930502541343/1353175379455774811/1354248987837861999) since you wrapped the HBox with a SizeBox, which has sized slots. Since the size of the HBox will now be determined by the slot, UE hides the size-related options from the details view.
- Your solution works because Instead of resizing the outer HBoxes, now you're resizing the inner ones(buttons) with ScaleBoxes.Ah, very interesting - thanks for the explanation! And for all the help and advice! I learned a lot doing this!
Solution
To recap the solution, rather than piece together widgets in C++, I made the whole widget in UE and then used Bind on BP Function: Construct to find and insert the widget at runtime, but only because widget blueprint hooks didn't work (the widget only got inserted on the first map screen open... there's a quirk there that somebody might want to sort out... I'd do it if I had more time right now). Then I had to sort out button layout and alignment, which yeshjho helped with.
Which function did you hook from C++ to inject the widget, that resulted in the hooked stuff no longer working the second time the widget gets opened?
That was just using WidgetBlueprintHooks on the instance module, no extra hooking.
Ah, so it's a bug with widget blueprint hooks? Would be nice to open an issue in the SML GitHub repo to not forget about it
Gonna be away for a few days but will try to remember when I get back.
GitHub
Widget Blueprint Hooks only seems to work the first time for some w...
Summary: in figuring out how to add custom buttons to BPW_MapMenu, I tried creating a custom widget and then inserting it into BPW_MapMenu using Widget Blueprint Hooks on my root instance module. T...