mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Fixed bevy_ui touch input (#4099)
# Objective `bevy_ui` doesn't support correctly touch inputs because of two problems in the focus system: - It attempts to retrieve touch input with a specific `0` id - It doesn't retrieve touch positions and bases its focus solely on mouse position, absent from mobile devices ## Solution I added a few methods to the `Touches` resource, allowing to check if **any** touch input was pressed, released or cancelled and to retrieve the *position* of the first pressed touch input and adapted the focus system. I added a test button to the *iOS* example and it works correclty on emulator. I did not test on a real touch device as: - Android is not working (https://github.com/bevyengine/bevy/issues/3249) - I don't have an iOS device
This commit is contained in:
parent
30ca97e287
commit
c4fc5d88f0
3 changed files with 89 additions and 7 deletions
|
@ -224,6 +224,11 @@ impl Touches {
|
|||
self.pressed.get(&id)
|
||||
}
|
||||
|
||||
/// Checks if any touch input was just pressed.
|
||||
pub fn any_just_pressed(&self) -> bool {
|
||||
!self.just_pressed.is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if the input corresponding to the `id` has just been pressed.
|
||||
pub fn just_pressed(&self, id: u64) -> bool {
|
||||
self.just_pressed.contains_key(&id)
|
||||
|
@ -239,6 +244,11 @@ impl Touches {
|
|||
self.just_released.get(&id)
|
||||
}
|
||||
|
||||
/// Checks if any touch input was just released.
|
||||
pub fn any_just_released(&self) -> bool {
|
||||
!self.just_released.is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if the input corresponding to the `id` has just been released.
|
||||
pub fn just_released(&self, id: u64) -> bool {
|
||||
self.just_released.contains_key(&id)
|
||||
|
@ -249,6 +259,11 @@ impl Touches {
|
|||
self.just_released.values()
|
||||
}
|
||||
|
||||
/// Checks if any touch input was just cancelled.
|
||||
pub fn any_just_cancelled(&self) -> bool {
|
||||
!self.just_cancelled.is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if the input corresponding to the `id` has just been cancelled.
|
||||
pub fn just_cancelled(&self, id: u64) -> bool {
|
||||
self.just_cancelled.contains_key(&id)
|
||||
|
@ -259,6 +274,11 @@ impl Touches {
|
|||
self.just_cancelled.values()
|
||||
}
|
||||
|
||||
/// Retrieves the position of the first currently pressed touch, if any
|
||||
pub fn first_pressed_position(&self) -> Option<Vec2> {
|
||||
self.pressed.values().next().map(|t| t.position)
|
||||
}
|
||||
|
||||
/// Processes a [`TouchInput`] event by updating the `pressed`, `just_pressed`,
|
||||
/// `just_released`, and `just_cancelled` collections.
|
||||
fn process_touch_event(&mut self, event: &TouchInput) {
|
||||
|
|
|
@ -71,10 +71,6 @@ pub fn ui_focus_system(
|
|||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
) {
|
||||
let cursor_position = windows
|
||||
.get_primary()
|
||||
.and_then(|window| window.cursor_position());
|
||||
|
||||
// reset entities that were both clicked and released in the last frame
|
||||
for entity in state.entities_to_reset.drain(..) {
|
||||
if let Ok(mut interaction) = node_query.get_component_mut::<Interaction>(entity) {
|
||||
|
@ -83,7 +79,7 @@ pub fn ui_focus_system(
|
|||
}
|
||||
|
||||
let mouse_released =
|
||||
mouse_button_input.just_released(MouseButton::Left) || touches_input.just_released(0);
|
||||
mouse_button_input.just_released(MouseButton::Left) || touches_input.any_just_released();
|
||||
if mouse_released {
|
||||
for (_entity, _node, _global_transform, interaction, _focus_policy, _clip) in
|
||||
node_query.iter_mut()
|
||||
|
@ -97,7 +93,12 @@ pub fn ui_focus_system(
|
|||
}
|
||||
|
||||
let mouse_clicked =
|
||||
mouse_button_input.just_pressed(MouseButton::Left) || touches_input.just_pressed(0);
|
||||
mouse_button_input.just_pressed(MouseButton::Left) || touches_input.any_just_pressed();
|
||||
|
||||
let cursor_position = windows
|
||||
.get_primary()
|
||||
.and_then(|window| window.cursor_position())
|
||||
.or_else(|| touches_input.first_pressed_position());
|
||||
|
||||
let mut moused_over_z_sorted_nodes = node_query
|
||||
.iter_mut()
|
||||
|
|
|
@ -13,13 +13,14 @@ fn main() {
|
|||
.add_startup_system(setup_scene)
|
||||
.add_startup_system(setup_music)
|
||||
.add_system(touch_camera)
|
||||
.add_system(button_handler)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn touch_camera(
|
||||
windows: ResMut<Windows>,
|
||||
mut touches: EventReader<TouchInput>,
|
||||
mut camera: Query<&mut Transform, With<Camera>>,
|
||||
mut camera: Query<&mut Transform, With<Camera3d>>,
|
||||
mut last_position: Local<Option<Vec2>>,
|
||||
) {
|
||||
for touch in touches.iter() {
|
||||
|
@ -47,6 +48,7 @@ fn setup_scene(
|
|||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
// plane
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
|
@ -86,6 +88,65 @@ fn setup_scene(
|
|||
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Test ui
|
||||
commands.spawn_bundle(Camera2dBundle::default());
|
||||
commands
|
||||
.spawn_bundle(ButtonBundle {
|
||||
style: Style {
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
position_type: PositionType::Absolute,
|
||||
position: UiRect {
|
||||
left: Val::Px(50.0),
|
||||
right: Val::Px(50.0),
|
||||
top: Val::Auto,
|
||||
bottom: Val::Px(50.0),
|
||||
},
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
.with_children(|b| {
|
||||
b.spawn_bundle(TextBundle {
|
||||
text: Text {
|
||||
sections: vec![TextSection {
|
||||
value: "Test Button".to_string(),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 30.0,
|
||||
color: Color::BLACK,
|
||||
},
|
||||
}],
|
||||
alignment: TextAlignment {
|
||||
vertical: VerticalAlign::Center,
|
||||
horizontal: HorizontalAlign::Center,
|
||||
},
|
||||
},
|
||||
..default()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn button_handler(
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &mut UiColor),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
) {
|
||||
for (interaction, mut color) in interaction_query.iter_mut() {
|
||||
match *interaction {
|
||||
Interaction::Clicked => {
|
||||
*color = Color::BLUE.into();
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
*color = Color::GRAY.into();
|
||||
}
|
||||
Interaction::None => {
|
||||
*color = Color::WHITE.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_music(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
|
|
Loading…
Reference in a new issue