Repository: bevyengine/bevy
File: crates/bevy_ui/src/widget/viewport.rs (Rust)

Code before:
pub struct Viewport {
    /// The entity representing the [`Camera`] associated with this viewport.
    ///
    /// Note that removing the [`Viewport`] component will not despawn this entity.
    pub camera: Entity,
}

impl Viewport {
    /// Creates a new [`Viewport`] with a given `camera`.
    pub fn new(camera: Entity) -> Self {
        Self { camera }
    }
}

#[cfg(feature = "bevy_ui_picking_backend")]
/// Handles viewport picking logic.
///
/// Viewport entities that are being hovered or dragged will have all pointer inputs sent to them.
#[expect(
    clippy::too_many_arguments,
    reason = "System requires a lot of arguments"
)]
pub fn viewport_picking(
    mut commands: Commands,
    viewport_query: Query<(&Viewport, &PointerId, &Children)>,
    node_query: Query<(&ComputedNode, &ImageNode, &GlobalTransform)>,
    camera_query: Query<&Camera>,
    hover_map: Res<HoverMap>,
    pointer_state: Res<PointerState>,
    mut pointer_inputs: EventReader<PointerInput>,
    mut dragged_last_frame: Local<HashSet<(Entity, PointerId)>>,
) {
    let mut viewport_picks: HashSet<(Entity, PointerId)> = dragged_last_frame
        .drain()
        .chain(hover_map.iter().flat_map(|(hover_pointer_id, hits)| {
            hits.iter()
                .filter(|(entity, _)| viewport_query.contains(**entity))
                .map(|(entity, _)| (*entity, *hover_pointer_id))
        }))
        .collect();

    // Currently, we have only retrieved viewport entities if they are being hovered. However, this
    // does not allow dragging in-and-out of viewports.
    //
    // We resolve this by considering viewports that are being dragged.
    for ((pointer_id, _), pointer_state) in pointer_state.pointer_buttons.iter() {
        for &target in pointer_state
            .dragging
            .keys()
            .filter(|&entity| viewport_query.contains(*entity))
        {

Review comment (suggestion):
```suggestion
    viewport_query: Query<(&ViewportNode, &PointerId, &ComputedNode, &GlobalTransform)>,
```

Code after:
    /// The entity representing the [`Camera`] associated with this viewport.
    ///
    /// Note that removing the [`ViewportNode`] component will not despawn this entity.
    pub camera: Entity,
}

impl ViewportNode {
    /// Creates a new [`ViewportNode`] with a given `camera`.
    pub fn new(camera: Entity) -> Self {
        Self { camera }
    }
}

#[cfg(feature = "bevy_ui_picking_backend")]
/// Handles viewport picking logic.
///
/// Viewport entities that are being hovered or dragged will have all pointer inputs sent to them.
pub fn viewport_picking(
    mut commands: Commands,
    mut viewport_query: Query<(
        Entity,
        &ViewportNode,
        &PointerId,
        &mut PointerLocation,
        &ComputedNode,
        &GlobalTransform,
    )>,
    camera_query: Query<&Camera>,
    hover_map: Res<HoverMap>,
    pointer_state: Res<PointerState>,
    mut pointer_inputs: EventReader<PointerInput>,
) {
    // Handle hovered entities.
    let mut viewport_picks: HashMap<Entity, PointerId> = hover_map
        .iter()
        .flat_map(|(hover_pointer_id, hits)| {
            hits.iter()
                .filter(|(entity, _)| viewport_query.contains(**entity))
                .map(|(entity, _)| (*entity, *hover_pointer_id))
        })
        .collect();

    // Handle dragged entities, which need to be considered for dragging in and out of viewports.
    for ((pointer_id, _), pointer_state) in pointer_state.pointer_buttons.iter() {
        for &target in pointer_state
            .dragging
            .keys()
            .filter(|&entity| viewport_query.contains(*entity))
        {
            viewport_picks.insert(target, *pointer_id);
        }