diff --git a/crates/bevy_ui_widgets/src/slider.rs b/crates/bevy_ui_widgets/src/slider.rs index f8748e0a55a19..645b3043bd1e4 100644 --- a/crates/bevy_ui_widgets/src/slider.rs +++ b/crates/bevy_ui_widgets/src/slider.rs @@ -1,5 +1,3 @@ -use core::ops::RangeInclusive; - use accesskit::{Orientation, Role}; use bevy_a11y::AccessibilityNode; use bevy_app::{App, Plugin}; @@ -7,7 +5,7 @@ use bevy_ecs::event::EntityEvent; use bevy_ecs::hierarchy::Children; use bevy_ecs::lifecycle::Insert; use bevy_ecs::query::Has; -use bevy_ecs::system::Res; +use bevy_ecs::system::{Res, ResMut}; use bevy_ecs::world::DeferredWorld; use bevy_ecs::{ component::Component, @@ -24,11 +22,14 @@ use bevy_math::ops; use bevy_picking::events::{ PointerCancel, PointerDrag, PointerDragEnd, PointerDragStart, PointerPress, PointerRelease, }; +use bevy_picking::hover::PointerCaptureMap; + use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_ui::{ ComputedNode, ComputedUiRenderTargetInfo, InteractionDisabled, Pressed, UiGlobalTransform, UiScale, }; +use core::ops::RangeInclusive; use crate::ValueChange; use bevy_ecs::entity::Entity; @@ -383,12 +384,18 @@ pub(crate) fn slider_on_drag_start( (&SliderValue, &mut SliderDragState, Has), With, >, + mut capture_map: ResMut, ) { if let Ok((value, mut drag, disabled)) = q_slider.get_mut(drag_start.entity) { drag_start.propagate(false); if !disabled { drag.dragging = true; drag.offset = value.0; + capture_map.capture( + drag_start.pointer.id, + drag_start.entity, + drag_start.hit.clone(), + ); } } } @@ -438,6 +445,7 @@ pub(crate) fn slider_on_drag( pub(crate) fn slider_on_drag_end( mut drag_end: On, + mut capture_map: ResMut, mut q_slider: Query< ( Entity, @@ -480,6 +488,7 @@ pub(crate) fn slider_on_drag_end( } commands.entity(slider_ent).remove::(); drag.dragging = false; + capture_map.release(drag_end.pointer.id); } } } @@ -754,6 +763,7 @@ impl Plugin for SliderPlugin { #[cfg(test)] mod tests { use super::*; + use bevy_camera::NormalizedRenderTarget; use bevy_ecs::hierarchy::ChildOf; use bevy_input::keyboard::Key; use bevy_input::InputPlugin; @@ -761,6 +771,12 @@ mod tests { tab_navigation::{TabIndex, TabNavigationPlugin}, FocusCause, InputDispatchPlugin, InputFocus, InputFocusPlugin, }; + use bevy_math::Vec2; + use bevy_picking::{ + backend::HitData, + events::Pointer, + pointer::{Location, PointerButton, PointerId}, + }; use bevy_window::{PrimaryWindow, Window}; /// Builds a headless app with the slider observers plus [`slider_self_update`] (so a @@ -804,6 +820,48 @@ mod tests { slider } + #[test] + fn slider_captures_pointer_on_drag_start() { + let (mut app, window) = slider_app(); + let slider = spawn_focused_slider(&mut app, window); + let camera = app.world_mut().spawn_empty().id(); + app.init_resource::(); + + let hit = HitData { + camera, + depth: 0.0, + position: None, + normal: None, + extra: None, + }; + + let pointer = Pointer::new( + PointerId::Mouse, + Location { + target: NormalizedRenderTarget::None { + width: 0, + height: 0, + }, + position: Vec2::ZERO, + }, + ); + + app.world_mut().trigger(PointerDragStart { + entity: slider, + pointer, + button: PointerButton::Primary, + hit, + }); + + let capture_map = app.world().resource::(); + + let (captured_entity, _) = capture_map + .get(&PointerId::Mouse) + .expect("slider should capture the pointer on drag start"); + + assert_eq!(captured_entity, slider); + } + fn press_key(app: &mut App, key_code: KeyCode, logical_key: Key, window: Entity) { app.world_mut().write_message(KeyboardInput { key_code,