Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use TapGestureTracker on all platforms and let is detect double tap too #2526

Merged
merged 16 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 18 additions & 56 deletions Mapsui.UI.Android/MapControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,13 @@ public enum SkiaRenderMode
Software
}

internal class MapControlGestureListener : GestureDetector.SimpleOnGestureListener
{
public EventHandler<GestureDetector.FlingEventArgs>? Fling;
#if NET7_0
public override bool OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
#else
public override bool OnFling(MotionEvent? e1, MotionEvent e2, float velocityX, float velocityY)
#endif
{
if (Fling != null)
{
Fling?.Invoke(this, new GestureDetector.FlingEventArgs(false, e1, e2, velocityX, velocityY));
return true;
}

return base.OnFling(e1, e2, velocityX, velocityY);
}
}

public partial class MapControl : ViewGroup, IMapControl
{
private View? _canvas;
private GestureDetector? _gestureDetector;
private Handler? _mainLooperHandler;
private SkiaRenderMode _renderMode = SkiaRenderMode.Hardware;
private readonly ManipulationTracker _manipulationTracker = new();
private readonly TapGestureTracker _tapGestureTracker = new();

public MapControl(Context context, IAttributeSet attrs) :
base(context, attrs)
Expand Down Expand Up @@ -72,12 +53,6 @@ private void LocalConstructor()

// Pointer events
Touch += MapControl_Touch;
var listener = new MapControlGestureListener(); // Todo: Find out if/why we need this custom gesture detector. Why not the _gestureDetector?
listener.Fling += OnFling;
_gestureDetector?.Dispose();
_gestureDetector = new GestureDetector(Context, listener);
_gestureDetector.SingleTapConfirmed += OnSingleTapped;
_gestureDetector.DoubleTap += OnDoubleTapped;
}

private void CanvasOnPaintSurface(object? sender, SKPaintSurfaceEventArgs args)
Expand Down Expand Up @@ -117,27 +92,6 @@ public SkiaRenderMode RenderMode
}
}

private void OnSingleTapped(object? sender, GestureDetector.SingleTapConfirmedEventArgs e)
{
if (e.Event == null)
return;

var position = GetScreenPosition(e.Event, this);
if (OnWidgetTapped(position, 1, false))
return;
OnInfo(CreateMapInfoEventArgs(position, position, 1));
}

private void OnDoubleTapped(object? sender, GestureDetector.DoubleTapEventArgs e)
{
if (e.Event == null)
return;

var position = GetScreenPosition(e.Event, this);
if (OnWidgetTapped(position, 2, false))
return;
OnInfo(CreateMapInfoEventArgs(position, position, 2));
}

protected override void OnSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
Expand Down Expand Up @@ -175,25 +129,34 @@ public void MapControl_Touch(object? sender, TouchEventArgs args)
if (args.Event is null)
return;

if (_gestureDetector?.OnTouchEvent(args.Event) == true)
return;

var locations = GetTouchLocations(args.Event, this, PixelDensity);

switch (args.Event.Action)
{
case MotionEventActions.Down:
_manipulationTracker.Restart(locations);
if (OnWidgetPointerPressed(locations[0], false))
return;
if (locations.Length == 1)
{
_tapGestureTracker.Restart(locations[0]);
if (OnWidgetPointerPressed(locations[0], false))
return;
}
break;
case MotionEventActions.Move:
if (OnWidgetPointerMoved(locations[0], true, false))
return;
if (locations.Length == 1)
if (OnWidgetPointerMoved(locations[0], true, false))
return;
_manipulationTracker.Manipulate(locations, Map.Navigator.Manipulate);
break;
case MotionEventActions.Up:
// Todo: Add HandleWidgetPointerUp
if (locations.Length == 1)
_tapGestureTracker.IfTap(locations[0], MaxTapGestureMovement * PixelDensity, (p, c) =>
{
if (OnWidgetTapped(p, c, false))
return;
OnInfo(CreateMapInfoEventArgs(p, p, c));

});
_manipulationTracker.Manipulate(locations, Map.Navigator.Manipulate);
Refresh();
break;
Expand Down Expand Up @@ -291,7 +254,6 @@ protected override void Dispose(bool disposing)
_map?.Dispose();
_mainLooperHandler?.Dispose();
_canvas?.Dispose();
_gestureDetector?.Dispose();
}
CommonDispose(disposing);

Expand Down
60 changes: 24 additions & 36 deletions Mapsui.UI.Avalonia/MapControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public partial class MapControl : UserControl, IMapControl, IDisposable
private readonly ConcurrentDictionary<long, MPoint> _pointerLocations = new();
private bool _shiftPressed;
private readonly ManipulationTracker _manipulationTracker = new();
private readonly TapGestureTracker _tapGestureTracker = new();

public MapControl()
{
Expand All @@ -41,9 +42,6 @@ public MapControl()
PointerCaptureLost += MapControl_PointerCaptureLost;
PointerWheelChanged += MapControl_PointerWheelChanged;

Tapped += MapControl_Tapped;
DoubleTapped += MapControl_DoubleTapped;

// Needed to track the state of _shiftPressed because DoubleTapped does not have KeyModifiers.
KeyDown += (s, e) => _shiftPressed = GetShiftPressed(e.KeyModifiers);
KeyUp += (s, e) => _shiftPressed = GetShiftPressed(e.KeyModifiers);
Expand Down Expand Up @@ -84,56 +82,62 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang

private void MapControl_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!IsMouseDown(e))
if (IsHovering(e))
return;

var tapPosition = e.GetPosition(this).ToMapsui();
_pointerLocations[e.Pointer.Id] = tapPosition;

_manipulationTracker.Restart(_pointerLocations.Values.ToArray());

if (OnWidgetPointerPressed(tapPosition, _shiftPressed))
return;

if (_pointerLocations.Count() == 1)
{
_tapGestureTracker.Restart(tapPosition);
_manipulationTracker.Restart(_pointerLocations.Values.ToArray());
if (OnWidgetPointerPressed(tapPosition, _shiftPressed))
return;
}
e.Pointer.Capture(this);
}

private bool IsMouseDown(PointerPressedEventArgs e) => e.GetCurrentPoint(this).Properties.IsLeftButtonPressed;

private void MapControl_PointerMoved(object? sender, PointerEventArgs e)
{
var isHovering = IsHovering(e);

if (OnWidgetPointerMoved(e.GetPosition(this).ToMapsui(), !isHovering, _shiftPressed))
var position = e.GetPosition(this).ToMapsui();
if (OnWidgetPointerMoved(position, !isHovering, _shiftPressed))
return;

if (isHovering)
return; // In case of hovering we just call the widget move event and ignore the event otherwise.

var pointerLocation = e.GetPosition(this).ToMapsui();
_pointerLocations[e.Pointer.Id] = pointerLocation;
_pointerLocations[e.Pointer.Id] = position;

_manipulationTracker.Manipulate(_pointerLocations.Values.ToArray(), Map.Navigator.Manipulate);

RefreshGraphics();
}

private bool IsHovering(PointerEventArgs e)
{
return e.Pointer.Type == PointerType.Mouse && !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed;
}

private void MapControl_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
_pointerLocations.TryRemove(e.Pointer.Id, out _);
e.Pointer.Capture(null);

var pointerPosition = e.GetPosition(this).ToMapsui();
var position = e.GetPosition(this).ToMapsui();
_tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) =>
{
if (OnWidgetTapped(p, c, _shiftPressed))
return;
OnInfo(CreateMapInfoEventArgs(p, p, c));
});
_manipulationTracker.Manipulate(_pointerLocations.Values.ToArray(), Map.Navigator.Manipulate);

Refresh();
}

private bool IsHovering(PointerEventArgs e)
{
return e.Pointer.Type == PointerType.Mouse && !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed;
}

private void MapControl_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
// In Avalonia the touchpad can trigger the mouse wheel event. In that case there are more events and the Delta.Y is a double value,
Expand All @@ -157,22 +161,6 @@ private void MapControl_PointerCaptureLost(object? sender, PointerCaptureLostEve
ClearTouchState();
}

private void MapControl_Tapped(object? sender, TappedEventArgs e)
{
var position = e.GetPosition(this).ToMapsui();
if (OnWidgetTapped(position, 1, _shiftPressed))
return;
OnInfo(CreateMapInfoEventArgs(position, position, 2));
}

private void MapControl_DoubleTapped(object? sender, TappedEventArgs e)
{
var position = e.GetPosition(this).ToMapsui();
if (OnWidgetTapped(position, 2, _shiftPressed))
return;
OnInfo(CreateMapInfoEventArgs(position, position, 2));
}

public override void Render(DrawingContext context)
{
_drawOperation ??= new MapsuiCustomDrawOperation(new Rect(0, 0, Bounds.Width, Bounds.Height), this);
Expand Down
55 changes: 23 additions & 32 deletions Mapsui.UI.Blazor/MapControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ public partial class MapControl : ComponentBase, IMapControl
public int ZoomButton { get; set; } = MouseButtons.Primary;
public int ZoomModifier { get; set; } = Keys.Control;
public string ElementId => _elementId;
/// <summary>
/// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels.
/// </summary>
public int MaxTapGestureMovement { get; set; } = 8;
private MapsuiJsInterop? Interop =>
_interop == null && JsRuntime != null
? _interop ??= new MapsuiJsInterop(JsRuntime)
Expand Down Expand Up @@ -162,17 +158,6 @@ private protected static void RunOnUIThread(Action action)
action();
}

protected void OnDblClick(MouseEventArgs e)
{
Catch.Exceptions(() =>
{
var location = e.ToLocation(_clientRect);
if (OnWidgetTapped(location, 2, GetShiftPressed()))
return;
OnInfo(CreateMapInfoEventArgs(location, location, 1));
});
}

protected void OnMouseDown(MouseEventArgs e)
{
Catch.Exceptions(() =>
Expand All @@ -181,7 +166,7 @@ protected void OnMouseDown(MouseEventArgs e)
_ = UpdateBoundingRectAsync();

var location = e.ToLocation(_clientRect);
_tapGestureTracker.SetDownPosition(location);
_tapGestureTracker.Restart(location);
_manipulationTracker.Restart([]);

if (OnWidgetPointerPressed(location, GetShiftPressed()))
Expand All @@ -195,7 +180,6 @@ protected void OnMouseMove(MouseEventArgs e)
{
var isHovering = !IsMouseButtonPressed(e);
var position = e.ToLocation(_clientRect);
_tapGestureTracker.SetLastMovePosition(position);

if (OnWidgetPointerMoved(position, !isHovering, GetShiftPressed()))
return;
Expand All @@ -213,18 +197,18 @@ protected void OnMouseUp(MouseEventArgs e)
{
Catch.Exceptions(() =>
{
var location = e.ToLocation(_clientRect);
var position = e.ToLocation(_clientRect);

_tapGestureTracker.IfTap((p) =>
_tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) =>
{
if (OnWidgetTapped(p, 1, GetShiftPressed()))
if (OnWidgetTapped(p, c, GetShiftPressed()))
return;
OnInfo(CreateMapInfoEventArgs(p, p, 1));
OnInfo(CreateMapInfoEventArgs(p, p, c));

}, MaxTapGestureMovement * PixelDensity);
});

_manipulationTracker.Manipulate([e.ToLocation(_clientRect)], Map.Navigator.Manipulate);
RefreshData();
_manipulationTracker.Manipulate([position], Map.Navigator.Manipulate);
Refresh();
});
}

Expand Down Expand Up @@ -276,7 +260,7 @@ public void OnTouchStart(TouchEventArgs e)
var locations = e.TargetTouches.ToTouchLocations(_clientRect);
if (OnWidgetPointerPressed(locations[0], GetShiftPressed()))
return;
_tapGestureTracker.SetDownPosition(locations[0]);
_tapGestureTracker.Restart(locations[0]);
_manipulationTracker.Restart(locations);
});
}
Expand All @@ -286,9 +270,13 @@ public void OnTouchMove(TouchEventArgs e)
Catch.Exceptions(() =>
{
var locations = e.TargetTouches.ToTouchLocations(_clientRect);
_tapGestureTracker.SetLastMovePosition(locations[0]);
if (OnWidgetPointerMoved(locations[0], true, GetShiftPressed()))
return;
if (locations.Length == 1)
{
var position = locations[0];
_tapGestureTracker.LastMovePosition = position;
if (OnWidgetPointerMoved(position, true, GetShiftPressed()))
return;
}
_manipulationTracker.Manipulate(locations.ToArray(), Map.Navigator.Manipulate);
});
}
Expand All @@ -297,12 +285,15 @@ public void OnTouchEnd(TouchEventArgs _)
{
Catch.Exceptions(() =>
{
_tapGestureTracker.IfTap((position) =>
if (_tapGestureTracker.LastMovePosition == null)
return;

_tapGestureTracker.IfTap(_tapGestureTracker.LastMovePosition, MaxTapGestureMovement * PixelDensity, (p, c) =>
{
if (OnWidgetTapped(position, 1, GetShiftPressed()))
if (OnWidgetTapped(p, c, GetShiftPressed()))
return;
OnInfo(CreateMapInfoEventArgs(position, position, 1));
}, MaxTapGestureMovement * PixelDensity);
OnInfo(CreateMapInfoEventArgs(p, p, c));
});

RefreshData();
});
Expand Down
2 changes: 1 addition & 1 deletion Mapsui.UI.Blazor/MapControlComponent.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<div @onmouseup=@OnMouseUp @onmousedown=@OnMouseDown
@onmousewheel=@OnMouseWheel @onmousemove=@OnMouseMove
@onkeydown=@OnKeyDown @onkeyup=@OnKeyUp @ondblclick="OnDblClick"
@onkeydown=@OnKeyDown @onkeyup=@OnKeyUp
@ontouchstart=@OnTouchStart @ontouchmove=@OnTouchMove @ontouchend=@OnTouchEnd
@onwheel="OnMouseWheel"
name="@_elementId" id="@_elementId">
Expand Down