rename touchpad to gesture, and add new gestures (#13660)

# Objective

- With the recent winit update, touchpad specific events can also be
triggered on mobile

## Solution

- Rename them to gestures and add support for the new ones

## Testing

- Tested on the mobile example on iOS


https://github.com/bevyengine/bevy/assets/8672791/da4ed23f-ff0a-41b2-9dcd-726e8546bef2


## Migration Guide

- `TouchpadMagnify` has been renamed to `PinchGesture`
- `TouchpadRotate` has been renamed to `RotationGesture `

---------

Co-authored-by: mike <ramirezmike2@gmail.com>
This commit is contained in:
François Mockers 2024-06-04 14:44:25 +02:00 committed by GitHub
parent 58a0c1336c
commit df57850310
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 217 additions and 67 deletions

View file

@ -0,0 +1,73 @@
//! Gestures functionality, from touchscreens and touchpads.
use bevy_ecs::event::Event;
use bevy_math::Vec2;
use bevy_reflect::Reflect;
#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// Two-finger pinch gesture, often used for magnifications.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct PinchGesture(pub f32);
/// Two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct RotationGesture(pub f32);
/// Double tap gesture.
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct DoubleTapGesture;
/// Pan gesture.
///
/// ## Platform-specific
///
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct PanGesture(pub Vec2);

View file

@ -16,10 +16,10 @@ mod button_input;
/// Common run conditions
pub mod common_conditions;
pub mod gamepad;
pub mod gestures;
pub mod keyboard;
pub mod mouse;
pub mod touch;
pub mod touchpad;
pub use axis::*;
pub use button_input::*;
@ -41,10 +41,10 @@ pub mod prelude {
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use gestures::*;
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput};
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
use touch::{touch_screen_input_system, TouchInput, Touches};
use touchpad::{TouchpadMagnify, TouchpadRotate};
use gamepad::{
gamepad_axis_event_system, gamepad_button_event_system, gamepad_connection_system,
@ -77,8 +77,10 @@ impl Plugin for InputPlugin {
.add_event::<MouseWheel>()
.init_resource::<ButtonInput<MouseButton>>()
.add_systems(PreUpdate, mouse_button_input_system.in_set(InputSystem))
.add_event::<TouchpadMagnify>()
.add_event::<TouchpadRotate>()
.add_event::<PinchGesture>()
.add_event::<RotationGesture>()
.add_event::<DoubleTapGesture>()
.add_event::<PanGesture>()
// gamepad
.add_event::<GamepadConnectionEvent>()
.add_event::<GamepadButtonChangedEvent>()
@ -114,8 +116,10 @@ impl Plugin for InputPlugin {
app.register_type::<ButtonState>()
.register_type::<KeyboardInput>()
.register_type::<MouseButtonInput>()
.register_type::<TouchpadMagnify>()
.register_type::<TouchpadRotate>()
.register_type::<PinchGesture>()
.register_type::<RotationGesture>()
.register_type::<DoubleTapGesture>()
.register_type::<PanGesture>()
.register_type::<TouchInput>()
.register_type::<GamepadEvent>()
.register_type::<GamepadButtonInput>()

View file

@ -1,41 +0,0 @@
//! The touchpad input functionality.
use bevy_ecs::event::Event;
use bevy_reflect::Reflect;
#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// Touchpad magnification event with two-finger pinch gesture.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
///
/// ## Platform-specific
///
/// - Only available on **`macOS`**.
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct TouchpadMagnify(pub f32);
/// Touchpad rotation event with two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **`macOS`**.
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct TouchpadRotate(pub f32);

View file

@ -281,6 +281,33 @@ pub struct Window {
/// [`wgpu::SurfaceConfiguration::desired_maximum_frame_latency`]:
/// https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html#structfield.desired_maximum_frame_latency
pub desired_maximum_frame_latency: Option<NonZeroU32>,
/// Sets whether this window recognizes [`PinchGesture`]
///
/// ## Platform-specific
///
/// - Only used on iOS.
/// - On macOS, they are recognized by default and can't be disabled.
pub recognize_pinch_gesture: bool,
/// Sets whether this window recognizes [`RotationGesture`]
///
/// ## Platform-specific
///
/// - Only used on iOS.
/// - On macOS, they are recognized by default and can't be disabled.
pub recognize_rotation_gesture: bool,
/// Sets whether this window recognizes [`DoubleTapGesture`]
///
/// ## Platform-specific
///
/// - Only used on iOS.
/// - On macOS, they are recognized by default and can't be disabled.
pub recognize_doubletap_gesture: bool,
/// Sets whether this window recognizes [`PanGesture`], with a number of fingers between the first value and the last.
///
/// ## Platform-specific
///
/// - Only used on iOS.
pub recognize_pan_gesture: Option<(u8, u8)>,
}
impl Default for Window {
@ -311,6 +338,10 @@ impl Default for Window {
visible: true,
skip_taskbar: false,
desired_maximum_frame_latency: None,
recognize_pinch_gesture: false,
recognize_rotation_gesture: false,
recognize_doubletap_gesture: false,
recognize_pan_gesture: None,
}
}
}

View file

@ -7,8 +7,8 @@ use bevy_ecs::prelude::*;
use bevy_ecs::system::SystemState;
use bevy_ecs::world::FromWorld;
use bevy_input::{
gestures::*,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touchpad::{TouchpadMagnify, TouchpadRotate},
};
use bevy_log::{error, trace, warn};
use bevy_math::{ivec2, DVec2, Vec2};
@ -264,10 +264,19 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
});
}
WindowEvent::PinchGesture { delta, .. } => {
self.winit_events.send(TouchpadMagnify(delta as f32));
self.winit_events.send(PinchGesture(delta as f32));
}
WindowEvent::RotationGesture { delta, .. } => {
self.winit_events.send(TouchpadRotate(delta));
self.winit_events.send(RotationGesture(delta));
}
WindowEvent::DoubleTapGesture { .. } => {
self.winit_events.send(DoubleTapGesture);
}
WindowEvent::PanGesture { delta, .. } => {
self.winit_events.send(PanGesture(Vec2 {
x: delta.x,
y: delta.y,
}));
}
WindowEvent::MouseWheel { delta, .. } => match delta {
event::MouseScrollDelta::LineDelta(x, y) => {
@ -674,10 +683,16 @@ impl<T: Event> WinitAppRunnerState<T> {
WinitEvent::MouseWheel(e) => {
world.send_event(e);
}
WinitEvent::TouchpadMagnify(e) => {
WinitEvent::PinchGesture(e) => {
world.send_event(e);
}
WinitEvent::TouchpadRotate(e) => {
WinitEvent::RotationGesture(e) => {
world.send_event(e);
}
WinitEvent::DoubleTapGesture(e) => {
world.send_event(e);
}
WinitEvent::PanGesture(e) => {
world.send_event(e);
}
WinitEvent::TouchInput(e) => {

View file

@ -16,6 +16,8 @@ use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event_loop::ActiveEventLoop;
use bevy_ecs::query::With;
#[cfg(target_os = "ios")]
use winit::platform::ios::WindowExtIOS;
#[cfg(target_arch = "wasm32")]
use winit::platform::web::WindowExtWebSys;
@ -97,6 +99,19 @@ pub fn create_windows<F: QueryFilter + 'static>(
style.set_property("height", "100%").unwrap();
}
}
#[cfg(target_os = "ios")]
{
winit_window.recognize_pinch_gesture(window.recognize_pinch_gesture);
winit_window.recognize_rotation_gesture(window.recognize_rotation_gesture);
winit_window.recognize_doubletap_gesture(window.recognize_doubletap_gesture);
if let Some((min, max)) = window.recognize_pan_gesture {
winit_window.recognize_pan_gesture(true, min, max);
} else {
winit_window.recognize_pan_gesture(false, 0, 0);
}
}
window_created_events.send(WindowCreated { window: entity });
}
}
@ -360,6 +375,31 @@ pub(crate) fn changed_windows(
winit_window.set_visible(window.visible);
}
#[cfg(target_os = "ios")]
{
if window.recognize_pinch_gesture != cache.window.recognize_pinch_gesture {
winit_window.recognize_pinch_gesture(window.recognize_pinch_gesture);
}
if window.recognize_rotation_gesture != cache.window.recognize_rotation_gesture {
winit_window.recognize_rotation_gesture(window.recognize_rotation_gesture);
}
if window.recognize_doubletap_gesture != cache.window.recognize_doubletap_gesture {
winit_window.recognize_doubletap_gesture(window.recognize_doubletap_gesture);
}
if window.recognize_pan_gesture != cache.window.recognize_pan_gesture {
match (
window.recognize_pan_gesture,
cache.window.recognize_pan_gesture,
) {
(Some(_), Some(_)) => {
warn!("Bevy currently doesn't support modifying PanGesture number of fingers recognition. Please disable it before re-enabling it with the new number of fingers");
}
(Some((min, max)), _) => winit_window.recognize_pan_gesture(true, min, max),
_ => winit_window.recognize_pan_gesture(false, 0, 0),
}
}
}
cache.window = window.clone();
}
}

View file

@ -5,8 +5,8 @@ use bevy_ecs::prelude::*;
use bevy_input::keyboard::KeyboardInput;
use bevy_input::touch::TouchInput;
use bevy_input::{
gestures::*,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
touchpad::{TouchpadMagnify, TouchpadRotate},
};
use bevy_reflect::Reflect;
#[cfg(feature = "serialize")]
@ -55,8 +55,10 @@ pub enum WinitEvent {
MouseMotion(MouseMotion),
MouseWheel(MouseWheel),
TouchpadMagnify(TouchpadMagnify),
TouchpadRotate(TouchpadRotate),
PinchGesture(PinchGesture),
RotationGesture(RotationGesture),
DoubleTapGesture(DoubleTapGesture),
PanGesture(PanGesture),
TouchInput(TouchInput),
@ -168,14 +170,24 @@ impl From<MouseWheel> for WinitEvent {
Self::MouseWheel(e)
}
}
impl From<TouchpadMagnify> for WinitEvent {
fn from(e: TouchpadMagnify) -> Self {
Self::TouchpadMagnify(e)
impl From<PinchGesture> for WinitEvent {
fn from(e: PinchGesture) -> Self {
Self::PinchGesture(e)
}
}
impl From<TouchpadRotate> for WinitEvent {
fn from(e: TouchpadRotate) -> Self {
Self::TouchpadRotate(e)
impl From<RotationGesture> for WinitEvent {
fn from(e: RotationGesture) -> Self {
Self::RotationGesture(e)
}
}
impl From<DoubleTapGesture> for WinitEvent {
fn from(e: DoubleTapGesture) -> Self {
Self::DoubleTapGesture(e)
}
}
impl From<PanGesture> for WinitEvent {
fn from(e: PanGesture) -> Self {
Self::PanGesture(e)
}
}
impl From<TouchInput> for WinitEvent {

View file

@ -2,8 +2,8 @@
use bevy::{
input::{
gestures::*,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
touchpad::{TouchpadMagnify, TouchpadRotate},
},
prelude::*,
};
@ -21,8 +21,9 @@ fn print_mouse_events_system(
mut mouse_motion_events: EventReader<MouseMotion>,
mut cursor_moved_events: EventReader<CursorMoved>,
mut mouse_wheel_events: EventReader<MouseWheel>,
mut touchpad_magnify_events: EventReader<TouchpadMagnify>,
mut touchpad_rotate_events: EventReader<TouchpadRotate>,
mut pinch_gesture_events: EventReader<PinchGesture>,
mut rotation_gesture_events: EventReader<RotationGesture>,
mut double_tap_gesture_events: EventReader<DoubleTapGesture>,
) {
for event in mouse_button_input_events.read() {
info!("{:?}", event);
@ -41,12 +42,17 @@ fn print_mouse_events_system(
}
// This event will only fire on macOS
for event in touchpad_magnify_events.read() {
for event in pinch_gesture_events.read() {
info!("{:?}", event);
}
// This event will only fire on macOS
for event in touchpad_rotate_events.read() {
for event in rotation_gesture_events.read() {
info!("{:?}", event);
}
// This event will only fire on macOS
for event in double_tap_gesture_events.read() {
info!("{:?}", event);
}
}

View file

@ -2,7 +2,7 @@
use bevy::{
color::palettes::basic::*,
input::touch::TouchPhase,
input::{gestures::RotationGesture, touch::TouchPhase},
prelude::*,
window::{AppLifecycle, WindowMode},
};
@ -15,6 +15,9 @@ fn main() {
primary_window: Some(Window {
resizable: false,
mode: WindowMode::BorderlessFullscreen,
// on iOS, gestures must be enabled.
// This doesn't work on Android
recognize_rotation_gesture: true,
..default()
}),
..default()
@ -35,6 +38,7 @@ fn touch_camera(
mut touches: EventReader<TouchInput>,
mut camera: Query<&mut Transform, With<Camera3d>>,
mut last_position: Local<Option<Vec2>>,
mut rotations: EventReader<RotationGesture>,
) {
let window = windows.single();
@ -55,6 +59,12 @@ fn touch_camera(
}
*last_position = Some(touch.position);
}
// Rotation gestures only work on iOS
for rotation in rotations.read() {
let mut transform = camera.single_mut();
let forward = transform.forward();
transform.rotate_axis(forward, rotation.0 / 10.0);
}
}
/// set up a simple 3D scene