Gestures¶
Native-backed gesture recognition, attached to any view-like element
via the gestures= prop. Descriptors are frozen dataclasses; their
numeric configuration crosses the bridge while callbacks are routed
through the tag-based event channel. Every descriptor accepts
enabled=False to keep it in the list without recognizing;
Pan adds React Native Gesture Handler's
activation criteria (active_offset_x, fail_offset_y,
max_pointers, min_velocity), and
GestureEvent reports the pointer
in both the view's (x, y) and the window's (absolute_x,
absolute_y) coordinate space. Swipe and fling directions are typed
as SwipeDirection. See the
Gestures guide for usage patterns.
Native-backed gesture system with composition and arbitration.
Attach gestures to any view-like element via the gestures= prop:
import pythonnative as pn
from pythonnative import gestures
@pn.component
def Draggable():
tx = pn.use_animated_value(0.0)
ty = pn.use_animated_value(0.0)
def on_end(event):
pn.Animated.spring(tx, to=0.0).start()
pn.Animated.spring(ty, to=0.0).start()
return pn.Animated.View(
pn.Text("Drag me"),
style={"transform": [{"translate_x": tx}, {"translate_y": ty}], "padding": 24},
gestures=[
gestures.Pan(
on_change=pn.Animated.event(translation_x=tx, translation_y=ty),
on_end=on_end,
)
],
)
Each gesture descriptor is a frozen dataclass holding numeric configuration plus user callbacks. The reconciler serializes the configuration into plain dicts for the native handler (so prop diffing never compares closures) and routes the callbacks through the tag-based event channel. Recognition itself is native:
- iOS attaches real
UIGestureRecognizerinstances (PythonNativeKit), so gestures cooperate with scroll views and system gestures. - Android, the browser preview, and headless tests feed raw pointer streams to the renderer core's gesture engine (RFC 0007), which recognizes and arbitrates them on the UI thread with the same rules.
Composition¶
Gestures in a plain gestures=[...] list all recognize
simultaneously (a press ripple plus a pan plus a pinch can all run at
once). To control how gestures interact, wrap them in composition
nodes, which nest arbitrarily:
Simultaneous: members may all activate together (the flat-list default, useful inside other nodes).Race: the first member to activate wins; the rest fail for the remainder of the interaction.Exclusive: priority order. A member may only activate after every member listed before it has failed.Exclusive(double_tap, single_tap)is the classic double-tap-wins arrangement: the single tap fires only after the double-tap window expires.
gestures=[
gestures.Race(
gestures.Pan(on_change=drag),
gestures.LongPress(on_long_press=show_menu),
),
]
Every callback receives a GestureEvent
with position, translation, velocity, scale, and rotation populated as
appropriate for the gesture kind.
Every descriptor accepts enabled=False to keep it in the list (so
sibling indices and composition stay stable) while it neither
recognizes nor takes part in arbitration. Pan
adds React Native Gesture Handler's activation criteria
(active_offset_x, fail_offset_y, max_pointers,
min_velocity, ...); the rules are spelled out on the class and
implemented identically by the core's engine and the iOS recognizers.
Classes:
| Name | Description |
|---|---|
GestureState |
Lifecycle states reported on |
GestureEvent |
Snapshot delivered to gesture callbacks. |
Tap |
Recognize |
LongPress |
Recognize a sustained press. |
Pan |
Track a drag with translation and velocity. |
Swipe |
Recognize a quick directional flick. |
Fling |
Recognize a quick multi-pointer directional flick. |
Pinch |
Track a two-finger pinch; |
Rotation |
Track a two-finger rotation; |
GestureGroup |
A composition node relating child gestures (or nested groups). |
Functions:
| Name | Description |
|---|---|
Simultaneous |
Compose gestures that may all be active at the same time. |
Race |
Compose gestures where only the first to activate wins. |
Exclusive |
Compose gestures by priority: earlier members outrank later ones. |
serialize_gestures |
Flatten gesture descriptors into native config dicts and event routers. |
Attributes:
| Name | Type | Description |
|---|---|---|
SwipeDirection |
A resolved swipe or fling direction; |
|
DEFAULT_PAN_MIN_DISTANCE |
Points a pan must travel before activating when no other activation criterion is given. |
|
GestureSpec |
Any gesture descriptor accepted by the |
SwipeDirection
module-attribute
¶
SwipeDirection = Literal['left', 'right', 'up', 'down']
A resolved swipe or fling direction; None on a descriptor means any direction.
Offset
module-attribute
¶
A pan activation or failure bound: one number or a (negative_bound, positive_bound) pair.
DEFAULT_PAN_MIN_DISTANCE
module-attribute
¶
Points a pan must travel before activating when no other activation criterion is given.
GestureSpec
module-attribute
¶
Any gesture descriptor accepted by the gestures= prop.
GestureState
¶
Lifecycle states reported on GestureEvent.state.
A str enum, so members compare equal to their wire value
(GestureState.ENDED == "ended") and serialize as plain strings
across the native bridge, while callers get exhaustive
match support and autocomplete.
Attributes:
| Name | Type | Description |
|---|---|---|
BEGAN |
The gesture activated (first callback). |
|
CHANGED |
A continuous gesture updated (pan, pinch, rotation). |
|
ENDED |
The gesture completed successfully. |
|
CANCELLED |
The gesture was interrupted (lost arbitration, view unmounted, pointer left the window). |
GestureEvent
dataclass
¶
GestureEvent(
kind: str,
state: GestureState,
x: float = 0.0,
y: float = 0.0,
absolute_x: float = 0.0,
absolute_y: float = 0.0,
translation_x: float = 0.0,
translation_y: float = 0.0,
velocity_x: float = 0.0,
velocity_y: float = 0.0,
scale: float = 1.0,
rotation: float = 0.0,
pointer_count: int = 1,
direction: Optional[SwipeDirection] = None,
)
Snapshot delivered to gesture callbacks.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
Gesture kind ( |
state |
GestureState
|
One of |
x |
float
|
Pointer x-position in the view's coordinate space (points). |
y |
float
|
Pointer y-position in the view's coordinate space (points). |
absolute_x |
float
|
Pointer x-position in the window's coordinate space (points). |
absolute_y |
float
|
Pointer y-position in the window's coordinate space (points). |
translation_x |
float
|
Horizontal displacement since the gesture activated (pan only). |
translation_y |
float
|
Vertical displacement since the gesture activated (pan only). |
velocity_x |
float
|
Horizontal pointer velocity in points/second (pan, swipe, and fling). |
velocity_y |
float
|
Vertical pointer velocity in points/second (pan, swipe, and fling). |
scale |
float
|
Pinch scale factor relative to activation (pinch only). |
rotation |
float
|
Rotation in radians relative to activation (rotation only). |
pointer_count |
int
|
Number of pointers currently down. |
direction |
Optional[SwipeDirection]
|
Resolved swipe/fling direction, or |
Tap
dataclass
¶
Tap(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True,
on_tap: Optional[GestureCallback] = None,
n_taps: int = 1,
max_distance: float = 12.0
)
Bases: _BaseGesture
Recognize n_taps quick taps.
Attributes:
| Name | Type | Description |
|---|---|---|
on_tap |
Optional[GestureCallback]
|
Called once the tap (or multi-tap) completes. |
n_taps |
int
|
Number of consecutive taps required ( |
max_distance |
float
|
Maximum pointer travel (points) for a touch to still count as a tap. |
LongPress
dataclass
¶
LongPress(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True,
on_long_press: Optional[GestureCallback] = None,
min_duration_ms: float = 500.0,
max_distance: float = 12.0
)
Bases: _BaseGesture
Recognize a sustained press.
on_long_press fires as soon as the press has been held for
min_duration_ms (matching UILongPressGestureRecognizer);
on_end fires when the finger lifts.
Attributes:
| Name | Type | Description |
|---|---|---|
on_long_press |
Optional[GestureCallback]
|
Called at activation time. |
min_duration_ms |
float
|
Hold duration required to activate. |
max_distance |
float
|
Maximum pointer travel before the press fails. |
Pan
dataclass
¶
Pan(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True,
min_distance: Optional[float] = None,
min_pointers: int = 1,
max_pointers: Optional[int] = None,
active_offset_x: Optional[Offset] = None,
active_offset_y: Optional[Offset] = None,
fail_offset_x: Optional[Offset] = None,
fail_offset_y: Optional[Offset] = None,
min_velocity: Optional[float] = None
)
Bases: _BaseGesture
Track a drag with translation and velocity.
Once the pan activates it reports on_change for every movement
with translation measured from the activation point, and on_end
with the release velocity.
Activation rules (the same on iOS, Android, and the browser
preview). Before activation, with dx/dy the pointer travel
from where it first touched down:
- If
dxcrossesfail_offset_xordycrossesfail_offset_y, the pan fails for the rest of the interaction. - Otherwise the pan activates as soon as any one criterion holds:
dxcrossesactive_offset_x;dycrossesactive_offset_y; the straight-line travel is at leastmin_distance; or the pointer speed is at leastmin_velocity.
"Crossing" a bound is strict: a single number n means dx > n
when n >= 0 and dx < n when n < 0; a
(negative_bound, positive_bound) pair means dx <
negative_bound or dx > positive_bound, and the pair must satisfy
negative_bound <= 0 <= positive_bound. Following React Native
Gesture Handler, the default min_distance of
DEFAULT_PAN_MIN_DISTANCE (10 points) applies only when none of
the offset or velocity criteria is given; pass min_distance
explicitly to combine it with them.
Pointer counts: the pan tracks only while at least min_pointers
are down. When more than max_pointers touch down, a pan that
hasn't activated fails and an active pan is cancelled.
Attributes:
| Name | Type | Description |
|---|---|---|
min_distance |
Optional[float]
|
Travel (points) that activates the pan. |
min_pointers |
int
|
Minimum pointers that must be down. |
max_pointers |
Optional[int]
|
Maximum pointers allowed; |
active_offset_x |
Optional[Offset]
|
Horizontal travel that activates the pan. |
active_offset_y |
Optional[Offset]
|
Vertical travel that activates the pan. |
fail_offset_x |
Optional[Offset]
|
Horizontal travel that fails the pan. |
fail_offset_y |
Optional[Offset]
|
Vertical travel that fails the pan. |
min_velocity |
Optional[float]
|
Pointer speed in points/second that activates the pan regardless of distance. |
Example
Swipe
dataclass
¶
Swipe(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True,
on_swipe: Optional[GestureCallback] = None,
direction: Optional[SwipeDirection] = None,
min_velocity: float = 300.0
)
Bases: _BaseGesture
Recognize a quick directional flick.
Attributes:
| Name | Type | Description |
|---|---|---|
on_swipe |
Optional[GestureCallback]
|
Called once on release with the resolved
|
direction |
Optional[SwipeDirection]
|
Required direction, or |
min_velocity |
float
|
Minimum release speed in points/second. |
Fling
dataclass
¶
Fling(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True,
on_fling: Optional[GestureCallback] = None,
direction: Optional[SwipeDirection] = None,
n_pointers: int = 1,
min_velocity: float = 300.0
)
Bases: _BaseGesture
Recognize a quick multi-pointer directional flick.
Like Swipe but with a pointer-count
requirement, mirroring React Native Gesture Handler's Fling
(and iOS UISwipeGestureRecognizer with
numberOfTouchesRequired). A two-finger downward fling is a
common dismiss gesture:
Attributes:
| Name | Type | Description |
|---|---|---|
on_fling |
Optional[GestureCallback]
|
Called once on release with the resolved
|
direction |
Optional[SwipeDirection]
|
Required direction, or |
n_pointers |
int
|
Number of pointers that must participate. |
min_velocity |
float
|
Minimum release speed in points/second. |
Pinch
dataclass
¶
Pinch(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True
)
Rotation
dataclass
¶
Rotation(
*,
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
enabled: bool = True
)
GestureGroup
dataclass
¶
GestureGroup(
mode: Literal["simultaneous", "race", "exclusive"],
children: Tuple[Any, ...] = tuple(),
)
event_from_payload
¶
event_from_payload(payload: Dict[str, Any]) -> GestureEvent
Build a GestureEvent from a payload dict.
Unknown keys are dropped so platform handlers can attach extra diagnostics without breaking the public dataclass.
Simultaneous
¶
Simultaneous(*gestures: Any) -> GestureGroup
Race
¶
Race(*gestures: Any) -> GestureGroup
Compose gestures where only the first to activate wins.
As soon as one member activates, every other member fails for the rest of the interaction (its in-progress recognition is abandoned without firing callbacks).
Exclusive
¶
Exclusive(*gestures: Any) -> GestureGroup
Compose gestures by priority: earlier members outrank later ones.
A member may only activate once every member listed before it has
failed. Exclusive(double_tap, single_tap) delays the single
tap until the double-tap window has expired, then fires it; if the
second tap lands in time, only the double tap fires.
serialize_gestures
¶
serialize_gestures(
specs: Sequence[Any],
) -> Tuple[
List[Dict[str, Any]], Dict[str, Callable[..., Any]]
]
Flatten gesture descriptors into native config dicts and event routers.
Composition nodes (Simultaneous,
Race,
Exclusive) are flattened
depth-first; each resulting spec dict carries the relationship
metadata the recognizers need:
"simultaneous": indices this gesture may be active alongside."wait_for": indices that must fail before this gesture may activate.
Two gestures that are not in each other's simultaneous sets
race: the first to activate causes the other to fail. Gestures in
the top-level list (outside any composition node) are mutually
simultaneous.
A gesture with enabled=False keeps its index and its
"enabled": false config so the native side can skip it, but it
is left out of every relationship: nothing waits for it and it
races nobody.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
specs
|
Sequence[Any]
|
The value of an element's |
required |
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
|
Dict[str, Callable[..., Any]]
|
JSON-ish config dicts (one per leaf gesture, depth-first) and |
Tuple[List[Dict[str, Any]], Dict[str, Callable[..., Any]]]
|
|
Tuple[List[Dict[str, Any]], Dict[str, Callable[..., Any]]]
|
native payload into a |
Tuple[List[Dict[str, Any]], Dict[str, Callable[..., Any]]]
|
user callback. |
See also¶
- The Gestures guide walks through taps, drags, activation offsets, and gesture-driven animations.
- Animated pairs with
Panvelocity for springs and decays.