Skip to content

TapGestureRecognizer and PointerGestureRecognizer lack public controller interfaces for custom backends #34161

@Redth

Description

@Redth

Related to

Problem

Custom platform backends need to fire gesture events when native gestures are recognized. MAUI provides public controller interfaces for some gesture types but not others:

Gesture Public Interface Status
Swipe ISwipeGestureController ✅ Public — SendSwipe() + DetectSwipe()
Pinch IPinchGestureController ✅ Public — SendPinch() etc.
Pan IPanGestureController ✅ Public — SendPan() etc.
Tap ❌ None SendTapped() is internal
Pointer ❌ None SendPointerEntered/Exited/Moved/Pressed/Released() are all internal

TapGestureRecognizer

TapGestureRecognizer.SendTapped(View) is internal. A custom backend must use reflection:

var sendTapped = typeof(TapGestureRecognizer).GetMethod(
    "SendTapped", BindingFlags.Instance | BindingFlags.NonPublic);
sendTapped.Invoke(gesture, new object[] { parentView });

An ITapGestureController with a public SendTapped() method (matching the pattern of ISwipeGestureController) would solve this.

PointerGestureRecognizer

SendPointerEntered(View, Func<IElement?, Point?>, PlatformPointerEventArgs, ButtonsMask) and the other SendPointer* methods are all internal. Custom backends must reflect:

var sendEntered = typeof(PointerGestureRecognizer).GetMethod(
    "SendPointerEntered", BindingFlags.Instance | BindingFlags.NonPublic);
sendEntered.Invoke(gesture, new object[] { view, getPositionFunc, null, ButtonsMask.Primary });

An IPointerGestureController interface (or making the existing SendPointer* methods public) would allow backends to properly implement pointer tracking without reflection.

Impact

Without these public APIs, custom platform backends (macOS/AppKit, Linux/GTK, etc.) are:

  • Forced to use reflection, which is fragile across MAUI versions
  • Subject to trimming/AOT failures since the internal methods aren't preserved
  • Unable to properly validate parameter signatures at compile time (we shipped a bug where we passed 2 args to a 4-param method, silently failing)

Proposed Solution

Add public controller interfaces matching the existing pattern:

public interface ITapGestureController
{
    void SendTapped(View sender);
}

public interface IPointerGestureController
{
    void SendPointerEntered(View sender, Func<IElement?, Point?>? getPosition);
    void SendPointerExited(View sender, Func<IElement?, Point?>? getPosition);
    void SendPointerMoved(View sender, Func<IElement?, Point?>? getPosition);
    void SendPointerPressed(View sender, Func<IElement?, Point?>? getPosition);
    void SendPointerReleased(View sender, Func<IElement?, Point?>? getPosition);
}

Or alternatively, make the existing internal methods public.

Metadata

Metadata

Assignees

No one assigned

    Labels

    s/triagedIssue has been reviewed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions