Documentation
¶
Overview ¶
Package gesture implements a gesture recognition system for gogpu/ui.
The gesture package provides infrastructure for recognizing user input patterns such as clicks, drags, long presses, and combined tap-and-drag sequences from a stream of pointer events. It sits at the infrastructure layer alongside focus/, overlay/, state/, and animation/.
Architecture ¶
The system is based on Flutter's GestureArena protocol (source-verified):
- Arena manages gesture disambiguation for a single window.
- Recognizer is the interface for all gesture recognizers.
- RecognizerBase provides shared tracking logic.
- Concrete recognizers (ClickRecognizer, DragRecognizer, LongPressRecognizer, TapAndDragRecognizer) implement specific gesture patterns.
Arena Protocol ¶
When a PointerDown event occurs, all interested recognizers register in the arena for that pointer ID. As pointer events arrive, recognizers evaluate whether the gesture matches their pattern. A recognizer calls Arena.Resolve(Accepted) to claim victory or Resolve(Rejected) to withdraw.
- Resolve(Accepted) while arena is open: stored as eager winner.
- Resolve(Rejected): member removed, RejectGesture called.
- Arena closes (end of PointerDown dispatch): if 1 member remains, it wins.
- Sweep (after PointerUp): first remaining member wins.
- Hold/Release: defers sweep (used by multi-tap between taps).
Dependency Rules ¶
gesture/ imports only Layer 1 packages (event/, geometry/) and infrastructure (state/). It does NOT import widget/, core/, app/, theme/, or any external rendering libraries.
Signals Integration ¶
Gesture recognizers support opt-in reactive signals via functional options. For example, WithDraggingSignal binds a state.Signal to the drag state. Signals use equality suppression for bool values to prevent redundant notifications.
Index ¶
- Constants
- func SlopForDevice(kind DeviceKind) float32
- type Arena
- func (a *Arena) Add(pointerID int, member ArenaMember) ArenaEntry
- func (a *Arena) Close(pointerID int)
- func (a *Arena) Hold(pointerID int)
- func (a *Arena) IsHeld(pointerID int) bool
- func (a *Arena) IsResolved(pointerID int) bool
- func (a *Arena) MemberCount(pointerID int) int
- func (a *Arena) Release(pointerID int)
- func (a *Arena) Resolve(pointerID int, member ArenaMember, disposition Disposition)
- func (a *Arena) Route(ev *PointerEvent)
- func (a *Arena) Sweep(pointerID int)
- type ArenaEntry
- type ArenaMember
- type ClickConfig
- type ClickDetails
- type ClickDownDetails
- type ClickOption
- type ClickRecognizer
- type DeviceKind
- type Disposition
- type DragConfig
- type DragDirection
- type DragEndDetails
- type DragOption
- type DragRecognizer
- type DragStartDetails
- type DragUpdateDetails
- type GestureAware
- type LongPressConfig
- type LongPressDetails
- type LongPressMoveDetails
- type LongPressOption
- type LongPressRecognizer
- func (r *LongPressRecognizer) AcceptGesture(pointerID int)
- func (r *LongPressRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
- func (r *LongPressRecognizer) CheckTimer(now time.Duration) bool
- func (r *LongPressRecognizer) Dispose()
- func (r *LongPressRecognizer) HandleEvent(ev *PointerEvent)
- func (r *LongPressRecognizer) RejectGesture(pointerID int)
- type PointerEvent
- type PointerEventType
- type PointerType
- type Recognizer
- type RecognizerBase
- func (r *RecognizerBase) Dispose()
- func (r *RecognizerBase) IsTrackingPointer(pointerID int) bool
- func (r *RecognizerBase) ResolvePointer(pointerID int, disposition Disposition, member ArenaMember)
- func (r *RecognizerBase) SetDeviceKind(pt PointerType)
- func (r *RecognizerBase) SetMemberOverride(m ArenaMember)
- func (r *RecognizerBase) Slop() float32
- func (r *RecognizerBase) StartTrackingPointer(pointerID int, arena *Arena, member ArenaMember)
- func (r *RecognizerBase) StopTrackingPointer(pointerID int)
- type TapAndDragConfig
- type TapAndDragRecognizer
- func (r *TapAndDragRecognizer) AcceptGesture(pointerID int)
- func (r *TapAndDragRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
- func (r *TapAndDragRecognizer) Dispose()
- func (r *TapAndDragRecognizer) HandleEvent(ev *PointerEvent)
- func (r *TapAndDragRecognizer) RejectGesture(pointerID int)
- type TapDragDownDetails
- type TapDragEndDetails
- type TapDragStartDetails
- type TapDragUpDetails
- type TapDragUpdateDetails
- type Team
- type VelocityTracker
Constants ¶
const ( // PressTimeout is the duration before showing visual feedback (ripple). // The recognizer has not yet won the arena at this point. PressTimeout = 100 * time.Millisecond // LongPressTimeout is the duration a pointer must be held without // moving beyond slop to trigger a long-press gesture. LongPressTimeout = 500 * time.Millisecond // DoubleTapTimeout is the maximum time between taps for a multi-click // sequence. If more than this duration elapses between pointer-up and // the next pointer-down, the click count resets to 1. DoubleTapTimeout = 300 * time.Millisecond // DoubleTapMinTime is the minimum time between taps (anti-bounce). // Prevents hardware debounce glitches from being counted as double-taps. DoubleTapMinTime = 40 * time.Millisecond )
Timing thresholds (from Flutter constants.dart, source-verified).
const ( // TouchSlop is the minimum distance a touch pointer must move to be // considered a drag rather than a tap. Accounts for finger imprecision. // 18 logical pixels (Flutter kTouchSlop, Android ViewConfiguration). TouchSlop float32 = 18.0 // PrecisePointerSlop is the minimum distance a mouse or trackpad pointer // must move to be considered a drag. Much smaller than TouchSlop because // precise pointers have sub-pixel accuracy. // 1 logical pixel (Flutter kPrecisePointerHitSlop). PrecisePointerSlop float32 = 1.0 // DoubleTapSlop is the maximum distance between consecutive tap // positions for them to count as a multi-tap sequence (touch only). // Mouse has no distance constraint (cursor stays precise). // 100 logical pixels (Flutter kDoubleTapSlop). DoubleTapSlop float32 = 100.0 )
Spatial thresholds.
const ( // MinFlingVelocity is the minimum velocity (px/s) for a fling gesture. MinFlingVelocity float32 = 50.0 // MaxFlingVelocity caps fling velocity to prevent extreme scrolling. MaxFlingVelocity float32 = 8000.0 )
Velocity thresholds.
const MaxClickCount = 3
MaxClickCount is the maximum click count tracked. Chromium caps at 3 (single, double, triple). Going higher has no standard UI semantic.
Variables ¶
This section is empty.
Functions ¶
func SlopForDevice ¶
func SlopForDevice(kind DeviceKind) float32
SlopForDevice returns the drag detection threshold for the given device kind.
Types ¶
type Arena ¶
type Arena struct {
// contains filtered or unexported fields
}
Arena manages gesture disambiguation for a single window.
When a PointerDown event occurs, all interested recognizers add themselves to the arena for that pointer ID. As pointer events arrive, recognizers evaluate whether the gesture matches their pattern. A recognizer calls Resolve(Accepted) to claim victory or Resolve(Rejected) to withdraw.
Resolution rules (Flutter GestureArenaManager protocol):
- Resolve(Accepted) while arena is open: store as eager winner.
- Resolve(Rejected): remove member, call RejectGesture.
- Arena closes (end of PointerDown dispatch): if eager winner exists, it wins; if 1 member remains, it wins.
- Sweep (after PointerUp): first remaining member wins (last resort).
- Hold/Release: prevents sweep (used by multi-tap between taps).
func (*Arena) Add ¶
func (a *Arena) Add(pointerID int, member ArenaMember) ArenaEntry
Add registers a member in the arena for the given pointer ID. Must be called during PointerDown dispatch; the arena closes at end of dispatch. Returns an ArenaEntry for tracking.
func (*Arena) Close ¶
Close marks the arena for a pointer as closed (no more members can join). Called at the end of PointerDown dispatch. If exactly one member remains or an eager winner exists, resolves immediately.
func (*Arena) Hold ¶
Hold prevents the arena from sweeping for the given pointer ID. Used by multi-click recognizers between taps to defer resolution.
func (*Arena) IsResolved ¶
IsResolved reports whether the arena for the given pointer has been resolved.
func (*Arena) MemberCount ¶
MemberCount returns the number of members currently in the arena for a pointer. Returns 0 if no arena exists for the pointer.
func (*Arena) Release ¶
Release allows the arena to sweep again for the given pointer ID. If sweep was pending, it executes immediately.
func (*Arena) Resolve ¶
func (a *Arena) Resolve(pointerID int, member ArenaMember, disposition Disposition)
Resolve declares the member's disposition for the given pointer ID.
func (*Arena) Route ¶
func (a *Arena) Route(ev *PointerEvent)
Route dispatches a pointer event to all members tracking the given pointer. Called for PointerMove, PointerUp, and PointerCancel events.
type ArenaEntry ¶
type ArenaEntry struct {
// PointerID is the pointer this entry is registered for.
PointerID int
// Member is the arena participant.
Member ArenaMember
}
ArenaEntry is a handle returned by Arena.Add, used for tracking a member's registration in the arena.
type ArenaMember ¶
type ArenaMember interface {
// AcceptGesture is called when this member wins the arena.
// The member should commit its gesture (fire callbacks, transition state).
AcceptGesture(pointerID int)
// RejectGesture is called when this member loses the arena.
// The member should reset its internal state and release resources.
RejectGesture(pointerID int)
}
ArenaMember is the interface that all gesture arena participants implement. Each recognizer that wants to claim a pointer sequence registers as an ArenaMember in the arena for that pointer's ID.
type ClickConfig ¶
type ClickConfig struct {
// MaxClickCount caps the click count. Default: 3 (Chromium standard).
// Set to 1 to detect only single clicks.
MaxClickCount int
// OnClickDown is called when the pointer goes down with the current
// consecutive click count. Useful for visual feedback before the
// arena resolves.
OnClickDown func(details ClickDownDetails)
// OnClick is called when a click sequence completes (pointer up
// within slop and within timing window). Provides the final click count.
OnClick func(details ClickDetails)
// OnClickCancel is called if the click is canceled (pointer moved
// beyond slop, arena lost to another recognizer, pointer canceled).
OnClickCancel func()
}
ClickConfig configures a ClickRecognizer.
type ClickDetails ¶
type ClickDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
ClickCount int
PointerType PointerType
Button event.Button
Modifiers event.Modifiers
Timestamp time.Duration
}
ClickDetails carries information about a completed click.
type ClickDownDetails ¶
type ClickDownDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
ClickCount int
PointerType PointerType
Button event.Button
Modifiers event.Modifiers
Timestamp time.Duration
}
ClickDownDetails carries information about a pointer-down in a click sequence.
type ClickOption ¶
type ClickOption func(*ClickRecognizer)
ClickOption configures a ClickRecognizer via functional options.
func WithPressedSignal ¶
func WithPressedSignal(sig state.Signal[bool]) ClickOption
WithPressedSignal returns a ClickOption that populates the given signal with the pressed state (true while pointer is down, false otherwise).
type ClickRecognizer ¶
type ClickRecognizer struct {
RecognizerBase
// contains filtered or unexported fields
}
ClickRecognizer detects single-click, double-click, and triple-click sequences. Click count is synthesized from timing and position constraints, replacing the platform-dependent MouseDoubleClick event type.
State machine:
ready -> possible (PointerDown, start deadline timer) -> accepted (arena won, PointerUp -> fire OnClick with ClickCount) -> rejected (moved > slop, canceled, arena lost)
func NewClickRecognizer ¶
func NewClickRecognizer(cfg ClickConfig, opts ...ClickOption) *ClickRecognizer
NewClickRecognizer creates a recognizer that detects click sequences.
func (*ClickRecognizer) AcceptGesture ¶
func (r *ClickRecognizer) AcceptGesture(pointerID int)
AcceptGesture is called when this recognizer wins the arena.
func (*ClickRecognizer) AddPointer ¶
func (r *ClickRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
AddPointer is called when a new pointer goes down. The click recognizer is always interested in pointer-down events for click detection.
func (*ClickRecognizer) HandleEvent ¶
func (r *ClickRecognizer) HandleEvent(ev *PointerEvent)
HandleEvent processes pointer events for the tracked pointer.
func (*ClickRecognizer) RejectGesture ¶
func (r *ClickRecognizer) RejectGesture(pointerID int)
RejectGesture is called when this recognizer loses the arena.
type DeviceKind ¶
type DeviceKind uint8
DeviceKind classifies a pointing device for threshold selection.
const ( // DeviceKindPrecise classifies mouse and trackpad pointers. DeviceKindPrecise DeviceKind = iota // DeviceKindTouch classifies touch and pen pointers. DeviceKindTouch )
func (DeviceKind) String ¶
func (k DeviceKind) String() string
String returns a human-readable name for the device kind.
type Disposition ¶
type Disposition uint8
Disposition is the result of a recognizer's arena evaluation.
const ( // Accepted indicates the recognizer claims victory in the arena. Accepted Disposition = iota // Rejected indicates the recognizer withdraws from the arena. Rejected )
type DragConfig ¶
type DragConfig struct {
// Direction constrains which axis is recognized.
Direction DragDirection
// OnDragStart is called when movement exceeds the slop threshold.
OnDragStart func(details DragStartDetails)
// OnDragUpdate is called for each pointer move during an active drag.
OnDragUpdate func(details DragUpdateDetails)
// OnDragEnd is called when the pointer is released during a drag.
// Includes velocity for fling detection.
OnDragEnd func(details DragEndDetails)
// OnDragCancel is called if the drag is canceled.
OnDragCancel func()
}
DragConfig configures a DragRecognizer.
type DragDirection ¶
type DragDirection uint8
DragDirection constrains which axis the drag recognizer responds to.
const ( // DragDirectionPan allows drag in both axes. DragDirectionPan DragDirection = iota // DragDirectionHorizontal restricts drag to the horizontal axis. DragDirectionHorizontal // DragDirectionVertical restricts drag to the vertical axis. DragDirectionVertical )
func (DragDirection) String ¶
func (d DragDirection) String() string
String returns a human-readable name for the drag direction.
type DragEndDetails ¶
type DragEndDetails struct {
Velocity geometry.Point // Pixels per second at release
PrimaryVelocity float32 // Velocity along the drag axis
}
DragEndDetails carries information about the end of a drag.
type DragOption ¶
type DragOption func(*DragRecognizer)
DragOption configures a DragRecognizer via functional options.
func WithDragPositionSignal ¶
func WithDragPositionSignal(sig state.Signal[geometry.Point]) DragOption
WithDragPositionSignal returns a DragOption that populates the given signal with the current drag position during an active drag.
func WithDraggingSignal ¶
func WithDraggingSignal(sig state.Signal[bool]) DragOption
WithDraggingSignal returns a DragOption that populates the given signal with the current drag state (true while dragging, false otherwise).
type DragRecognizer ¶
type DragRecognizer struct {
RecognizerBase
// contains filtered or unexported fields
}
DragRecognizer detects drag gestures (pan, vertical-only, horizontal-only). Replaces ad-hoc drag logic in Slider, SplitView, ScrollView, and Docking.
State machine:
ready -> possible (PointerDown, accumulate delta) -> accepted (delta > slop, fire OnDragStart) -> updates (PointerMove while accepted, fire OnDragUpdate) -> ended (PointerUp, fire OnDragEnd with velocity)
func NewDragRecognizer ¶
func NewDragRecognizer(cfg DragConfig, opts ...DragOption) *DragRecognizer
NewDragRecognizer creates a recognizer that detects drag gestures.
func (*DragRecognizer) AcceptGesture ¶
func (r *DragRecognizer) AcceptGesture(pointerID int)
AcceptGesture is called when this recognizer wins the arena. This may happen before slop is exceeded (single-member auto-accept). Actual drag start is deferred until slop is exceeded.
func (*DragRecognizer) AddPointer ¶
func (r *DragRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
AddPointer is called when a new pointer goes down.
func (*DragRecognizer) HandleEvent ¶
func (r *DragRecognizer) HandleEvent(ev *PointerEvent)
HandleEvent processes pointer events for the tracked pointer.
func (*DragRecognizer) RejectGesture ¶
func (r *DragRecognizer) RejectGesture(pointerID int)
RejectGesture is called when this recognizer loses the arena.
type DragStartDetails ¶
type DragStartDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
PointerType PointerType
Timestamp time.Duration
}
DragStartDetails carries information about the start of a drag.
type DragUpdateDetails ¶
type DragUpdateDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
Delta geometry.Point // Movement since last update
PrimaryDelta float32 // Movement along the drag axis
Timestamp time.Duration
}
DragUpdateDetails carries information about a drag movement.
type GestureAware ¶
type GestureAware interface {
// GestureHitTest returns the gesture recognizers that should participate
// in the gesture arena for a pointer event at the given position.
//
// pos is in widget-local coordinates (relative to the widget's own
// origin). The hit-test framework translates window coordinates to
// widget-local space before calling this method.
//
// Leaf widgets (Button, Checkbox, etc.) should always return their
// recognizers — the framework already confirmed the point is within
// the widget's bounds.
//
// Container widgets with partial interactive areas (Collapsible header,
// TabView tab strip, Docking zone tabs) MUST check whether pos falls
// within their interactive region before returning recognizers. If pos
// is outside the interactive region, return nil to let child widgets'
// recognizers be the sole participants in the arena.
//
// Implementations should return the same recognizer instances across
// calls (created once in the constructor or Mount), not new instances
// each time. The arena manages recognizer lifecycle per-pointer.
GestureHitTest(pos geometry.Point) []Recognizer
}
GestureAware is an optional interface implemented by widgets that participate in the gesture recognition system.
During PointerDown hit-testing, the Window checks each widget in the hit-test path for GestureAware. Widgets that implement it have their recognizers registered in the gesture arena for that pointer.
Widgets that do not implement GestureAware continue to receive events through the existing Event(ctx, event.Event) path unchanged. This is the same opt-in pattern used by [widget.Focusable] and [widget.RepaintBoundaryMarker].
GestureHitTest receives the pointer position in widget-local coordinates. This allows container widgets with partial interactive regions (e.g., a Collapsible header or TabView tab strip) to return recognizers ONLY when the pointer is within the interactive region. Child widgets' recognizers are then the sole participants in the gesture arena, preventing parent containers from consuming clicks meant for children.
Example — leaf widget (always returns recognizers):
func (b *MyButton) GestureHitTest(_ geometry.Point) []gesture.Recognizer {
return []gesture.Recognizer{b.click}
}
Example — container widget with interactive header:
func (c *Collapsible) GestureHitTest(pos geometry.Point) []gesture.Recognizer {
if !c.headerBounds().Contains(pos) {
return nil // let children handle the gesture
}
return []gesture.Recognizer{c.click}
}
type LongPressConfig ¶
type LongPressConfig struct {
// OnLongPressDown is called after PressTimeout (100ms) if the pointer
// is still within slop. Used for visual feedback (ripple, highlight).
OnLongPressDown func(details LongPressDetails)
// OnLongPress is called when the long-press duration (500ms) is reached.
OnLongPress func(details LongPressDetails)
// OnLongPressMoveUpdate is called if the pointer moves after a
// long-press has been recognized (long-press-drag).
OnLongPressMoveUpdate func(details LongPressMoveDetails)
// OnLongPressUp is called when the pointer is released after a long-press.
OnLongPressUp func(details LongPressDetails)
// OnLongPressCancel is called if the long-press is canceled.
OnLongPressCancel func()
}
LongPressConfig configures a LongPressRecognizer.
type LongPressDetails ¶
type LongPressDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
PointerType PointerType
}
LongPressDetails carries information about a long-press event.
type LongPressMoveDetails ¶
type LongPressMoveDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
Delta geometry.Point
}
LongPressMoveDetails carries movement information during a long-press-drag.
type LongPressOption ¶
type LongPressOption func(*LongPressRecognizer)
LongPressOption configures a LongPressRecognizer via functional options.
func WithLongPressActiveSignal ¶
func WithLongPressActiveSignal(sig state.Signal[bool]) LongPressOption
WithLongPressActiveSignal returns a LongPressOption that populates the given signal with the long-press active state.
type LongPressRecognizer ¶
type LongPressRecognizer struct {
RecognizerBase
// contains filtered or unexported fields
}
LongPressRecognizer detects long-press gestures (hold without moving for 500ms). Required for context menus on touch devices.
Timer implementation uses frame-based polling via CheckTimer, called by the animation scheduler. The recognizer records the PointerDown timestamp and checks elapsed time on each frame tick. This keeps all gesture logic on the main thread, avoiding concurrency issues.
func NewLongPressRecognizer ¶
func NewLongPressRecognizer(cfg LongPressConfig, opts ...LongPressOption) *LongPressRecognizer
NewLongPressRecognizer creates a recognizer that detects long-press gestures.
func (*LongPressRecognizer) AcceptGesture ¶
func (r *LongPressRecognizer) AcceptGesture(pointerID int)
AcceptGesture is called when this recognizer wins the arena. May happen before LongPressTimeout (single-member auto-accept). Actual long press is deferred until timeout via CheckTimer.
func (*LongPressRecognizer) AddPointer ¶
func (r *LongPressRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
AddPointer is called when a new pointer goes down.
func (*LongPressRecognizer) CheckTimer ¶
func (r *LongPressRecognizer) CheckTimer(now time.Duration) bool
CheckTimer checks whether the long-press timeout has been reached. Must be called from the animation frame loop with the current timestamp. This is the frame-based timer approach (no goroutines).
Returns true if the recognizer needs continued animation frames.
func (*LongPressRecognizer) Dispose ¶
func (r *LongPressRecognizer) Dispose()
Dispose releases resources.
func (*LongPressRecognizer) HandleEvent ¶
func (r *LongPressRecognizer) HandleEvent(ev *PointerEvent)
HandleEvent processes pointer events for the tracked pointer.
func (*LongPressRecognizer) RejectGesture ¶
func (r *LongPressRecognizer) RejectGesture(pointerID int)
RejectGesture is called when this recognizer loses the arena.
type PointerEvent ¶
type PointerEvent struct {
event.Base
// EventType is the pointer event type (Down, Up, Move, Cancel).
EventType PointerEventType
// PointerID uniquely identifies this pointer across its lifetime.
// Mouse: always 1. Touch: per-finger. Pen: per-stylus.
PointerID int
// PointerType distinguishes the input device.
PointerType PointerType
// Position is the pointer location relative to the receiving widget.
Position geometry.Point
// GlobalPosition is the pointer location in window coordinates.
GlobalPosition geometry.Point
// Pressure is the normalized pressure (0.0-1.0).
// Mouse: 0.5 when pressed, 0.0 when not. Touch/Pen: actual pressure.
Pressure float32
// TiltX is the pen tilt angle around the X axis in degrees (-90 to 90).
TiltX float32
// TiltY is the pen tilt angle around the Y axis in degrees (-90 to 90).
TiltY float32
// Twist is the pen rotation in degrees (0 to 359).
Twist float32
// ContactWidth is the touch contact width in logical pixels.
// 1.0 for devices without contact geometry (mouse).
ContactWidth float32
// ContactHeight is the touch contact height in logical pixels.
// 1.0 for devices without contact geometry (mouse).
ContactHeight float32
// Button is the button that triggered this event (Down/Up only).
Button event.Button
// Buttons is the bitmask of all currently pressed buttons.
Buttons event.ButtonState
// Delta is the relative movement since the last event.
// Non-zero only during pointer-locked mode.
Delta geometry.Point
// Timestamp is the platform event time for velocity calculation.
// Zero if the platform does not provide timestamps.
Timestamp time.Duration
}
PointerEvent carries unified pointer data for gesture recognition. It extends event.Base with W3C Pointer Events Level 3 fields from gpucontext.PointerEvent, adding widget-relative positioning.
PointerEvent is the sole input type for all Recognizer implementations. The event_bridge constructs these from gpucontext.PointerEvent, enriching them with widget-relative coordinates during tree dispatch.
func (*PointerEvent) String ¶
func (e *PointerEvent) String() string
String returns a human-readable representation of the pointer event.
type PointerEventType ¶
type PointerEventType uint8
PointerEventType indicates the type of pointer event.
const ( // PointerDown indicates a pointer became active (button pressed, finger touched). PointerDown PointerEventType = iota // PointerUp indicates a pointer was deactivated (button released, finger lifted). PointerUp // PointerMove indicates a pointer position changed. PointerMove // PointerCancel indicates the system canceled the pointer sequence. PointerCancel )
func (PointerEventType) String ¶
func (t PointerEventType) String() string
String returns a human-readable name for the pointer event type.
type PointerType ¶
type PointerType uint8
PointerType distinguishes the category of pointing device.
const ( // PointerTypeMouse is a mouse or trackpad pointer. PointerTypeMouse PointerType = iota // PointerTypeTouch is a finger on a touch screen. PointerTypeTouch // PointerTypePen is a stylus or pen input device. PointerTypePen )
func (PointerType) DeviceKind ¶
func (t PointerType) DeviceKind() DeviceKind
DeviceKind returns a classification used for threshold selection. Touch and Pen use touch thresholds; Mouse uses precise thresholds.
func (PointerType) String ¶
func (t PointerType) String() string
String returns a human-readable name for the pointer type.
type Recognizer ¶
type Recognizer interface {
ArenaMember
// AddPointer is called when a new pointer goes down.
// If the recognizer is interested, it should add itself to the arena
// and begin tracking the pointer. If not interested, it should return
// false and will not receive further events for this pointer.
AddPointer(ev *PointerEvent, arena *Arena) bool
// HandleEvent processes a pointer event for a tracked pointer.
// Called for PointerMove, PointerUp, and PointerCancel after AddPointer
// returned true.
HandleEvent(ev *PointerEvent)
// Dispose releases resources. Called when the widget is unmounted.
Dispose()
}
Recognizer is the interface for all gesture recognizers.
Recognizers are stateful objects that observe a stream of PointerEvents and decide whether the sequence matches a specific gesture pattern (click, drag, long-press, pinch, etc.).
Lifecycle:
- AddPointer is called for each PointerDown; the recognizer decides whether to compete in the arena for this pointer.
- HandleEvent receives all subsequent events for tracked pointers.
- The recognizer calls Arena.Resolve(Accepted) or Resolve(Rejected).
- AcceptGesture/RejectGesture is called by the arena.
- Dispose releases resources when the recognizer is removed.
type RecognizerBase ¶
type RecognizerBase struct {
// contains filtered or unexported fields
}
RecognizerBase provides common functionality for recognizer implementations. Embed this in concrete recognizers.
func (*RecognizerBase) Dispose ¶
func (r *RecognizerBase) Dispose()
Dispose resets the base recognizer state.
func (*RecognizerBase) IsTrackingPointer ¶
func (r *RecognizerBase) IsTrackingPointer(pointerID int) bool
IsTrackingPointer reports whether the recognizer is tracking the given pointer.
func (*RecognizerBase) ResolvePointer ¶
func (r *RecognizerBase) ResolvePointer(pointerID int, disposition Disposition, member ArenaMember)
ResolvePointer resolves the arena for a tracked pointer. If a memberOverride is set, the override is used as the member identity so the arena correctly matches the registered participant.
func (*RecognizerBase) SetDeviceKind ¶
func (r *RecognizerBase) SetDeviceKind(pt PointerType)
SetDeviceKind records the device kind from a pointer event.
func (*RecognizerBase) SetMemberOverride ¶
func (r *RecognizerBase) SetMemberOverride(m ArenaMember)
SetMemberOverride sets an ArenaMember that will be registered in the arena instead of the recognizer itself. This is used by Team to ensure the teamMember wrapper (not the inner recognizer) is the arena participant, so that AcceptGesture flows through the captain interception logic.
func (*RecognizerBase) Slop ¶
func (r *RecognizerBase) Slop() float32
Slop returns the drag detection threshold for the current device kind.
func (*RecognizerBase) StartTrackingPointer ¶
func (r *RecognizerBase) StartTrackingPointer(pointerID int, arena *Arena, member ArenaMember)
StartTrackingPointer registers the recognizer in the arena for this pointer. The member parameter is the concrete recognizer (or team wrapper) that should be registered as the arena member. If a memberOverride is set (via SetMemberOverride), it takes precedence over the member parameter.
func (*RecognizerBase) StopTrackingPointer ¶
func (r *RecognizerBase) StopTrackingPointer(pointerID int)
StopTrackingPointer removes tracking for a pointer.
type TapAndDragConfig ¶
type TapAndDragConfig struct {
// OnTapDown is called on pointer down with the current tap count.
OnTapDown func(details TapDragDownDetails)
// OnTapUp is called on pointer up without exceeding drag slop.
OnTapUp func(details TapDragUpDetails)
// OnDragStart is called when movement exceeds slop during a tap sequence.
OnDragStart func(details TapDragStartDetails)
// OnDragUpdate is called for each move during a tap-drag.
OnDragUpdate func(details TapDragUpdateDetails)
// OnDragEnd is called when the pointer is released during a drag.
OnDragEnd func(details TapDragEndDetails)
// OnCancel is called if the gesture is canceled.
OnCancel func()
}
TapAndDragConfig configures a TapAndDragRecognizer.
type TapAndDragRecognizer ¶
type TapAndDragRecognizer struct {
RecognizerBase
// contains filtered or unexported fields
}
TapAndDragRecognizer combines click-count tracking with drag detection. Every callback receives ConsecutiveTapCount, enabling:
- Double-tap + drag = word-by-word selection (TextField)
- Triple-tap + drag = line-by-line selection (TextField)
This is the Flutter TapAndDragGestureRecognizer pattern.
func NewTapAndDragRecognizer ¶
func NewTapAndDragRecognizer(cfg TapAndDragConfig) *TapAndDragRecognizer
NewTapAndDragRecognizer creates a combined tap-and-drag recognizer.
func (*TapAndDragRecognizer) AcceptGesture ¶
func (r *TapAndDragRecognizer) AcceptGesture(pointerID int)
AcceptGesture is called when this recognizer wins the arena.
func (*TapAndDragRecognizer) AddPointer ¶
func (r *TapAndDragRecognizer) AddPointer(ev *PointerEvent, arena *Arena) bool
AddPointer is called when a new pointer goes down.
func (*TapAndDragRecognizer) Dispose ¶
func (r *TapAndDragRecognizer) Dispose()
Dispose releases resources.
func (*TapAndDragRecognizer) HandleEvent ¶
func (r *TapAndDragRecognizer) HandleEvent(ev *PointerEvent)
HandleEvent processes pointer events for the tracked pointer.
func (*TapAndDragRecognizer) RejectGesture ¶
func (r *TapAndDragRecognizer) RejectGesture(pointerID int)
RejectGesture is called when this recognizer loses the arena.
type TapDragDownDetails ¶
type TapDragDownDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
ConsecutiveTapCount int // 1=single, 2=double, 3=triple
PointerType PointerType
Button event.Button
Modifiers event.Modifiers
}
TapDragDownDetails carries pointer-down information with tap count.
type TapDragEndDetails ¶
TapDragEndDetails carries drag-end information.
type TapDragStartDetails ¶
type TapDragStartDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
ConsecutiveTapCount int
PointerType PointerType
}
TapDragStartDetails carries drag-start information with tap count.
type TapDragUpDetails ¶
type TapDragUpDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
ConsecutiveTapCount int
}
TapDragUpDetails carries pointer-up information with tap count.
type TapDragUpdateDetails ¶
type TapDragUpdateDetails struct {
GlobalPosition geometry.Point
LocalPosition geometry.Point
Delta geometry.Point
ConsecutiveTapCount int
}
TapDragUpdateDetails carries drag-update information with tap count.
type Team ¶
type Team struct {
// Captain is the preferred winner when a team member would win.
// If nil, the original winner keeps the victory.
Captain ArenaMember
// contains filtered or unexported fields
}
Team groups recognizers that cooperate rather than compete.
Within a team, when one member would win the arena, the captain (if set) is given the chance to claim instead. This enables widgets like Slider where Tap (click-to-position) and Drag (thumb-drag) should cooperate: if the user starts dragging, the drag recognizer wins without waiting for the tap's timeout.
Flutter equivalent: GestureArenaTeam.
func (*Team) Add ¶
func (t *Team) Add(r Recognizer) Recognizer
Add adds a recognizer to this team. The returned Recognizer is a wrapper that intercepts arena accept to support team captain logic.
type VelocityTracker ¶
type VelocityTracker struct {
// contains filtered or unexported fields
}
VelocityTracker estimates pointer velocity from a stream of timestamped positions. Used by DragRecognizer to provide fling velocity at drag end.
The tracker uses a least-squares linear regression over the most recent samples within a 100ms window. Falls back to simple delta/dt when fewer than 2 valid samples are available.
func NewVelocityTracker ¶
func NewVelocityTracker() *VelocityTracker
NewVelocityTracker creates a new velocity tracker.
func (*VelocityTracker) AddPosition ¶
func (v *VelocityTracker) AddPosition(timestamp time.Duration, position geometry.Point)
AddPosition records a timestamped position sample.
func (*VelocityTracker) Reset ¶
func (v *VelocityTracker) Reset()
Reset clears all recorded samples.
func (*VelocityTracker) SampleCount ¶
func (v *VelocityTracker) SampleCount() int
SampleCount returns the number of samples currently stored.
func (*VelocityTracker) Velocity ¶
func (v *VelocityTracker) Velocity() geometry.Point
Velocity returns the estimated velocity in logical pixels per second. Returns (0,0) if insufficient data is available for estimation.