Skip to content

Remove the id from PointerId::Touch #17847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions crates/bevy_picking/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ pub fn pointer_events(
pointer_id,
location,
action,
..
} in input_events.read().cloned()
{
match action {
Expand Down
102 changes: 47 additions & 55 deletions crates/bevy_picking/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use bevy_input::{
ButtonState,
};
use bevy_math::Vec2;
use bevy_platform_support::collections::{HashMap, HashSet};
use bevy_platform_support::collections::HashMap;
use bevy_reflect::prelude::*;
use bevy_render::camera::RenderTarget;
use bevy_window::{PrimaryWindow, WindowEvent, WindowRef};
Expand Down Expand Up @@ -88,10 +88,6 @@ impl Plugin for PointerInputPlugin {
.chain()
.in_set(PickSet::Input),
)
.add_systems(
Last,
deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled),
)
.register_type::<Self>()
.register_type::<PointerInputPlugin>();
}
Expand Down Expand Up @@ -185,14 +181,13 @@ pub fn touch_pick_events(
mut window_events: EventReader<WindowEvent>,
primary_window: Query<Entity, With<PrimaryWindow>>,
// Locals
mut touch_cache: Local<HashMap<u64, TouchInput>>,
mut touch_cache: Local<HashMap<u64, (TouchInput, Entity)>>,
// Output
mut commands: Commands,
mut pointer_events: EventWriter<PointerInput>,
) {
for window_event in window_events.read() {
if let WindowEvent::TouchInput(touch) = window_event {
let pointer = PointerId::Touch(touch.id);
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
.normalize(primary_window.get_single().ok())
Expand All @@ -202,78 +197,75 @@ pub fn touch_pick_events(
},
position: touch.position,
};

match touch.phase {
TouchPhase::Started => {
debug!("Spawning pointer {:?}", pointer);
commands.spawn((pointer, PointerLocation::new(location.clone())));
let pointer = commands
.spawn((PointerId::Touch, PointerLocation::new(location.clone())))
.id();
touch_cache.insert(touch.id, (*touch, pointer));

debug!(
"Spawned touch, finger: {:?}, pointer:{:?}",
touch.id, pointer
);

pointer_events.send(PointerInput::new(
pointer_events.send(PointerInput::new_with_entity(
PointerId::Touch,
pointer,
location,
PointerAction::Press(PointerButton::Primary),
));

touch_cache.insert(touch.id, *touch);
}
TouchPhase::Moved => {
// Send a move event only if it isn't the same as the last one
if let Some(last_touch) = touch_cache.get(&touch.id) {
if last_touch == touch {
if let Some((last_touch, pointer)) = touch_cache.get_mut(&touch.id) {
if *last_touch == *touch {
continue;
}
pointer_events.send(PointerInput::new(
pointer,
pointer_events.send(PointerInput::new_with_entity(
PointerId::Touch,
*pointer,
location,
PointerAction::Move {
delta: touch.position - last_touch.position,
},
));
*last_touch = *touch;
}
touch_cache.insert(touch.id, *touch);
}
TouchPhase::Ended => {
pointer_events.send(PointerInput::new(
pointer,
location,
PointerAction::Release(PointerButton::Primary),
));
touch_cache.remove(&touch.id);
if let Some((_, pointer)) = touch_cache.remove(&touch.id) {
debug!(
"Despawning touch, finger {:?}, entity {:?}",
touch.id, pointer
);
commands.entity(pointer).despawn();

pointer_events.send(PointerInput::new_with_entity(
PointerId::Touch,
pointer,
location,
PointerAction::Release(PointerButton::Primary),
));
}
}
TouchPhase::Canceled => {
pointer_events.send(PointerInput::new(
pointer,
location,
PointerAction::Cancel,
));
touch_cache.remove(&touch.id);
}
}
}
}
}
if let Some((_, pointer)) = touch_cache.remove(&touch.id) {
debug!(
"Despawning touch, finger {:?}, entity {:?}",
touch.id, pointer
);
commands.entity(pointer).despawn();

/// Deactivates unused touch pointers.
///
/// Because each new touch gets assigned a new ID, we need to remove the pointers associated with
/// touches that are no longer active.
pub fn deactivate_touch_pointers(
mut commands: Commands,
mut despawn_list: Local<HashSet<(Entity, PointerId)>>,
pointers: Query<(Entity, &PointerId)>,
mut touches: EventReader<TouchInput>,
) {
for touch in touches.read() {
if let TouchPhase::Ended | TouchPhase::Canceled = touch.phase {
for (entity, pointer) in &pointers {
if pointer.get_touch_id() == Some(touch.id) {
despawn_list.insert((entity, *pointer));
pointer_events.send(PointerInput::new_with_entity(
PointerId::Touch,
pointer,
location,
PointerAction::Cancel,
));
}
}
}
}
}
// A hash set is used to prevent despawning the same entity twice.
for (entity, pointer) in despawn_list.drain() {
debug!("Despawning pointer {:?}", pointer);
commands.entity(entity).despawn();
}
}
34 changes: 23 additions & 11 deletions crates/bevy_picking/src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub enum PointerId {
/// The mouse pointer.
#[default]
Mouse,
/// A touch input, usually numbered by window touch events from `winit`.
Touch(u64),
/// A touch input.
Touch,
/// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software
/// controlled cursor.
#[reflect(ignore)]
Expand All @@ -44,7 +44,7 @@ pub enum PointerId {
impl PointerId {
/// Returns true if the pointer is a touch input.
pub fn is_touch(&self) -> bool {
matches!(self, PointerId::Touch(_))
matches!(self, PointerId::Touch)
}
/// Returns true if the pointer is the mouse.
pub fn is_mouse(&self) -> bool {
Expand All @@ -54,14 +54,6 @@ impl PointerId {
pub fn is_custom(&self) -> bool {
matches!(self, PointerId::Custom(_))
}
/// Returns the touch id if the pointer is a touch input.
pub fn get_touch_id(&self) -> Option<u64> {
if let PointerId::Touch(id) = self {
Some(*id)
} else {
None
}
}
}

/// Holds a list of entities this pointer is currently interacting with, sorted from nearest to
Expand Down Expand Up @@ -270,6 +262,8 @@ pub enum PointerAction {
pub struct PointerInput {
/// The id of the pointer.
pub pointer_id: PointerId,
/// The pointer entity
pub pointer_entity: Option<Entity>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Picking is already inconsistent enough across backends, I would strongly prefer if we didn't add more inconsistency, though I appreciate your reasoning.

/// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement.
pub location: Location,
/// The action that the event describes.
Expand All @@ -283,6 +277,24 @@ impl PointerInput {
pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
PointerInput {
pointer_id,
pointer_entity: None,
location,
action,
}
}

/// Creates a new pointer input event.
///
/// Note that `location` refers to the position of the pointer *after* the event occurred.
pub fn new_with_entity(
pointer_id: PointerId,
pointer_entity: Entity,
location: Location,
action: PointerAction,
) -> PointerInput {
PointerInput {
pointer_id,
pointer_entity: Some(pointer_entity),
location,
action,
}
Expand Down
Loading