Skip to content
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

Split PointerId into distinct id and PointerKind fields #17808

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

alice-i-cecile
Copy link
Member

Objective

As discussed in #17399, sending fake picking events (as you might want to do for clicking a focused element) is frustrating.

After discussing this with @aevyrie (notes here), part of the problem here is that our PointerId type is questionably designed. There are several small problems with it:

  1. Constructing Uuids is very annoying.
  2. Storing the id inside of each variant, rather than splitting the ID apart from the variants encourages inconsistent design and makes matching harder.
  3. The Custom variant has unclear semantics. When mocking inputs for tests, you almost always want to pretend to be a real mouse or touch (often at a level above this, sending raw input events). But when sending picking events for "click on the focused element", it's not clear what whether you should pretend to be a mouse, a custom input, or even a touch.

Solution

  1. Store the id of each pointer separately from the kind of pointer it is.
  2. Standardize on a u64 identifier, and cut uuid from bevy_picking completely.
  3. Add a PointerKind::Virtual variant with clear semantics for this sort of software-driven pointer that doesn't correspond to real hardware.

Future work

It would be nice to support multiple mice at some point in the future (I've had creative coding users asking me for this). Unfortunately, while this PR ensures that bevy_picking could easily accommodate it, winit still assumes a single mouse (and keyboard).

Unsurprisingly, patching winit is outside of the scope of this PR.

Testing

Existing tests pass. The picking examples seem to work fine, although I haven't tested them with touch inputs.

Migration Guide

PointerId has been refactored for consistency. Check the kind field to see the PointerKind of the pointer, and read the id field to recover the unique identifier.

To construct a PointerId::Mouse, use the PointerId::MOUSE constant. If you were using PointerId::Custom, either use PointerKind::Mouse (if you were mocking inputs for tests) or use PointerKind::Virtual if you were emulating a gamepad-controlled pointer or mocking pointer events to interact with various objects.

@alice-i-cecile alice-i-cecile added C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward A-Picking Pointing at and selecting objects of all sorts labels Feb 11, 2025
id: 0,
kind: PointerKind::Mouse,
};

/// Returns true if the pointer is a touch input.
pub fn is_touch(&self) -> bool {
Copy link
Member Author

Choose a reason for hiding this comment

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

I've left these utility methods alone to ease migration, but I'm not convinced they're particularly valuable now.

pointer_id: PointerId::Mouse,
pointer_id: PointerId {
id: 0,
kind: PointerKind::Virtual,
Copy link
Member Author

Choose a reason for hiding this comment

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

No more pretending to be a mouse!

/// controlled cursor.
#[reflect(ignore)]
Custom(Uuid),
pub struct PointerId {
Copy link
Member

Choose a reason for hiding this comment

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

Given that PointerId is now a common "thing" maybe it should just be PointerId(u64) and PointerKind would just be stored adjacent to it in places where that is relevant (ex: as a separate Component)? To distinguish between two pointers, don't we only need to compare / hash the ID, not the kind?

Copy link
Member

@cart cart Feb 12, 2025

Choose a reason for hiding this comment

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

If it is not a common thing, then I think we should move back to a typed-wrapper to protect against comparing "unlike" ids across types.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right now (and before this PR), the IDs are only meaningfully used by touches. In theory you could encode information for Virtual / Custom types in the u64 / UUID, but I'm not sure what you would use that for.

In either case, in order for two PointerId to match, both the kind and the number associated with it must match. As a result, I don't think we should split these up, but I'm fine to go back to an enum design here. Which do you prefer?

  1. The current design, with split Id + kind.
  2. enum PointerId, only Touch gets a u64.
  3. enum PointerId, each kind has a u64.

I think I prefer solution 2, but went with solution 1 to ease migration.

Copy link
Member

Choose a reason for hiding this comment

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

I also prefer solution (2). I think thats the move.

/// A pointer that is controlled by a mouse.
///
/// Not a rodent; the device that you move around on a desk.
Mouse,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that PointerKind is meant to describe the behaviour of the pointer, not the actual input device, but it seems a bit unclear. It would be nice to be able to support things like a "mouse" pointer that's controlled by a joystick or a mouse controlled first person pov with diegetic touch screen UI.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the intent is that things like UI libraries will match on this variant, and behave accordingly.

@NthTensor
Copy link
Contributor

I don't currently have time for review, but I'd like us to consider that winit is shipping a pointer abstraction in it's next major release that does away with the distinction between mouse and touch entirely.

@alice-i-cecile
Copy link
Member Author

We could just do a single u64 then 🤔 I'd prefer to reduce controversy and get this merged though; there's relatively important work (like removing Interaction) that this is soft-blocking.

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
#[reflect(Default, Debug, Hash, PartialEq)]
pub enum PointerKind {
/// A pointer that is controlled by a mouse.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// A pointer that is controlled by a mouse.
/// A pointer that behaves like a traditional mouse cursor.

///
/// Not a rodent; the device that you move around on a desk.
Mouse,
/// A pointer that is controlled by a touch input.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// A pointer that is controlled by a touch input.
/// A pointer that behaves like it is controlled by touch.

Like an obvious thing I think we'd want to support here is touch input emulation with the mouse?

Comment on lines +31 to +34
/// A stable identifier for a specific pointer.
///
/// This is typically provided by winit,
/// and is unique for each touch input or mouse.
Copy link
Contributor

@ickshonpe ickshonpe Feb 13, 2025

Choose a reason for hiding this comment

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

This feels misleading. The id here corresponds to a specific touch on the device and it's only guaranteed to be unique for the duration of that touch I think.

@ickshonpe
Copy link
Contributor

ickshonpe commented Feb 13, 2025

We could just do a single u64 then 🤔 I'd prefer to reduce controversy and get this merged though; there's relatively important work (like removing Interaction) that this is soft-blocking.

Maybe we shouldn't have the id here at all? The pointer entity which is spawned when the touch starts and despawned after it ends can be used to identify the touch within bevy. Then we just need to add a winit touch id -> pointer entity map somewhere that we can look up when we dispatch touch events.

edit: Implemented in PR #17847

Copy link
Contributor

@colepoirier colepoirier left a comment

Choose a reason for hiding this comment

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

The code looks correct to me, but given the comments of Cart in this PR and Aevyrie in ickshonpe's pr, I don't feel I have the requisite domain knowledge to properly weigh in on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Picking Pointing at and selecting objects of all sorts C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants