X Tutup
Skip to content

Add wheel support to Android MAUI views with v120 normalization #3535

@mattleibow

Description

@mattleibow

Overview

Add scroll wheel support to SkiaSharp's Android MAUI views by handling ACTION_SCROLL events in SKTouchHandler and normalizing AXIS_VSCROLL values to the v120 standard (120 = one discrete mouse wheel notch).

Parent issue: #3533

Current State

The Android SKTouchHandler (source/SkiaSharp.Views.Maui/SkiaSharp.Views.Maui.Core/Platform/Android/SKTouchHandler.cs) currently:

  • Handles: MotionEventActions.Down, Move, Up, PointerDown, PointerUp, Cancel
  • Does NOT handle: ACTION_SCROLL
  • All SKTouchEventArgs are constructed with wheelDelta = 0

Platform Details

Native API

MotionEvent with ACTION_SCROLL:

"The motion event contains relative vertical and/or horizontal scroll offsets."
MotionEvent.ACTION_SCROLL

// Received via onGenericMotionEvent(), NOT onTouchEvent()
float vScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
float hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);

Note: ACTION_SCROLL events are delivered through onGenericMotionEvent(), not onTouchEvent(). This is a separate event path from touch/pointer events. Pointer movement uses ACTION_MOVE (not ACTION_POINTER_MOVE, which is not a standard public constant).

Raw Values Per Discrete Mouse Notch

Device AXIS_VSCROLL value Notes
Standard mouse wheel ±1.0 One notch = typically ±1.0
High-res trackpad ±0.1 to ±0.5 Fractional, device-dependent
Fast scroll ±1.0 per event Multiple rapid events

⚠️ Note: The ±1.0 value per mouse notch is the empirically observed default across standard mice and Android devices. The Android documentation does not explicitly guarantee this value — it describes AXIS_VSCROLL as a "relative vertical scroll offset" without specifying units per notch. The ×120 normalization assumes this de-facto standard. If a device produces different values, the normalization will be proportionally off.

Sign Convention

"Positive values indicate scrolling forward (away from the user)"
MotionEvent.AXIS_VSCROLL

  • AXIS_VSCROLL > 0 = scroll up (away from user)
  • AXIS_VSCROLL < 0 = scroll down (toward user)
  • Already matches v120 sign convention — no negation needed

Official Documentation

Normalization Logic

// In SKTouchHandler.cs, handle ACTION_SCROLL:
float axisValue = motionEvent.GetAxisValue(Axis.Vscroll);
int wheelDelta = (int)Math.Round(axisValue * 120.0);
// Mouse notch (1.0) → 120
// Trackpad micro (0.1) → 12
// Trackpad half (0.5) → 60

Expected Results

Input Calculation WheelDelta
Mouse notch up (1.0) round(1.0 × 120) 120
Mouse notch down (-1.0) round(-1.0 × 120) -120
Trackpad micro up (0.1) round(0.1 × 120) 12
Trackpad half down (-0.5) round(-0.5 × 120) -60

Implementation Notes

  • ACTION_SCROLL events come through OnGenericMotionEvent(), not OnTouchEvent(). You may need to register a generic motion listener or override the view's OnGenericMotionEvent.
  • The MAUI Android handler is in source/SkiaSharp.Views.Maui/SkiaSharp.Views.Maui.Core/Platform/Android/SKTouchHandler.cs
  • Fire SKTouchAction.WheelChanged with SKTouchDeviceType.Mouse
  • Use motionEvent.GetX() / motionEvent.GetY() for pointer location
  • Set inContact = false (wheel doesn't imply contact)
  • Feed args.Handled back to return value of OnGenericMotionEvent (return true to consume)
  • Reference: Windows handler in Platform/Windows/SKTouchHandler.cs lines 101-104, 122-124 for pattern

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup