Skip to content

[Android] Add hover callbacks to Touchable - #4396

Merged
j-piasecki merged 1 commit into
mainfrom
jpiasecki/touchable-hover-android
Aug 10, 2026
Merged

[Android] Add hover callbacks to Touchable#4396
j-piasecki merged 1 commit into
mainfrom
jpiasecki/touchable-hover-android

Conversation

@j-piasecki

Copy link
Copy Markdown
Member

Description

Touchable gains onHoverIn/onHoverOut, reported for a mouse,
trackpad cursor or hovering stylus. The button already tracked hover to
drive its animation — this exposes it to JS as the new
onButtonHoverIn/onButtonHoverOut direct events, omitted from
RawButtonProps since the deprecated buttons never report hover.

  • Reporting follows isHovered && isEnabled, the same expression that
    drives the hover visual, so disabling a hovered button reports a
    hover-out and re-enabling it with the pointer still inside reports a
    hover-in.
  • No hover events arrive while the button is held, so the transitions
    are derived from the touch stream during a press — gated so a press can
    only maintain a hover that was already open, never open one. The pointer
    type carries over from the previous sample, since those events belong to
    the pressing pointer.
  • The payload is snapshotted when the pointer is seen, because the
    events outlive the MotionEvent behind them.

Test plan

yarn test covers the prop forwarding; hover itself needs a device or
emulator with a mouse, trackpad or stylus.

Example code
import React, { useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import {
  GestureHandlerRootView,
  Touchable,
} from 'react-native-gesture-handler';

export default function Example() {
  const [log, setLog] = useState<string[]>([]);
  const callbacks = (source: string) => ({
    onHoverIn: () => setLog((l) => [`${source} onHoverIn`, ...l]),
    onHoverOut: () => setLog((l) => [`${source} onHoverOut`, ...l]),
    onPressIn: () => setLog((l) => [`${source} onPressIn`, ...l]),
    onPressOut: () => setLog((l) => [`${source} onPressOut`, ...l]),
  });

  return (
    <GestureHandlerRootView style={styles.container}>
      <View style={styles.row}>
        <Touchable style={styles.box} {...callbacks('Touchable')}>
          <Text style={styles.text}>Touchable</Text>
        </Touchable>
        <Pressable style={styles.box} {...callbacks('Pressable')}>
          <Text style={styles.text}>Pressable</Text>
        </Pressable>
      </View>
      {log.slice(0, 12).map((entry, i) => (
        <Text key={i}>{entry}</Text>
      ))}
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  row: { flexDirection: 'row', gap: 24, marginBottom: 24 },
  box: {
    width: 120,
    height: 120,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#6941C6',
  },
  text: { color: 'white' },
});

Copilot AI review requested due to automatic review settings August 6, 2026 13:15
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ca33480-c5a2-4a1d-ae1c-0413cbab9c7c

📥 Commits

Reviewing files that changed from the base of the PR and between 0627f6e and 7c6aa7b.

📒 Files selected for processing (1)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added hover-in and hover-out callbacks to touchable buttons.
    • Hover events now include pointer type, coordinates, and native event details.
    • Improved hover tracking during pointer movement, touch interactions, and view lifecycle changes.
  • Bug Fixes
    • Improved pointer-type detection and activity resolution for Android gesture handling.
  • Tests
    • Added coverage verifying hover events are forwarded to touchable callbacks.

Walkthrough

Changes

Button hover support now tracks Android pointer samples, dispatches HoverIn and HoverOut events, and forwards them through Touchable callbacks. Shared extensions resolve activities and map pointer types.

Button hover events

Layer / File(s) Summary
Shared Android context and pointer utilities
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/{react/Extensions.kt,core/GestureHandler.kt}
Activity resolution and pointer-type mapping now use shared extensions.
Android hover event pipeline
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt, packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerButtonEvent.kt
Button views track hover state and pointer samples. Native events include hover coordinates, pointer metadata, and hover event names.
JavaScript hover API
packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx, packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts, packages/react-native-gesture-handler/src/v3/components/Touchable/*, packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts, packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx
Button and Touchable props expose hover callbacks. Touchable forwards native event payloads, and tests cover hover-in and hover-out callbacks.

Sequence Diagram(s)

sequenceDiagram
  participant Pointer
  participant ButtonViewGroup
  participant NativeEvent
  participant Touchable
  Pointer->>ButtonViewGroup: Sends hover MotionEvent
  ButtonViewGroup->>NativeEvent: Creates ButtonEvent with HoverSample
  NativeEvent->>Touchable: Invokes onButtonHoverIn or onButtonHoverOut
  Touchable->>Touchable: Calls onHoverIn or onHoverOut with nativeEvent
Loading

Possibly related PRs

Suggested reviewers: m-bert

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding Android hover callbacks to Touchable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt`:
- Around line 881-886: Guard the hover-state update in the
hoverActiveAtPressStart path so press events only update isHovered,
lastHoverSample, and dispatchHoverEventIfNeeded when the event’s tool type
matches lastHoverSample.pointerType. Ignore mismatched pointer types, preserving
the active stylus or mouse hover session.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 020237d9-5a9d-4017-8f02-a4c16e47ada3

📥 Commits

Reviewing files that changed from the base of the PR and between 73c51c7 and 0f1e612.

📒 Files selected for processing (10)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerButtonEvent.kt
  • packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx
  • packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx
  • packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts
  • packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts
  • packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx
  • packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds JS-visible hover callbacks to the v3 Touchable by introducing new native direct events (onButtonHoverIn/onButtonHoverOut) and wiring them through to Touchable props (onHoverIn/onHoverOut), primarily via Android-native hover tracking.

Changes:

  • Expose Touchable onHoverIn/onHoverOut props and forward them to GestureHandlerButton as onButtonHoverIn/onButtonHoverOut.
  • Add Android-side hover event dispatch with a snapshotted hover sample payload and lifecycle handling (including enabled/disabled transitions).
  • Extend codegen spec + event plumbing and add a Jest test asserting correct payload unwrapping/forwarding.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts Adds public onHoverIn/onHoverOut props and updates omitted native interaction prop set.
packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx Forwards hover callbacks to GestureHandlerButton via memoized internal handlers.
packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts Omits hover direct events from deprecated button raw props surface.
packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts Adds new direct event handlers to the native component spec.
packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx Extends ButtonProps interface with onButtonHoverIn/onButtonHoverOut.
packages/react-native-gesture-handler/src/tests/api_v3.test.tsx Adds test verifying Touchable hover callbacks receive unwrapped native payload.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt Implements Android hover state tracking and dispatches hover in/out direct events.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt Adds Context?.findActivity() helper used for window offset capture.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerButtonEvent.kt Adds hover event name mapping and an event factory for hover samples.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt Reuses the new findActivity() helper for window offset resolution.
Suppressed comments (1)

packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts:121

  • onHoverOut is now part of the public Touchable API, but hover direct events are only dispatched on Android (iOS/web currently won’t invoke this callback). Please clarify the platform support in the JSDoc to prevent consumers from assuming it works everywhere.
    /**
     * Called when a non-touch pointer stops hovering over the component.
     */
    onHoverOut?: ((event: ButtonEvent) => void) | undefined;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@j-piasecki
j-piasecki force-pushed the jpiasecki/touchable-hover-android branch from 0f1e612 to 6d22aa9 Compare August 7, 2026 08:11
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@j-piasecki
j-piasecki force-pushed the jpiasecki/touchable-hover-android branch from 6d22aa9 to 6d06663 Compare August 7, 2026 09:52
@j-piasecki
j-piasecki changed the base branch from main to jpiasecki/pointer-type-align August 7, 2026 09:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt`:
- Around line 1081-1082: Preserve the serialized pointer-type integer contract
by assigning matching explicit values for KEY and OTHER in GestureHandler.kt,
RNGestureHandlerPointerType.h, and src/v3/types/EventTypes.ts; ensure KEY
remains 3 and OTHER remains 4 across all three definitions so legacy and
replayed events decode consistently.
- Around line 1081-1082: Update the pointer type constants so POINTER_TYPE_OTHER
retains its existing numeric value of 3, and assign POINTER_TYPE_KEY a distinct
value instead. Preserve compatibility for native paths interpreting the
established POINTER_TYPE_OTHER value.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c40833e5-4b18-44e7-a4cc-3c34a950e426

📥 Commits

Reviewing files that changed from the base of the PR and between 6d22aa9 and 6d06663.

📒 Files selected for processing (3)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt
  • packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h
  • packages/react-native-gesture-handler/src/v3/types/EventTypes.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt`:
- Around line 1081-1082: Preserve the serialized pointer-type integer contract
by assigning matching explicit values for KEY and OTHER in GestureHandler.kt,
RNGestureHandlerPointerType.h, and src/v3/types/EventTypes.ts; ensure KEY
remains 3 and OTHER remains 4 across all three definitions so legacy and
replayed events decode consistently.
- Around line 1081-1082: Update the pointer type constants so POINTER_TYPE_OTHER
retains its existing numeric value of 3, and assign POINTER_TYPE_KEY a distinct
value instead. Preserve compatibility for native paths interpreting the
established POINTER_TYPE_OTHER value.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c40833e5-4b18-44e7-a4cc-3c34a950e426

📥 Commits

Reviewing files that changed from the base of the PR and between 6d22aa9 and 6d06663.

📒 Files selected for processing (3)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt
  • packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h
  • packages/react-native-gesture-handler/src/v3/types/EventTypes.ts
🛑 Comments failed to post (1)
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt (1)

1081-1082: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 \
  'POINTER_TYPE_(KEY|OTHER)|RNGestureHandler(Key|OtherPointer)|PointerType\.(KEY|OTHER)|pointerType\s*(===|!==|==|!=)' \
  packages/react-native-gesture-handler

Repository: software-mansion/react-native-gesture-handler

Length of output: 15138


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Android pointer type constants and mappings:"
sed -n '880,1083p' packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt

echo
echo "Apple pointer type header:"
cat -n packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h

echo
echo "EventTypes pointer type definition:"
sed -n '1,35p' packages/react-native-gesture-handler/src/v3/types/EventTypes.ts

echo
echo "Read-only semantic check of enum value ordering/mappings:"
python3 - <<'PY'
from pathlib import Path
import re

android = Path('packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt').read_text()
apple = Path(' packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h').read_text() if Path(' packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h').exists() else Path('packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h').read_text()
event = Path('packages/react-native-gesture-handler/src/v3/types/EventTypes.ts').read_text()

def ints(s):
    return list(map(str.strip, s.splitlines()))

android_consts = re.findall(r'const val POINTER_TYPE_(KEY|OTHER|TOUCH|STYLUS|MOUSE)\s*=\s*(\d+)', android)
apple_consts = re.findall(r'RNGestureHandler(KEY|OtherPointer|Touch|Stylus|Mouse)\s*,?\s*$', apple, re.M)
event_type = re.search(r'enum\s+PointerType\s*{(?P<body>.*?)\n}', event, re.S)
print("android_consts", android_consts)
print("apple_consts", apple_consts)
if event_type:
    body = event_type.group('body').strip()
    print("event_types", re.findall(r'([A-Z_]+)\s*(?:=\s*(\d+))?(?:,|$)?', body, re.M))
else:
    print("event_types missing enum")

print("\nAndroid OTHER equals 3?", any(name == 'OTHER' and value == '3' for name, value in android_consts))
print("Apple OtherPointer equals 3?", any(name == 'OtherPointer' for name in apple_consts) and apple_consts.index('OtherPointer') == 3)
print("Apple Key equals 4?", any(name == 'KEY' for name in apple_consts) and apple_consts.index('KEY') == 3)
PY

Repository: software-mansion/react-native-gesture-handler

Length of output: 10129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PointerType files:"
fd -a 'PointerType\.(ts|tsx|js|jsx|mm|m|h)$' packages/react-native-gesture-handler

echo
echo "PointerType.ts full:"
cat -n packages/react-native-gesture-handler/src/PointerType.ts 2>/dev/null || true

echo
echo "Android PointerType-related text:"
rg -n -C 3 'POINTER_TYPE_(TOUCH|STYLUS|MOUSE|KEY|OTHER)|setPointerType|pointerType|ReadableType\.INT|putInt|pointerTypeOf' packages/react-native-gesture-handler/android/src/main/java

echo
echo "Apple Raw pointer type related text:"
rg -n -C 3 'RNGestureHandlerPointerType|RNGestureHandler(Key|OtherPointer)|pointerType|rht_pointer_type|`@dtoa`|putInt|setPointer' packages/react-native-gesture-handler/apple

Repository: software-mansion/react-native-gesture-handler

Length of output: 50401


Preserve the existing pointer-type integer contract

These pointer types are serialized as integers across the shared enum, Apple native enum, and Android constants. With KEY at 3 and OTHER at 4, OldPointer/replayed OTHER events read as 3 map to KEY, and KEY events read by older consumers map as OTHER. Keep explicit compatibility values between the Android constants, Apple enum, and shared PointerType.

📍 Affects 3 files
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt#L1081-L1082 (this comment)
  • packages/react-native-gesture-handler/apple/RNGestureHandlerPointerType.h#L7-L7
  • packages/react-native-gesture-handler/src/v3/types/EventTypes.ts#L19-L19
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt`
around lines 1081 - 1082, Preserve the serialized pointer-type integer contract
by assigning matching explicit values for KEY and OTHER in GestureHandler.kt,
RNGestureHandlerPointerType.h, and src/v3/types/EventTypes.ts; ensure KEY
remains 3 and OTHER remains 4 across all three definitions so legacy and
replayed events decode consistently.

Source: Coding guidelines


🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching GestureHandler.kt:"
fd -a 'GestureHandler\.kt$' . || true

echo
echo "Relevant const values and nearby events:"
file="packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt"
if [ -f "$file" ]; then
  nl -ba "$file" sed -n '1050,1125p'
else
  echo "GestureHandler.kt not found at expected path"
fi

echo
echo "Search for POINTER_TYPE constants/usages:"
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|PEN)|POINTER_TYPE_KEY|POINTER_TYPE_OTHER" packages/react-native-gesture-handler -S || true

echo
echo "Deduce git diff context for these constants if available:"
git diff -- packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt | sed -n '1,220p' || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 1664


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt"

echo "File size and target lines:"
wc -l "$file"
python3 - <<'PY' "$file"
import sys
path=sys.argv[1]
for i,l in enumerate(open(path), 1):
    if 1060 <= i <= 1110:
        print(f"{i:5d}\t{l.rstrip()}")
PY

echo
echo "Relevant const values and usages:"
python3 - <<'PY' "$file"
import re
path=sys.argv[1]
for i,l in enumerate(open(path), 1):
    if re.search(r'POINTER_TYPE_(KEY|OTHER|TOUCH|PEN|TOUCH)', l) or 'KEY' in l or 'OTHER' in l:
        print(f"{i:5d}\t{l.rstrip()}")
PY

echo
echo "Search outside target file: POINTER_TYPE constants/usages:"
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|PEN)|POINTER_TYPE_KEY|POINTER_TYPE_OTHER" packages/react-native-gesture-handler -S || true

echo
echo "Git diff around target file if available:"
git diff -- packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt | sed -n '1,260p' || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 2867


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant POINTER_TYPE usages:"
python3 - <<'PY'
for p in ["packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt"]:
    with open(p, encoding="utf-8") as f:
        for i, line in enumerate(f, 1):
            if "POINTER_TYPE_" in line:
                print(f"{p}:{i}: {line.rstrip()}")
PY

echo
echo "Search all repository POINTER_TYPE references:"
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|STYLUS|MOUSE|PEN)|POINTER_TYPE_KEY|POINTER_TYPE_OTHER" packages/react-native-gesture-handler -S || true

echo
echo "Search TypeScript/native mapping references for pointer type names/numbers:"
rg -n 'pointerType|pointer.*type|POINTER_TYPE|TYPE_OTHER|TYPE_KEY|OTHER|KEY' packages/react-native-gesture-handler -S \
  -g '!android/build' -g '!ios' -g '!apple' -g '!build' -g '!dist' -g '!node_modules' || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 45286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PointerType.ts:"
cat -n packages/react-native-gesture-handler/src/PointerType.ts

echo
echo "TypeScript NativeComponent/spec pointerType code around PointerType.ts:"
python3 - <<'PY'
import re, pathlib
for p in [
    "packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts",
    "packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts",
    "packages/react-native-gesture-handler/src/v3/types/EventTypes.ts",
    "packages/react-native-gesture-handler/src/handlers/gestureHandlerCommon.ts",
]:
    path=pathlib.Path(p)
    if not path.exists():
        print(f"{p} missing")
        continue
    lines=path.read_text().splitlines()
    for i,l in enumerate(lines,1):
        if "pointerType" in l or "PointerType" in l:
            print(f"{p}:{i}: {l}")

echo
echo "Apple/native pointerType definitions and native mappings:"
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|STYLUS|MOUSE|PEN)|pointerType:|PointerType::|PointerProperties|TOOL_TYPE" packages/react-native-gesture-handler/apple packages/react-native-gesture-handler/android -S || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "TypeScript PointerType enum:"
cat -n packages/react-native-gesture-handler/src/PointerType.ts

echo
echo "Pointer type references in native/spec/type files:"
python3 <<'PY'
import pathlib
patterns = ["native", "specs", "EventTypes", "PointerType", "pointerType"]
files = [
    pathlib.Path("packages/react-native-gesture-handler/src/PointerType.ts"),
    pathlib.Path("packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts"),
    pathlib.Path("packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts"),
    pathlib.Path("packages/react-native-gesture-handler/src/v3/types/EventTypes.ts"),
    pathlib.Path("packages/react-native-gesture-handler/src/handlers/gestureHandlerCommon.ts"),
]
for path in files:
    print(f"\n--- {path} ---")
    if not path.exists():
        print("missing")
        continue
    for i, line in enumerate(path.read_text().splitlines(), 1):
        if any(s in line for s in patterns):
            print(f"{i}: {line}")

print("\nApple native pointer type references:")
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|STYLUS|MOUSE|PEN)|pointerType:|PointerType::|PointerProperties|TOOL_TYPE|TOOL_TYPE_KEY|KEY" packages/react-native-gesture-handler/apple packages/react-native-gesture-handler/android -S || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 772


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Apple native pointer type references:"
rg -n "POINTER_TYPE_(KEY|OTHER|TOUCH|STYLUS|MOUSE|PEN)|pointerType:|PointerType::|PointerProperties|TOOL_TYPE|TOOL_TYPE_KEY|KEY|OTHER" packages/react-native-gesture-handler/apple packages/react-native-gesture-handler/android -S || true

Repository: software-mansion/react-native-gesture-handler

Length of output: 35424


Preserve the existing numeric value for POINTER_TYPE_OTHER.

POINTER_TYPE_OTHER moved from 3 to 4, while POINTER_TYPE_KEY now uses 3; make POINTER_TYPE_OTHER retain 3 so older native paths interpreting numeric pointer types do not map new key events as OTHER.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt`
around lines 1081 - 1082, Update the pointer type constants so
POINTER_TYPE_OTHER retains its existing numeric value of 3, and assign
POINTER_TYPE_KEY a distinct value instead. Preserve compatibility for native
paths interpreting the established POINTER_TYPE_OTHER value.

@j-piasecki
j-piasecki force-pushed the jpiasecki/touchable-hover-android branch from 6d06663 to 0627f6e Compare August 7, 2026 12:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt`:
- Around line 35-43: Update the tool-type mapping in MotionEvent.getPointerType
so MotionEvent.TOOL_TYPE_ERASER shares the stylus branch and returns
GestureHandler.POINTER_TYPE_STYLUS, including inverted-stylus hover events;
leave other mappings unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 107a82c6-de60-49f5-8a80-ac7a7fe175a8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d06663 and 0627f6e.

📒 Files selected for processing (4)
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt
  • packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt

Base automatically changed from jpiasecki/pointer-type-align to main August 7, 2026 12:54
@j-piasecki
j-piasecki force-pushed the jpiasecki/touchable-hover-android branch from 0627f6e to 9f98e34 Compare August 7, 2026 12:54
## Description

`Touchable` gains `onHoverIn`/`onHoverOut`, reported for a mouse,
trackpad cursor or hovering stylus. The button already tracked hover to
drive its animation — this exposes it to JS as the new
`onButtonHoverIn`/`onButtonHoverOut` direct events, omitted from
`RawButtonProps` since the deprecated buttons never report hover.

- Reporting follows `isHovered && isEnabled`, the same expression that
drives the hover visual, so disabling a hovered button reports a
hover-out and re-enabling it with the pointer still inside reports a
hover-in.
- No hover events arrive while the button is held, so the transitions
are derived from the touch stream during a press — gated so a press can
only maintain a hover that was already open, never open one. The pointer
type carries over from the previous sample, since those events belong to
the pressing pointer.
- The payload is snapshotted when the pointer is seen, because the
events outlive the `MotionEvent` behind them.

## Test plan

`yarn test` covers the prop forwarding; hover itself needs a device or
emulator with a mouse, trackpad or stylus.

<details>
<summary>Example code</summary>

```tsx
import React, { useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import {
  GestureHandlerRootView,
  Touchable,
} from 'react-native-gesture-handler';

export default function Example() {
  const [log, setLog] = useState<string[]>([]);
  const callbacks = (source: string) => ({
    onHoverIn: () => setLog((l) => [`${source} onHoverIn`, ...l]),
    onHoverOut: () => setLog((l) => [`${source} onHoverOut`, ...l]),
    onPressIn: () => setLog((l) => [`${source} onPressIn`, ...l]),
    onPressOut: () => setLog((l) => [`${source} onPressOut`, ...l]),
  });

  return (
    <GestureHandlerRootView style={styles.container}>
      <View style={styles.row}>
        <Touchable style={styles.box} {...callbacks('Touchable')}>
          <Text style={styles.text}>Touchable</Text>
        </Touchable>
        <Pressable style={styles.box} {...callbacks('Pressable')}>
          <Text style={styles.text}>Pressable</Text>
        </Pressable>
      </View>
      {log.slice(0, 12).map((entry, i) => (
        <Text key={i}>{entry}</Text>
      ))}
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  row: { flexDirection: 'row', gap: 24, marginBottom: 24 },
  box: {
    width: 120,
    height: 120,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#6941C6',
  },
  text: { color: 'white' },
});
```

</details>
@j-piasecki
j-piasecki force-pushed the jpiasecki/touchable-hover-android branch from 9f98e34 to 7c6aa7b Compare August 10, 2026 06:46
@j-piasecki
j-piasecki merged commit 78cec50 into main Aug 10, 2026
9 checks passed
@j-piasecki
j-piasecki deleted the jpiasecki/touchable-hover-android branch August 10, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants