Skip to content

(UI) picking unexpectedly triggers outside the viewport #19692

Open
@jannik4

Description

@jannik4

Bevy version

What you did

Drag a UI node outside of the viewport. I would then expect that I can not click/drag this node anymore.

What went wrong

Clicking/Start dragging works outside of the viewport.

Code

Click to expand code
use bevy::{
    prelude::*, render::camera::Viewport, window::SystemCursorIcon, winit::cursor::CursorIcon,
};

fn main() -> AppExit {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .insert_resource(ClearColor(Color::BLACK))
        .run()
}

fn setup(mut commands: Commands, window: Single<&Window>) {
    commands.spawn((
        Camera2d,
        Camera {
            clear_color: ClearColorConfig::Custom(Color::WHITE),
            viewport: Some(Viewport {
                physical_position: UVec2::ZERO,
                physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
                ..default()
            }),
            ..default()
        },
    ));

    commands
        .spawn((
            Node {
                top: Val::Px(100.0),
                left: Val::Px(100.0),
                width: Val::Px(200.0),
                height: Val::Px(200.0),
                ..default()
            },
            BackgroundColor(Color::srgb(1.0, 0.0, 0.0)),
        ))
        .observe(|trigger: On<Pointer<Drag>>, mut node: Query<&mut Node>| {
            let mut node = node.get_mut(trigger.target()).unwrap();
            node.left = Val::Px(px(node.left) + trigger.delta.x);
            node.top = Val::Px(px(node.top) + trigger.delta.y);
        })
        .observe(
            |_: On<Pointer<DragStart>>,
             window: Single<Entity, With<Window>>,
             mut commands: Commands| {
                commands
                    .entity(*window)
                    .insert(CursorIcon::from(SystemCursorIcon::Grabbing));
            },
        )
        .observe(
            |_: On<Pointer<DragEnd>>,
             window: Single<Entity, With<Window>>,
             mut commands: Commands| {
                commands.entity(*window).remove::<CursorIcon>();
            },
        );
}

fn px(val: Val) -> f32 {
    match val {
        Val::Px(px) => px,
        _ => 0.0,
    }
}

Video

(White is the viewport of the camera)

Screen.Recording.2025-06-17.145003.mp4

Additional information

I don't know if this is specific to UI or also happens on other picking backends.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-PickingPointing at and selecting objects of all sortsC-BugAn unexpected or incorrect behaviorD-ModestA "normal" level of difficulty; suitable for simple features or challenging fixesS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions