Files
bevy/examples/ui/layout/fixed_node.rs
ickshonpe 6dca76afcf FixedNode (#24323)
# Objective

Allow UI elements to be positioned relative to the viewport rather than
their parent element.

Fixes #9564

## Solution

- New UI marker component `FixedNode`. Requires `Node` and
`OverrideClip`.
- `FixedNode` entities are treated as UI roots in layout, even if they
have a parent.
- `FixedNode`s don't inherit their parent's layout, clipping or
transform context.
- During the taffy layout updates children with `FixedNode` are skipped.
- Added a couple of basic helper functions to `UiSurface`, mainly there
to make the tests a little less painful.
- Added a fairly comprehensive range of new tests, including tests with
`GhostNode`s.
- In the Taffy layout (stored in `UiSurface`) there is nothing to
distinguish `FixedNode`s and root nodes, so they are treated identically
during updates.

--

The original suggestion was to implement it as a `PositionType::Fixed`
variant that could used with `Node`, but I think that would be much more
complicated without support from Taffy. Being able to just directly
query for and filter out `FixedNode` entities directly makes the
implementation much simpler and more efficient.

## Testing

Basic example which shows events bubbling up to the parent from the
fixed node:

```
cargo run --example fixed_node
```

There are also a number of new tests in the `layout` module.

```
cargo test -p bevy_ui --lib --features ghost_nodes
```

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2026-05-21 11:08:44 +00:00

50 lines
1.4 KiB
Rust

//! Demonstrates how to use `FixedNode` to lay out a UI node as a root node
use bevy::color::palettes::css::BLUE;
use bevy::color::palettes::css::RED;
use bevy::color::palettes::css::YELLOW;
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands
.spawn((
Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(BLUE.into()),
Pickable::IGNORE,
children![(
FixedNode,
Node {
width: px(100),
height: px(100),
..default()
},
BackgroundColor(YELLOW.into()),
)],
))
.observe(
|over: On<Pointer<Over>>, mut colors: Query<&mut BackgroundColor>| {
colors.get_mut(over.entity).unwrap().0 = RED.into();
},
)
.observe(
|over: On<Pointer<Leave>>, mut colors: Query<&mut BackgroundColor>| {
colors.get_mut(over.entity).unwrap().0 = BLUE.into();
},
);
}