-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Added PressedButtons
to PickingInteraction::Pressed
.
#16004
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
mintlu8
wants to merge
7
commits into
bevyengine:main
Choose a base branch
from
mintlu8:picking-btn-filter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
515ce68
Added `PressedButtons` to `PickingInteraction`.
mintlu8 eaec63c
Added a test for merging `PickingInteraction`s.
mintlu8 a10c839
Added an example for `PickingInteraction`.
mintlu8 ba58c3f
Update interaction.rs
mintlu8 bbf07fe
Updated examples README.md
mintlu8 4d007ca
Fleshed out the example.
mintlu8 3969f2d
Renamed left and right.
mintlu8 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
|
||
use alloc::collections::BTreeMap; | ||
use core::fmt::Debug; | ||
use core::ops::{BitOr, BitOrAssign}; | ||
use std::collections::HashSet; | ||
|
||
use crate::{ | ||
|
@@ -190,16 +191,81 @@ fn build_hover_map( | |
/// it will be considered hovered. | ||
#[derive(Component, Copy, Clone, Default, Eq, PartialEq, Debug, Reflect)] | ||
#[reflect(Component, Default, PartialEq, Debug)] | ||
#[repr(u8)] | ||
pub enum PickingInteraction { | ||
/// The entity is being pressed down by a pointer. | ||
Pressed = 2, | ||
Pressed(PressedButtons) = 2, | ||
/// The entity is being hovered by a pointer. | ||
Hovered = 1, | ||
/// No pointers are interacting with this entity. | ||
#[default] | ||
None = 0, | ||
} | ||
|
||
impl BitOr for PickingInteraction { | ||
type Output = Self; | ||
|
||
fn bitor(mut self, rhs: Self) -> PickingInteraction { | ||
self |= rhs; | ||
self | ||
} | ||
} | ||
|
||
impl BitOrAssign for PickingInteraction { | ||
fn bitor_assign(&mut self, rhs: Self) { | ||
use PickingInteraction::*; | ||
match (self, rhs) { | ||
(Pressed(a), Pressed(b)) => *a |= b, | ||
(Pressed(_), _) => (), | ||
(a, Pressed(b)) => *a = Pressed(b), | ||
(a @ None, Hovered) => *a = Hovered, | ||
_ => (), | ||
} | ||
} | ||
} | ||
|
||
bitflags::bitflags! { | ||
#[repr(transparent)] | ||
#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)] | ||
#[reflect(opaque)] | ||
#[reflect(Hash, PartialEq, Debug)] | ||
/// Button pressed in a [`PickingInteraction`] | ||
pub struct PressedButtons: u8 { | ||
/// Left mouse button is pressed. | ||
const LEFT = 1; | ||
/// Right mouse button is pressed. | ||
const RIGHT = 2; | ||
/// Middle mouse button is pressed. | ||
const MIDDLE = 4; | ||
/// Touch input is pressed. | ||
const TOUCH = 8; | ||
/// Custom input is pressed. | ||
const CUSTOM = 16; | ||
/// X1 or back, reserved, currently does nothing. | ||
const X1 = 32; | ||
/// X2 or forward, reserved, currently does nothing. | ||
const X2 = 64; | ||
/// Left mouse button or touch input is pressed. | ||
const LEFT_OR_TOUCH = 1|8; | ||
} | ||
} | ||
|
||
impl From<PointerPress> for PressedButtons { | ||
fn from(value: PointerPress) -> Self { | ||
let mut result = PressedButtons::empty(); | ||
if value.is_primary_pressed() { | ||
result |= PressedButtons::LEFT; | ||
} | ||
if value.is_secondary_pressed() { | ||
result |= PressedButtons::RIGHT; | ||
} | ||
if value.is_middle_pressed() { | ||
result |= PressedButtons::MIDDLE; | ||
} | ||
result | ||
} | ||
} | ||
|
||
/// Uses pointer events to update [`PointerInteraction`] and [`PickingInteraction`] components. | ||
pub fn update_interactions( | ||
// Input | ||
|
@@ -235,7 +301,12 @@ pub fn update_interactions( | |
pointer_interaction.sorted_entities = sorted_entities; | ||
|
||
for hovered_entity in pointers_hovered_entities.iter().map(|(entity, _)| entity) { | ||
merge_interaction_states(pointer_press, hovered_entity, &mut new_interaction_state); | ||
merge_interaction_states( | ||
pointer, | ||
pointer_press, | ||
hovered_entity, | ||
&mut new_interaction_state, | ||
); | ||
} | ||
} | ||
} | ||
|
@@ -252,28 +323,55 @@ pub fn update_interactions( | |
|
||
/// Merge the interaction state of this entity into the aggregated map. | ||
fn merge_interaction_states( | ||
id: &PointerId, | ||
pointer_press: &PointerPress, | ||
hovered_entity: &Entity, | ||
new_interaction_state: &mut HashMap<Entity, PickingInteraction>, | ||
) { | ||
let new_interaction = match pointer_press.is_any_pressed() { | ||
true => PickingInteraction::Pressed, | ||
false => PickingInteraction::Hovered, | ||
let new_interaction = if !pointer_press.is_any_pressed() { | ||
PickingInteraction::Hovered | ||
} else { | ||
match id { | ||
PointerId::Mouse => PickingInteraction::Pressed((*pointer_press).into()), | ||
PointerId::Touch(_) => PickingInteraction::Pressed(PressedButtons::TOUCH), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See here for the mapping from |
||
PointerId::Custom(_) => PickingInteraction::Pressed(PressedButtons::CUSTOM), | ||
} | ||
}; | ||
new_interaction_state | ||
.entry(*hovered_entity) | ||
.and_modify(|old| *old |= new_interaction) | ||
.or_insert(new_interaction); | ||
} | ||
|
||
if let Some(old_interaction) = new_interaction_state.get_mut(hovered_entity) { | ||
// Only update if the new value has a higher precedence than the old value. | ||
if *old_interaction != new_interaction | ||
&& matches!( | ||
(*old_interaction, new_interaction), | ||
(PickingInteraction::Hovered, PickingInteraction::Pressed) | ||
| (PickingInteraction::None, PickingInteraction::Pressed) | ||
| (PickingInteraction::None, PickingInteraction::Hovered) | ||
) | ||
{ | ||
*old_interaction = new_interaction; | ||
} | ||
} else { | ||
new_interaction_state.insert(*hovered_entity, new_interaction); | ||
#[cfg(test)] | ||
mod test { | ||
use crate::focus::{PickingInteraction::*, PressedButtons}; | ||
|
||
#[test] | ||
fn merge_interaction() { | ||
assert_eq!( | ||
Pressed(PressedButtons::LEFT) | Pressed(PressedButtons::RIGHT), | ||
Pressed(PressedButtons::LEFT | PressedButtons::RIGHT) | ||
); | ||
assert_eq!( | ||
Pressed(PressedButtons::LEFT) | Hovered, | ||
Pressed(PressedButtons::LEFT) | ||
); | ||
assert_eq!( | ||
Hovered | Pressed(PressedButtons::LEFT), | ||
Pressed(PressedButtons::LEFT) | ||
); | ||
assert_eq!( | ||
Pressed(PressedButtons::LEFT) | None, | ||
Pressed(PressedButtons::LEFT) | ||
); | ||
assert_eq!( | ||
None | Pressed(PressedButtons::LEFT), | ||
Pressed(PressedButtons::LEFT) | ||
); | ||
assert_eq!(Hovered | None, Hovered); | ||
assert_eq!(None | Hovered, Hovered); | ||
assert_eq!(Hovered | Hovered, Hovered); | ||
assert_eq!(None | None, None); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
//! Demonstrates how to use `PickingInteraction` without using events and observers. | ||
|
||
use bevy::{ | ||
picking::focus::{PickingInteraction, PressedButtons}, | ||
prelude::*, | ||
}; | ||
|
||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) | ||
.add_systems(Startup, setup) | ||
.add_systems(Update, (move_sprite, picking)) | ||
.run(); | ||
} | ||
|
||
// Move the sprite for variety. | ||
fn move_sprite(time: Res<Time>, mut sprite: Query<&mut Transform, With<Sprite>>) { | ||
let t = time.elapsed_secs() * 0.1; | ||
for mut transform in &mut sprite { | ||
let new = Vec2 { | ||
x: 50.0 * ops::sin(t), | ||
y: 50.0 * ops::sin(t * 2.0), | ||
}; | ||
transform.translation.x = new.x; | ||
transform.translation.y = new.y; | ||
} | ||
} | ||
|
||
// Display the current picking state. | ||
fn picking(sprite: Query<(&PickingInteraction, &Children)>, mut text: Query<&mut Text2d>) { | ||
for (interaction, children) in sprite.iter() { | ||
let mut iter = text.iter_many_mut(children); | ||
while let Some(mut text) = iter.fetch_next() { | ||
match interaction { | ||
PickingInteraction::Pressed(pressed_buttons) | ||
if pressed_buttons.contains(PressedButtons::LEFT) => | ||
{ | ||
text.0 = "Left Clicked!".into(); | ||
} | ||
PickingInteraction::Pressed(pressed_buttons) | ||
if pressed_buttons.contains(PressedButtons::RIGHT) => | ||
{ | ||
text.0 = "Right Clicked!".into(); | ||
} | ||
PickingInteraction::Pressed(pressed_buttons) | ||
if pressed_buttons.contains(PressedButtons::TOUCH) => | ||
{ | ||
text.0 = "Touched!".into(); | ||
} | ||
// We choose to ignore other mouse buttons like MMB, | ||
// in this pattern we treat them as hover. | ||
PickingInteraction::Hovered | PickingInteraction::Pressed(..) => { | ||
text.0 = "Hovered!".into(); | ||
} | ||
PickingInteraction::None => { | ||
text.0 = "Hover Me!".into(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Set up the scene. | ||
fn setup(mut commands: Commands) { | ||
commands.spawn(Camera2d); | ||
|
||
commands | ||
.spawn(( | ||
Sprite { | ||
custom_size: Some(Vec2::new(200., 50.)), | ||
color: Color::BLACK, | ||
..Default::default() | ||
}, | ||
PickingInteraction::None, | ||
)) | ||
.with_children(|s| { | ||
s.spawn((Text2d::new("Hover Me!"), Transform::from_xyz(0., 0., 1.))); | ||
}); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is correct. Touch inputs are probably akin to a primary press, and some touch devices, like pens, can have a primary secondary, tertiary press, just like a mouse.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've had something like that before: my Note 9 has a pen and it supports hovering over something with the pen in the air and clicking a side-button to perform a secondary action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A stylus is not a touch pointer. Winit does not currently support stylus input, so neither do we.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely works, I've tested it. It's treated like a touch input. This was on a MS surface with a surface pen.
Regardless, if you are matching on whether a button was pressed, why would you not want it to be considered pressed if it was touched by a different pointing device?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is why I specifically used the wording "primary/secondary". A primary press can come from many types of devices.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR just maps the input from picking in a form that makes the most sense to me. Definitely would like to see more detailed information and more buttons, but that has to come from picking first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to
left
andright
.