Skip to content

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 UIGestureRecognizer instances (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.state.

GestureEvent

Snapshot delivered to gesture callbacks.

Tap

Recognize n_taps quick taps.

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; event.scale is relative to activation.

Rotation

Track a two-finger rotation; event.rotation is in radians.

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; None on a descriptor means any 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 gestures= prop.

GestureCallback module-attribute

GestureCallback = Callable[['GestureEvent'], Any]

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

Offset = Union[float, Tuple[float, float]]

A pan activation or failure bound: one number or a (negative_bound, positive_bound) pair.

DEFAULT_PAN_MIN_DISTANCE module-attribute

DEFAULT_PAN_MIN_DISTANCE = 10.0

Points a pan must travel before activating when no other activation criterion is given.

GestureSpec module-attribute

GestureSpec = _BaseGesture

Any gesture descriptor accepted by the gestures= prop.

GestureState

Bases: str, Enum

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).

BEGAN class-attribute instance-attribute

BEGAN = 'began'

CHANGED class-attribute instance-attribute

CHANGED = 'changed'

ENDED class-attribute instance-attribute

ENDED = 'ended'

CANCELLED class-attribute instance-attribute

CANCELLED = 'cancelled'

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 ("tap", "long_press", "pan", "swipe", "fling", "pinch", "rotation").

state GestureState

One of GestureState.

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 None for other gesture kinds.

kind instance-attribute

kind: str

state instance-attribute

state: GestureState

x class-attribute instance-attribute

x: float = 0.0

y class-attribute instance-attribute

y: float = 0.0

absolute_x class-attribute instance-attribute

absolute_x: float = 0.0

absolute_y class-attribute instance-attribute

absolute_y: float = 0.0

translation_x class-attribute instance-attribute

translation_x: float = 0.0

translation_y class-attribute instance-attribute

translation_y: float = 0.0

velocity_x class-attribute instance-attribute

velocity_x: float = 0.0

velocity_y class-attribute instance-attribute

velocity_y: float = 0.0

scale class-attribute instance-attribute

scale: float = 1.0

rotation class-attribute instance-attribute

rotation: float = 0.0

pointer_count class-attribute instance-attribute

pointer_count: int = 1

direction class-attribute instance-attribute

direction: Optional[SwipeDirection] = None

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 (2 for double-tap).

max_distance float

Maximum pointer travel (points) for a touch to still count as a tap.

on_tap class-attribute instance-attribute

on_tap: Optional[GestureCallback] = None

n_taps class-attribute instance-attribute

n_taps: int = 1

max_distance class-attribute instance-attribute

max_distance: float = 12.0

kind class-attribute

kind: str = '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.

on_long_press class-attribute instance-attribute

on_long_press: Optional[GestureCallback] = None

min_duration_ms class-attribute instance-attribute

min_duration_ms: float = 500.0

max_distance class-attribute instance-attribute

max_distance: float = 12.0

kind class-attribute

kind: str = 'long_press'

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:

  1. If dx crosses fail_offset_x or dy crosses fail_offset_y, the pan fails for the rest of the interaction.
  2. Otherwise the pan activates as soon as any one criterion holds: dx crosses active_offset_x; dy crosses active_offset_y; the straight-line travel is at least min_distance; or the pointer speed is at least min_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. None (default) means 10 points unless another activation criterion is set.

min_pointers int

Minimum pointers that must be down.

max_pointers Optional[int]

Maximum pointers allowed; None for no limit.

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
# A horizontal swipe-to-dismiss row inside a vertical list.
gestures.Pan(active_offset_x=(-20, 20), fail_offset_y=(-15, 15), on_change=drag)

min_distance class-attribute instance-attribute

min_distance: Optional[float] = None

min_pointers class-attribute instance-attribute

min_pointers: int = 1

max_pointers class-attribute instance-attribute

max_pointers: Optional[int] = None

active_offset_x class-attribute instance-attribute

active_offset_x: Optional[Offset] = None

active_offset_y class-attribute instance-attribute

active_offset_y: Optional[Offset] = None

fail_offset_x class-attribute instance-attribute

fail_offset_x: Optional[Offset] = None

fail_offset_y class-attribute instance-attribute

fail_offset_y: Optional[Offset] = None

min_velocity class-attribute instance-attribute

min_velocity: Optional[float] = None

kind class-attribute

kind: str = 'pan'

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 and release velocity.

direction Optional[SwipeDirection]

Required direction, or None for any direction.

min_velocity float

Minimum release speed in points/second.

on_swipe class-attribute instance-attribute

on_swipe: Optional[GestureCallback] = None

direction class-attribute instance-attribute

direction: Optional[SwipeDirection] = None

min_velocity class-attribute instance-attribute

min_velocity: float = 300.0

kind class-attribute

kind: str = 'swipe'

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:

gestures.Fling(direction="down", n_pointers=2, on_fling=dismiss)

Attributes:

Name Type Description
on_fling Optional[GestureCallback]

Called once on release with the resolved direction and release velocity.

direction Optional[SwipeDirection]

Required direction, or None for any direction.

n_pointers int

Number of pointers that must participate.

min_velocity float

Minimum release speed in points/second.

on_fling class-attribute instance-attribute

on_fling: Optional[GestureCallback] = None

direction class-attribute instance-attribute

direction: Optional[SwipeDirection] = None

n_pointers class-attribute instance-attribute

n_pointers: int = 1

min_velocity class-attribute instance-attribute

min_velocity: float = 300.0

kind class-attribute

kind: str = 'fling'

Pinch dataclass

Pinch(
    *,
    on_begin: Optional[GestureCallback] = None,
    on_change: Optional[GestureCallback] = None,
    on_end: Optional[GestureCallback] = None,
    enabled: bool = True
)

Bases: _BaseGesture

Track a two-finger pinch; event.scale is relative to activation.

Attributes:

Name Type Description
kind str

kind class-attribute

kind: str = 'pinch'

Rotation dataclass

Rotation(
    *,
    on_begin: Optional[GestureCallback] = None,
    on_change: Optional[GestureCallback] = None,
    on_end: Optional[GestureCallback] = None,
    enabled: bool = True
)

Bases: _BaseGesture

Track a two-finger rotation; event.rotation is in radians.

Attributes:

Name Type Description
kind str

kind class-attribute

kind: str = 'rotation'

GestureGroup dataclass

GestureGroup(
    mode: Literal["simultaneous", "race", "exclusive"],
    children: Tuple[Any, ...] = tuple(),
)

A composition node relating child gestures (or nested groups).

Build instances with Simultaneous, Race, or Exclusive rather than directly.

Attributes:

Name Type Description
mode Literal['simultaneous', 'race', 'exclusive']
children Tuple[Any, ...]

mode instance-attribute

mode: Literal['simultaneous', 'race', 'exclusive']

children class-attribute instance-attribute

children: Tuple[Any, ...] = field(default_factory=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

Compose gestures that may all be active at the same time.

This matches the flat-list default; it exists so simultaneity can be expressed inside Race or Exclusive nodes:

gestures.Race(
    gestures.Simultaneous(gestures.Pinch(...), gestures.Rotation(...)),
    gestures.Pan(...),
)

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 gestures prop. Plain dicts are passed through with relationship metadata attached (no callbacks to route).

required

Returns:

Type Description
List[Dict[str, Any]]

(clean_specs, events) where clean_specs is a list of

Dict[str, Callable[..., Any]]

JSON-ish config dicts (one per leaf gesture, depth-first) and

Tuple[List[Dict[str, Any]], Dict[str, Callable[..., Any]]]

events maps "gesture:<i>" to a router that unpacks the

Tuple[List[Dict[str, Any]], Dict[str, Callable[..., Any]]]

native payload into a GestureEvent and invokes the right

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 Pan velocity for springs and decays.