Description
What problem does this solve or what need does it fill?
As we move towards a more reactive UI framework, there is a question of how to support conditional rendering and "foreach" nodes.
By conditional rendering, I mean a subtree of the UI that either exists or does not exist based on some condition, such as a modal dialog, a game mode, a tab panel and so on. Generally speaking, when a UI mode is not visible, you don't want to simply "hide" the elements, you want them to not exist at all - because the existence of these nodes often has side-effects beyond simply their impact on the layout.
Similarly, it is often the case that you'll have an array of items that are rendered from some data source that is an iterator, such as a list of saved games.
Immediate-mode frameworks have no problem with either of these patterns. However, a UI that retains elements wants to preserve the node tree where possible, otherwise you lose state (things like cursor position, text selection, focus and accessibility cues). This means that we want to avoid re-generating nodes when they have not changed. There are various strategies for this: the "React" approach is to generate a new list of child entities and compare that list with the old one; The "Solid" approach is to remember each child's index relative to its parent, and then "patch" the list as needed.
However, conditional and iterative UI elements are a challenge because they change the size of the child entity list, and alter all of the indices, making both comparing and patching difficult. For example, if I have a UI node whose children consist of a list of saved games followed by a "save" button, then the list index of that save button is going to change when additional save games are added. This is a challenge for both the React and Solid approaches.
For conditional nodes, a dummy "placeholder" node can be used to take up a child slot when the child is not being rendered. However, you then need to inform the layout engine to ignore this node in layout calculations. For lists, it's more complex, because a simple placeholder won't work.
What solution would you like?
The solution to all these problems is Display::Content
. This is an idea that is relatively new in CSS, and is not supported in all browsers yet, but makes sense in the context of Bevy UI. The basic idea is simple: a node with Display::Content
is handled by the layout engine as if it were replaced by its children. That is, the children of the element are hoisted up one level by the layout engine and rendered in place of that element. If the element with Display::Content
has no children, then it is simply ignored.
For conditional rendering, we create a placeholder node that represents the condition itself, and its single child is actually the conditionally-rendered content (or no child if the condition is not enabled). For iterative rendering, the placeholder node has a variable number of children depending on the number of elements in the array. In both cases, it means we can re-evaluate and re-generate the children of the conditional node, without altering the list indices of any of its siblings.
What alternative(s) have you considered?
The alternative (that I can think of) is to keep track of child node indices as a separate data structure, but this requires a lot more bookkeeping.