Skip to content

Ghost nodes: layout- and rendering-ignored entities in hierarchies #14826

Closed
@viridia

Description

@viridia

This is a follow-up to an issue I filed and then closed a long time ago: #9731 The reason I closed the issue was that I was able to find a workaround; however, I'm re-opening the issue in modified form because the workaround is making my code much more complex than it needs to be.

First, let me describe what I mean by "auxiliary" and "fragment" nodes. I'm not sure what's the best word to describe the concept, but the basic idea is that a fragment node is an entity that has a special marker component, or perhaps a special type of visibility, that causes the node to be ignored by the ui layout and rendering code, but allows its children to be displayed in its place. The term "fragment" comes from React - a <Fragment/> is a special type of JSX element that contains multiple children, but which can be passed around like a single element reference.

Fragment nodes behave like regular entities in all other ways: they can be children of other nodes, they can listen for events, they have have components and children of their own and so on. The only difference is that they are not displayed, but their children are displayed in their place.

This has an effect on flexboxes and grids: let's say we have a flexbox node with three children, one of which is a fragment with N children. The flexbox layout algorithm should treat each child of the fragment as if it were a direct child of the flexbox, meaning that in this example the flexbox will have 2+N layout items. If the fragment has a child which is also a fragment, the children of that fragment is also treated as direct children of the flexbox.

This also affects scenes: if a fragment node is a child of a SpatialBundle, and has in turn a child which is a MeshBundle, the mesh bundle is rendered as if it were a direct child of the SpatialBundle; there's no warning about the lack of a transform component on the fragment.

What problem does this solve or what need does it fill?

For a reactive framework, the benefits of fragments are huge: I'd estimate that the amount of code for bevy_reactor and Quill could be reduced by 30-50%.

The reason is that these frameworks need to insert special "housekeeping" nodes into the entity tree. An if-condition node needs to keep track of the current state of the condition, as well as state information needed to tear down the children when the condition changes. A for-loop node needs to keep around information about the array elements so that it can do a 'diff' when the array changes. Template call nodes need to keep a copy of the template parameters.

Why can't these simply be stored as components? Because there might not be any entity available to store them - a conditional branch might render an empty tree, a for-loop might render zero items. Or it might render 100 items. There's no obvious, unambiguous way to annotate the children of these conditional operators on the child nodes.

For similar reasons, these aux nodes can't be stored as siblings or on the parent node without creating additional ambiguity or complexity. The only way that really makes sense is to have a special node which dynamically generates its children, and then to somehow mark that node as not being part of the layout.

The way I am doing this currently is to maintain an entirely separate tree in parallel which stores these housekeeping nodes. However, this introduces a lot of complexity, because these trees have to cross-link to each other, when you despawn part of one tree you also have to despawn the corresponding part of the other tree. (This is part of the reason why Quill UIs cannot be StateScoped - you can't just call despawn_recursive and expect it not to crash.)

What solution would you like?

One possible approach is to add a new type of Visibility, such as Visibility::Fragment. Another is to have a special marker component. The hard part is modifying the layout / rendering code to recursively traverse into the fragment node.

The original proposal was to add a new type of Display, but that only fixes the issue for bevy_ui nodes and doesn't address the issue for other kinds of scenes.

Finally, I want to mention that this may have an impact on BSN. We don't have much details on the reactive strategy for BSN, other than @cart 's comment about favoring a fine-grained approach. However, I have a hard time imagining how BSN is going to be able to implement any kind of dynamic/incremental updates without a way to store template housekeeping information in the entity tree.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-UIGraphical user interfaces, styles, layouts, and widgetsC-FeatureA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to useS-Needs-DesignThis issue requires design work to think about how it would best be accomplished

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions