feat: Support :hover on touch devices#9720
Open
MatiPl01 wants to merge 8 commits into
Open
Conversation
634132b to
7b95326
Compare
A View exposes a single OnTouchListener slot, so attaching both :active and :active-deepest to one view made whichever selector registered second overwrite the first. Funnel both through one shared listener keyed by activeCallbacks and deepestCallbacks so they coexist.
On a touchscreen the pointer-only recognizers never fire for a finger, so :hover did nothing. Apply :hover while a finger is pressing a view and clear it on lift, matching Chromium-based browsers (non-sticky) - the same lifecycle as :active. Both platforms drive it from the per-view press path that :active already uses: iOS adds a min-duration UILongPressGestureRecognizer, Android adds a per-view OnTouchListener shared with :active. Real-pointer hover (trackpad, mouse, stylus) is unchanged.
e72ae7e to
e9e76cd
Compare
7b95326 to
1e20121
Compare
2d0f0b5 to
ddc6205
Compare
…over Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ddc6205 to
e0690e9
Compare
The observer set its state to .failed on touch end/cancel. Because it never sets .began it never claims a touch, so failing was pointless and made UIKit stop delivering the rest of a multi-touch sequence to it (losing the scroll-clear slop and the new-finger recompute). Drop the .failed writes so it stays .possible.
Two parity gaps in the sticky :hover clear paths, each present on one platform only: - iOS: removeWindowObserver did not clear hover, so when a modal/alert became the key window and the observer rebound, the old window's hover stayed stuck. Clear on teardown, mirroring Android. - Android: the window observer had no ACTION_MOVE handling, so a drag past touch slop that the per-view ACTION_CANCEL missed left sticky hover on. Clear on slop, mirroring iOS. Also dedupe the iOS purge-and-teardown into purgeEntries.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
:hoverwas a no-op on touchscreens (the pointer recognizers never fire for a finger). This makes it respond to touch with the sticky model the major browser engines use: a tapped view gains:hoverand keeps it after the finger lifts, clearing only when a later touch lands elsewhere or on scroll.:activestays press-only and distinct; real-pointer hover (trackpad/mouse/stylus) is unchanged.Native-only change (the pseudo-state flow is selector-agnostic). On each touch-down a
:hoverview is hovered when its on-screen bounds contain the point. iOS uses a shared coordinator watching the key window through a passive, non-recognizing gesture recognizer (so it never competes with the:activerecognizers); Android recomputes per view plus aWindow.Callbackobserver to catch taps on blank space.Demo
Tap the box: it turns green and stays green after you lift; tap elsewhere to clear.