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. 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). - Android runs an equivalent recognizer set in Kotlin (the
pythonnativeGradle module) on top ofMotionEventstreams. - The browser preview feeds DOM pointer events into the pure-Python
GestureArbiterbelow, which doubles as the executable specification for the native ports.
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.
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). |
GestureArbiter |
Turn a raw pointer-event stream into arbitrated gesture payloads. |
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. |
GestureSpec
module-attribute
¶
Any gesture descriptor accepted by the gestures= prop.
EmitFn
module-attribute
¶
emit(gesture_index, payload): the arbiter's output channel.
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,
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[str] = 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). |
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[str]
|
Resolved swipe/fling direction. |
Tap
dataclass
¶
Tap(
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
kind: str = "tap",
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,
kind: str = "long_press",
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,
kind: str = "pan",
min_distance: float = 10.0,
min_pointers: int = 1,
)
Bases: _BaseGesture
Track a drag with translation and velocity.
Activates once the pointer travels min_distance points, then
reports on_change for every movement with translation measured
from the activation point, and on_end with release velocity.
Attributes:
| Name | Type | Description |
|---|---|---|
min_distance |
float
|
Travel (points) required before the pan activates. |
min_pointers |
int
|
Minimum pointers that must be down. |
Swipe
dataclass
¶
Swipe(
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
kind: str = "swipe",
on_swipe: Optional[GestureCallback] = None,
direction: SwipeDirection = "any",
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 |
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,
kind: str = "fling",
on_fling: Optional[GestureCallback] = None,
direction: SwipeDirection = "any",
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 |
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,
kind: str = "pinch",
)
Rotation
dataclass
¶
Rotation(
on_begin: Optional[GestureCallback] = None,
on_change: Optional[GestureCallback] = None,
on_end: Optional[GestureCallback] = None,
kind: str = "rotation",
)
GestureGroup
dataclass
¶
GestureGroup(
mode: Literal["simultaneous", "race", "exclusive"],
children: Tuple[Any, ...] = tuple(),
)
GestureArbiter
¶
Turn a raw pointer-event stream into arbitrated gesture payloads.
One arbiter serves one view. The host backend feeds it normalized
pointer events (positions in the view's coordinate space, times in
seconds, any monotonic clock) and provides an emit callback
that forwards (gesture_index, payload) pairs to
dispatch_event.
Beyond running each gesture's state machine, the arbiter enforces
the relationships computed by
serialize_gestures:
- Two gestures not in each other's
"simultaneous"sets race; when one activates, the other is force-failed for the rest of the interaction. - A gesture with a
"wait_for"set may only activate after all of those gestures have failed. Its output (including a buffered discrete completion, like a single tap waiting out a double-tap window) is held and either flushed on failure of the targets or discarded if a target succeeds.
Specs without relationship metadata (hand-built dicts) default to fully simultaneous, matching pre-composition behavior.
Timing: after each pointer event, hosts should check
next_deadline
and schedule a poll
call for that time (long-press activation and multi-tap windows).
Methods:
| Name | Description |
|---|---|
pointer_down |
Record a pointer press and advance every recognizer. |
pointer_move |
Record pointer travel and advance every recognizer. |
pointer_up |
Record a pointer release and advance every recognizer. |
cancel |
Abort every in-flight gesture (e.g. touch stolen by a scroll parent). |
poll |
Advance time-based recognizers (long-press, multi-tap windows). |
next_deadline |
Earliest time |
has_active_pan |
Whether a pan gesture is currently activated. |
pointer_down
¶
Record a pointer press and advance every recognizer.
pointer_move
¶
Record pointer travel and advance every recognizer.
pointer_up
¶
Record a pointer release and advance every recognizer.
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.
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. |
make_arbiter
¶
Build a GestureArbiter from serialized specs.
See also¶
- The Gestures guide walks through taps, drags, and gesture-driven animations.
- Animated pairs with
Panvelocity for springs and decays.