You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
position_popover runs before layout, causing one-frame positional flash on spawn
Bevy version and features
Bevy 0.18.1
Feature: experimental_bevy_ui_widgets
Relevant system information
Windows 10
Rust stable (latest)
What you did
Spawned a Popover-bearing tooltip entity on hover using the canonical spawn/despawn pattern (matching the MenuPopup approach in examples/ui/standard_widgets.rs). The entity is spawned with Visibility::Hidden as documented by the inline comment // Will be visible after positioning in the official example.
fnon_pointer_over(trigger:On<Pointer<Over>>,mutcommands:Commands,mutquery:Query<(&mutBackgroundColor,&mutBorderColor)>,existing:Query<(),With<Tooltip>>,){ifletOk((mut bg,mut border)) = query.get_mut(trigger.entity){*bg = BackgroundColor(COLOR_HOVERED);*border = BorderColor::all(BORDER_HOVERED);}if !existing.is_empty(){return;}let tooltip = commands.spawn((Tooltip,Popover{positions:vec![PopoverPlacement{
side:PopoverSide::Bottom,
align:PopoverAlign::Center,
gap:6.0,},PopoverPlacement{
side:PopoverSide::Top,
align:PopoverAlign::Center,
gap:6.0,},],window_margin:8.0,},Node{padding:UiRect::all(px(6)),border:UiRect::all(px(1)),
..default()},BackgroundColor(TOOLTIP_BG),BorderColor::all(TOOLTIP_BORDER),Visibility::Hidden,// Will be visible after positioningPickable::IGNORE,children![(Text::new("Lorem ipsum dolor sit amet"),TextFont{ font_size:14.0, ..default()},TextColor(TOOLTIP_TEXT),Pickable::IGNORE,)],)).id();
commands.entity(trigger.entity).add_child(tooltip);}fnon_pointer_out(trigger:On<Pointer<Out>>,mutcommands:Commands,mutquery:Query<(&mutBackgroundColor,&mutBorderColor)>,tooltips:Query<Entity,With<Tooltip>>,){ifletOk((mut bg,mut border)) = query.get_mut(trigger.entity){*bg = BackgroundColor(COLOR_NORMAL);*border = BorderColor::all(BORDER_NORMAL);}for entity in&tooltips {
commands.entity(entity).despawn();}}
What went wrong
Expected: The tooltip should appear at the correct centered position on the first frame, with Visibility::Hidden preventing any flash while Popover computes placement.
Actual: The tooltip visibly appears offset to the right for one frame, then snaps to the correct centered position on the next frame.
Root cause
position_popover is registered in PostUpdate at UiSystems::Prepare:
UiSystems::Prepare runs beforeUiSystems::Layout. On the frame a Popover entity is spawned, ComputedNode has not yet been computed by Taffy (it defaults to zero width/height). position_popover reads this zero-sized ComputedNode, centers a zero-width element (which lands to the right of the true center), and then sets Visibility::Visible:
This overrides the spawned Visibility::Hidden before layout has run, so the entity becomes visible at the wrong position. On the next frame, ComputedNode has the correct size from the prior frame's layout pass, and Popover centers correctly.
The Visibility::Hidden at spawn is intended to prevent exactly this flash (as documented by the // Will be visible after positioning comment in examples/ui/standard_widgets.rs), but it cannot work when position_popover sets Visible before layout has computed the real size.
Proposed fix
Move position_popover to run after layout instead of before:
position_popover reads the correct size, computes the correct position, flips Visibility::Visible
Render shows the tooltip at the correct position on the first frame
Additional information
This affects any widget using the spawn/despawn pattern with Popover, including MenuPopup from the official standard_widgets example (though the flash is less noticeable with menus because they are click-triggered rather than hover-triggered).
This is directly relevant to the planned Headless Tooltips feature (Headless Tooltips #20601), which proposes spawn-on-show / despawn-on-hide using Popover for placement. That design will hit this same first-frame flash unless position_popover scheduling is corrected.
Keep the tooltip entity always in layout (Display::Flex with the Popover component always present) so ComputedNode is always valid. Use a marker component and a PostUpdate system scheduled after UiSystems::Prepare to gate visibility, effectively overriding Popover's unconditional Visibility::Visible write for inactive tooltips. This eliminates the flash but requires fighting Popover's visibility behavior.
position_popoverruns before layout, causing one-frame positional flash on spawnBevy version and features
0.18.1experimental_bevy_ui_widgetsRelevant system information
What you did
Spawned a
Popover-bearing tooltip entity on hover using the canonical spawn/despawn pattern (matching theMenuPopupapproach inexamples/ui/standard_widgets.rs). The entity is spawned withVisibility::Hiddenas documented by the inline comment// Will be visible after positioningin the official example.What went wrong
Expected: The tooltip should appear at the correct centered position on the first frame, with
Visibility::Hiddenpreventing any flash while Popover computes placement.Actual: The tooltip visibly appears offset to the right for one frame, then snaps to the correct centered position on the next frame.
Root cause
position_popoveris registered inPostUpdateatUiSystems::Prepare:UiSystems::Prepareruns beforeUiSystems::Layout. On the frame a Popover entity is spawned,ComputedNodehas not yet been computed by Taffy (it defaults to zero width/height).position_popoverreads this zero-sizedComputedNode, centers a zero-width element (which lands to the right of the true center), and then setsVisibility::Visible:This overrides the spawned
Visibility::Hiddenbefore layout has run, so the entity becomes visible at the wrong position. On the next frame,ComputedNodehas the correct size from the prior frame's layout pass, and Popover centers correctly.The
Visibility::Hiddenat spawn is intended to prevent exactly this flash (as documented by the// Will be visible after positioningcomment inexamples/ui/standard_widgets.rs), but it cannot work whenposition_popoversetsVisiblebefore layout has computed the real size.Proposed fix
Move
position_popoverto run after layout instead of before:This way, on the spawn frame:
ComputedNodesizeposition_popoverreads the correct size, computes the correct position, flipsVisibility::VisibleAdditional information
Popover, includingMenuPopupfrom the officialstandard_widgetsexample (though the flash is less noticeable with menus because they are click-triggered rather than hover-triggered).Popoverfor placement. That design will hit this same first-frame flash unlessposition_popoverscheduling is corrected.Workaround
Keep the tooltip entity always in layout (
Display::Flexwith thePopovercomponent always present) soComputedNodeis always valid. Use a marker component and aPostUpdatesystem scheduled afterUiSystems::Prepareto gate visibility, effectively overriding Popover's unconditionalVisibility::Visiblewrite for inactive tooltips. This eliminates the flash but requires fighting Popover's visibility behavior.