* Weak widgets present a piece of content without owning it.
* e.g. A tooltip is owned by the hovered widget but displayed
* by a floating window. That window cannot own the tooltip
* and must therefore use an SWeakWidget.
What makes the widget “weak”? What are some general use-cases for the SWeakWidget type? What are my other options?
When you create a compound widget that contains children, the child widgets are stored as shared pointers (TSharedPtr) inside your widget. Because shared pointers are reference counted, this effectively establishes an ownership relationship between the child widgets and your widget.
In other words: any object that keeps a shared pointer to the children owns the life time of the children.The child widgets will only be destroyed if no other object still has a reference to them. In particular, this means that the child widgets cannot be destroyed until your parent widget is destroyed.
In most cases this behavior is desirable, because we want to ensure that the children exist while our parent widget exists. In some cases, however, the behavior is not desirable, and we may not want our widget to ‘own’ the life time of a child widget, for example to avoid circular life time dependencies or to make sure that a widget’s life time is managed in a central place elsewhere. In those cases we would store the child widgets not in a TSharedPtr but in a TWeakPtr.
TWeakPtr does not affect the life time of the object that it points to. When the object goes away, the pointer simply becomes nullptr. The SWeakWidget widget is a Slate compatible convenience object that effectively wraps a TWeakPtr to an SWidget. The life time of a widget assigned to a SWeakWidget will not be affected by the life time of the SWeakWidget or the widget owning the SWeakWidget. If the child widget goes away, the SWeakWidget will simply be empty.
Am I wrong to assume that the reason we don’t give the Viewport a SharedPtr is because we don’t want it to be a form of “life support” for the widget it’s being given a “handle” on?
In this example MyUIWidget is a SCompoundWidget, the “master” widget so to speak, of which all other widgets are children. I guess that by “making sure the widget’s life time is managed in a central place elsewhere”, that is in this example, “elsewhere” is the HUD, which has a SCompoundWidget SharedPtr named MyUIWidget.
By keeping the Viewport from being a source of “life support” we make sure the widgets are dead when the HUD is dead (for example if we changed/killed the HUD from memory but kept the same Viewport), correct?
I think I understand your explanation, I just find it helpful to have a concrete example or two to solidify my understanding. Once I understand a what you’re saying within the context of an idiom or two I’ll not need to bugger you for input every time, so please correct me if I’m wrong. If you need some background on the above code, it’s part of my Hello Slate tutorial here.
I try to support the community when I can, if you do humor me the “buck won’t stop here.” I’m planning to update the Hello Slate tutorial soon, I had a hardware failure recently and it’s caused a bit of a setback. Luckily I was publishing all my research on this site so it’s all pretty well documented =)
I’m not sure what the intention was with that particular code, but I can tell you what it means: the lifetime of MyUIWidget does not depend on the lifetime of the game viewport. As soon as the last pointer to MyUIWidget is released (probably when the HUD object is being destroyed), MyUIWidget will be destroyed. At that point, the game viewport will hold an empty SWeakWidget. I guess the idea is to be able to destroy or recreate the HUD without having to worry about the viewport, but I’m not sure.
If you look at the implementation of UGameViewportClient you can see that the HUD widgets are actually being added to a SOverlay, so each time you call AddViewportWidgetContent() it adds a new slot. This is why I find the SWeakWidget approach problematic, because after a while you will end up with a bunch of empty overlay slots - they never get removed, unless you call RemoveViewportWidgetContent().
That being said, I was able to find this kind of code in our game code as well. Perhaps someone from our game teams can shed more light on this.
Someone suggested that SWeakWidget is being used here to support hot-reload of game modules, which seems plausible. Wrapping MyUIWidget in an SWeakWidget guarantees that the destruction of MyUIWidget is not being prevented by the viewport when reloading the game module.
This is actually my code, it’s inspired heavily by the RTSGame example, I just didn’t realize the gravity of a couple lines. I knew it was passing a Widget to the Viewport to be drawn, but I didn’t know why SWeakWidget was used and not say, TSharedPtr. But your explanation really brings it into focus.
And aye, that seems to be the case. The MyHUD class has a TSharedPtr to the MyUIWidget object, and should be the only one so if the HUD is destroyed so is everything associated with it.
Someone else suggested that the use of SWeakWidget helps decoupling the HUD widgets from the viewport. This can be important when running the game in the Editor (PIE), because then the viewport is the Editor, and you don’t want it holding on to game code when PIE is stopped.