-
Notifications
You must be signed in to change notification settings - Fork 920
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
Implement the pointer event overhaul #3876
Conversation
625265c
to
fafa90d
Compare
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.
Awesome! I'm all for getting closer to the web spec, since there's clearly a lot of people that have thought longer about the problem than I ever have, nor will! And thanks for PR-ing it, it's much easier to talk about it when seeing the code!
Only the PointerLeft.position
definition is a blocking concern for me (the macOS impl now doesn't emit PointerLeft
because of it), the rest do not need to be resolved for this to still be an improvement.
/// A [`WindowEvent::PointerLeft`] without a [`WindowEvent::PointerButton`] with | ||
/// [`ElementState::Released`] event is emitted when the system has canceled tracking this | ||
/// touch, such as when the window loses focus, or on mobile devices if the user moves the | ||
/// device against their face. |
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 behaviour of getting Pressed
without Released
when a touch phase is cancelled, is IMO quite subtle. Perhaps we should rather expose PointerCancel
?
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.
We have attempted to resolve this issue in length the other meeting, the outcome was not satisfactory, we should bring it up next meeting as well maybe together we can come up with a better solution.
In the previous proposal we had indeed TouchCancel
, because it only applies to touch.
It might be a good idea indeed to implement PointerCancel
and actually use it for other pointer types apart from touch?
This is definitely a weakness of this proposal, if we can't address it in this PR I will open an issue for it.
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.
Apologies, summarizing the context:
The reason why we couldn't come up with a good idea/didn't like TouchCancel
is because it actually goes against the idea of the unified pointer model, where users don't need special handling to correctly handle any pointer type.
So folding it into PointerLeft
without PointerButton
with Released
would make this "natural".
However for users who actually want to handle touch events explicitly, its harder to make out TouchCancel
.
So a PointerCancel
event makes much more sense, because it solves this issue.
But I don't know how we can use it for anything else apart from touch.
However, that said, we can just use it for touch only, as long as users handle it regardless of pointer type, it should fulfill the original purpose.
I like it so far.
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.
On another thought, this is also how Web works, another reason to switch to this new event.
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 fine with leaving this to be figured out 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.
PointerLeave
has nothing to do with focus.
While it is true that if pointer focus is in play you won't get a PointerLeave
until you release the pointer focus, this doesn't mean that any other scenario is not relevant.
PointerLeave
means the pointer has left the surface, with the exception of pointer focus, in which case you will get PointerLeave
upon loosing pointer focus if the pointer is not inside the surface.
PointerCancel
means that input should be discarded.
While in the case of touch input, PointerLeave
and PointerCancel
are the same thing, as @madsmtm pointed out, the problem is that it is currently hard to figure out when that actually happens because it depends on not receiving a PointerButton
with Released
.
While it is possible that users just assume that PointerLeave
means both and so they can handle it the same, it is not the same as pointed out above. Users who need to distinguish between these two scenarios will have a harder time doing so without a dedicated event.
I think the discussion should rather be around if this differentiation is actually needed, personally I don't know about that. On the other hand W3C deemed it necessary, so maybe digging there could yield something useful.
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.
PointerLeave has nothing to do with focus.
pointer focus, sorry.
PointerLeave means the pointer has left the surface, with the exception of pointer focus, in which case you will get PointerLeave upon loosing pointer focus if the pointer is not inside the surface.
PointerCancel means that input should be discarded.
This is not the case for grabs, if you want to do it that way, then you'd need to update all the implementations to account for that, which I don't think worth it, since you generally want a pointer focus, which what it models right now, at least on X11/Wayland.
While it is possible that users just assume that PointerLeave means both and so they can handle it the same, it is not the same as pointed out above. Users who need to distinguish between these two scenarios will have a harder time doing so without a dedicated event.
I'll just say that you can get leave on regular mouse devices without releasing the button meaning that you should cancel out everything.
In general, I don't mind having a cancel since I have a case on wayland where you can remove the pointer all together meaning that you likely have to cancel everything/same for keyboard/touch/etc.
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 not the case for grabs, if you want to do it that way, then you'd need to update all the implementations to account for that, which I don't think worth it, since you generally want a pointer focus, which what it models right now, at least on X11/Wayland.
The way I described is how it works currently for Web and X11.
I assume this is how it works for every other backend as well already.
If not we should figure out how it works currently and how we want it to work!
What do you mean with "grab"? Don't you mean "pointer focus" with that?
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 mean that if you press the button and move the pointer out of the window, you won't get the leave
until you release the button, the coordinates client will be getting will be outside of the window, but the pointer is still focusing the window, because you're holding the button.
So if you treat leave as left the surface area it means that you should detect when the mouse is not over the window and sent event that it left, but I'd say that all backends prevent pointer leave until you release everything...
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.
Cool, we agree then on what PointerLeave
means, seems this was a misunderstanding then!
fafa90d
to
0d359e4
Compare
0d359e4
to
d7e7e31
Compare
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.
macOS and iOS impls look good now, thanks!
src/platform_impl/android/mod.rs
Outdated
device_id, | ||
state: event::ElementState::Pressed, | ||
position, | ||
button: event::ButtonSource::Touch { finger_id, force }, |
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 guess we should differentiate the source here as well? Android also supports pens and mouse input, and getting at least mouse input represented accurately seems like a sensible thing to do in this PR?
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.
Mouse handling needs a bit more work, so I left it to a follow-up PR.
But I implemented differentiating the different tool types.
Entirely skipping the mouse tool type here shouldn't be a regression because before this PR it would show up as touch, which would have been wrong anyway.
Let me know what you think!
aa3509e
to
9ecf564
Compare
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.
What I've commented on Wayland should apply to the rest, but in general it looks ok, I'd say.
/// A [`WindowEvent::PointerLeft`] without a [`WindowEvent::PointerButton`] with | ||
/// [`ElementState::Released`] event is emitted when the system has canceled tracking this | ||
/// touch, such as when the window loses focus, or on mobile devices if the user moves the | ||
/// device against their face. |
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 mean, your PointerCancel
is PointerLeave
because it means that you effectively lost the focus and thus shouldn't do anything in reaction to pointer input, it's the same for mouse/tablet and touch cancel being here makes sense, at least to me, since I was the one suggesting to do it that way.
Maybe it's not clear what PointerLeave
actually does? Since what it says that you lost focus it's not like you left the surface since it won't be emitted in case you have on-going grabs, so it's effectively a cancel of the events, just with position attached.
If we add a generic PointerCancel
it won't be any different than PointerLeave
because that's what it does.
# Objective Correctly order picking events. Resolves #5984. ## Solution Event ordering [very long standing problem](aevyrie/bevy_mod_picking#294) with mod picking, stemming from two related issues. The first problem was that `Pointer<T>` events of different types couldn't be ordered, but we have already gotten around that in the upstream by switching to observers. Since observers run in the order they are triggered, this isn't an issue. The second problem was that the underlying event streams that picking uses to create it's pointer interaction events *also* lacked ordering, and the systems that generated the points couldn't interleave events. This PR fixes that by unifying the event streams and integrating the various interaction systems. The concrete changes are as follows: + `bevy_winit::WinitEvent` has been moved to `bevy_window::WindowEvent`. This provides a unified (and more importantly, *ordered*) input stream for both `bevy_window` and `bevy_input` events. + Replaces `InputMove` and `InputPress` with `PointerInput`, a new unified input event which drives picking and interaction. This event is built to have drop-in forward compatibility with [winit's upcoming pointer abstraction](rust-windowing/winit#3876). I have added code to emulate it using the current winit input abstractions, but this entire thing will be much more robust when it lands. + Rolls `pointer_events` `send_click_and_drag_events` and `send_drag_over_events` into a single system, which operates directly on `PointerEvent` and triggers observers as output. The PR also improves docs and takes the opportunity to refactor/streamline the pointer event dispatch logic. ## Status & Testing This PR is now feature complete and documented. While it is theoretically possible to add unit tests for the ordering, building the picking mocking for that will take a little while. Feedback on the chosen ordering of events is within-scope. ## Migration Guide For users switching from `bevy_mod_picking` to `bevy_picking`: + Instead of adding an `On<T>` component, use `.observe(|trigger: Trigger<T>|)`. You may now apply multiple handlers to the same entity using this command. + Pointer interaction events now have semi-deterministic ordering which (more or less) aligns with the order of the raw input stream. Consult the docs on `bevy_picking::event::pointer_events` for current information. You may need to adjust your event handling logic accordingly. + `PointerCancel` has been replaced with `Pointer<Cancled>`, which now has the semantics of an OS touch pointer cancel event. + `InputMove` and `InputPress` have been merged into `PointerInput`. The use remains exactly the same. + Picking interaction events are now only accessible through observers, and no `EventReader`. This functionality may be re-implemented later. For users of `bevy_winit`: + The event `bevy_winit::WinitEvent` has moved to `bevy_window::WindowEvent`. If this was the only thing you depended on `bevy_winit` for, you should switch your dependency to `bevy_window`. + `bevy_window` now depends on `bevy_input`. The dependencies of `bevy_input` are a subset of the existing dependencies for `bevy_window` so this should be non-breaking.
9ecf564
to
b1ebafa
Compare
5551169
to
4e9719c
Compare
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 rebased the PR, however I decided to not go for the PointerEntered
/PointerLeft
handling that I was suggesting. The main reason for that is that:
When you press a click with a pointer from e.g. unfocused window state, you generally have a sequence of Enter -> Button
, and in generally, if you do button input, you have a preceding event of moving it in place. So having only a single Enter
/Leave
will
make button jump without any other event, which is a bit more inconsistent in my opinion that having more than one PointerEntered
.
Fairly, there's nothing more with multiple PointerEntered
if you account for multi-seat and similar devices.
--
I've rebased and squashed @daxpedda 's commits and added my small doc patch/changelog patch at the end.
@daxpedda could you look into what I've added, so if it's fine I'll merge this PR, since I have no desire to rebase it further.
4e9719c
to
e02b043
Compare
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.
Just a small nit, otherwise LGTM.
Thank you @kchibisov for picking this up!
- Rename `CursorMoved` to `PointerMoved`. - Rename `CursorEntered` to `PointerEntered`. - Rename `CursorLeft` to `PointerLeft`. - Rename `MouseInput` to `PointerButton`. - Add `position` to every `PointerEvent`. - Remove `Touch`, which is folded into the `Pointer*` events. - New `PointerType` added to `PointerEntered` and `PointerLeft`, signifying which pointer type is the source of this event. - New `PointerSource` added to `PointerMoved`, similar to `PointerType` but holding additional data. - New `ButtonSource` added to `PointerButton`, similar to `PointerType` but holding pointer type specific buttons. Use `ButtonSource::mouse_button()` to easily normalize any pointer button type to a generic mouse button. - In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`. - Remove `Force::Calibrated::altitude_angle`. Fixes rust-windowing#3833. Fixes rust-windowing#883. Fixes rust-windowing#336. Co-authored-by: Kirill Chibisov <[email protected]>
41e939a
to
b2a61a6
Compare
Description
This implements #3833, please see it for all the details.
This implementation deviates on
PointerLeft::position
, which is now anOption
, because Windows (and maybe MacOS) doesn't have this kind of information.It also adds
FingerId
toPointerType
, which was an oversight of #3833.Notably I also removed pen/stylus handling in iOS and Windows, which is now emitted as
PointerType::Unknown
.I plan to reintroduce the same code and expand on it in #3810.
#3874 was a side-product of this PR.
Summary of the changes
CursorMoved
toPointerMoved
.CursorEntered
toPointerEntered
.CursorLeft
toPointerLeft
.MouseInput
toPointerButton
.position
to everyPointerEvent
.Touch
, which is folded into thePointer*
events.PointerType
added toPointerEntered
andPointerLeft
, signifying which pointer type is the source of this event.PointerSource
added toPointerMoved
, similar toPointerType
but holding additional data.ButtonSource
added toPointerButton
, similar toPointerType
but holding pointer type specific buttons. UseButtonSource::mouse_button()
to easily normalize any pointer button type to a generic mouse button.DeviceEvent::MouseMotion
toPointerMotion
.Force::Calibrated::altitude_angle
.Follow-ups
TouchPad
andTouchScreen
.Touch
will remain in place for backends who can not differentiate between these pointer types.Addresses #1555.
Addresses #99.
Fixes #336.
Fixes #883.
Fixes #1909.
Replaces #3001.