From f43fbc61dcc7ccc48b32784bfbf08e3bbd7804ad Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 07:09:17 +0100 Subject: [PATCH 01/15] Make method private because we can --- Mapsui/Manipulations/FlingTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapsui/Manipulations/FlingTracker.cs b/Mapsui/Manipulations/FlingTracker.cs index 1c9130e27f..67b03ab21b 100644 --- a/Mapsui/Manipulations/FlingTracker.cs +++ b/Mapsui/Manipulations/FlingTracker.cs @@ -46,7 +46,7 @@ public void Clear() _events.Clear(); } - public (double vx, double vy) CalcVelocity(long id, long now) + private (double vx, double vy) CalcVelocity(long id, long now) { double distanceX = 0; double distanceY = 0; From 446d9911ac8bfe3f61b1309ff70c95ebeb3b039b Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 07:09:53 +0100 Subject: [PATCH 02/15] Move action parameter to the end like in fling and manipuation --- Mapsui.UI.Blazor/MapControl.cs | 8 ++++---- Mapsui.UI.Eto/MapControl.cs | 4 ++-- Mapsui.UI.Maui/MapControl.cs | 4 ++-- Mapsui.UI.Wpf/MapControl.cs | 8 ++++---- Mapsui/Manipulations/TapGestureTracker.cs | 12 ++++++------ 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index 71ade55f12..83043ede1e 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -215,13 +215,13 @@ protected void OnMouseUp(MouseEventArgs e) { var location = e.ToLocation(_clientRect); - _tapGestureTracker.IfTap((p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p) => { if (OnWidgetTapped(p, 1, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); - }, MaxTapGestureMovement * PixelDensity); + }); _manipulationTracker.Manipulate([e.ToLocation(_clientRect)], Map.Navigator.Manipulate); RefreshData(); @@ -297,12 +297,12 @@ public void OnTouchEnd(TouchEventArgs _) { Catch.Exceptions(() => { - _tapGestureTracker.IfTap((position) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (position) => { if (OnWidgetTapped(position, 1, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(position, position, 1)); - }, MaxTapGestureMovement * PixelDensity); + }); RefreshData(); }); diff --git a/Mapsui.UI.Eto/MapControl.cs b/Mapsui.UI.Eto/MapControl.cs index 02231e99ce..42ebca625c 100644 --- a/Mapsui.UI.Eto/MapControl.cs +++ b/Mapsui.UI.Eto/MapControl.cs @@ -75,12 +75,12 @@ protected override void OnMouseUp(MouseEventArgs e) SetCursorInDefaultMode(); var mouseUpPosition = e.Location.ToMapsui(); - _tapGestureTracker.IfTap((p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, mouseUpPosition, (p) => { if (OnWidgetTapped(p, 1, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); - }, MaxTapGestureMovement * PixelDensity, mouseUpPosition); + }); _manipulationTracker.Manipulate([mouseUpPosition], Map.Navigator.Manipulate); RefreshData(); diff --git a/Mapsui.UI.Maui/MapControl.cs b/Mapsui.UI.Maui/MapControl.cs index 83b8a4df74..563b63a979 100644 --- a/Mapsui.UI.Maui/MapControl.cs +++ b/Mapsui.UI.Maui/MapControl.cs @@ -234,13 +234,13 @@ private void OnTouch(object? sender, SKTouchEventArgs e) _flingTracker.IfFling(e.Id, (vX, vY) => Map.Navigator.Fling(vX, vY, 1000)); _flingTracker.RemoveId(e.Id); - _tapGestureTracker.IfTap((p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement, releasedTouch!, (p) => { if (OnWidgetTapped(location, 1, false)) return; OnInfo(CreateMapInfoEventArgs(location, location, 1)); - }, MaxTapGestureMovement, releasedTouch!); + }); _manipulationTracker.Manipulate(_touches.Values.ToArray(), Map.Navigator.Manipulate); Refresh(); diff --git a/Mapsui.UI.Wpf/MapControl.cs b/Mapsui.UI.Wpf/MapControl.cs index a248ade652..5f54f2fda3 100644 --- a/Mapsui.UI.Wpf/MapControl.cs +++ b/Mapsui.UI.Wpf/MapControl.cs @@ -137,12 +137,12 @@ private void MapControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var position = e.GetPosition(this).ToMapsui(); - _tapGestureTracker.IfTap((p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, position, (p) => { if (OnWidgetTapped(p, 1, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); - }, MaxTapGestureMovement * PixelDensity, position); + }); RefreshData(); @@ -161,12 +161,12 @@ private void MapControl_TouchDown(object? sender, TouchEventArgs e) private void MapControlTouchUp(object? sender, TouchEventArgs e) { var touchUpPosition = e.GetTouchPoint(this).Position.ToMapsui(); - _tapGestureTracker.IfTap((p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, touchUpPosition, (p) => { if (OnWidgetTapped(p, 1, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); - }, MaxTapGestureMovement * PixelDensity, touchUpPosition); + }); } public void OpenInBrowser(string url) diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index 7da75095ba..046a1b44ac 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -9,29 +9,29 @@ public class TapGestureTracker private MPoint? _tapStartPosition; private MPoint? _tapEndPosition; - public void IfTap(Action onTap, double maxTapDistance, MPoint tapEndPosition) + public void IfTap(double maxTapDistance, MPoint tapEndPosition, Action onTap) { if (_tapStartPosition == null) return; if (tapEndPosition == null) return; // Note, this uses the tapEndPosition parameter. - IfTap(onTap, maxTapDistance, _tapStartPosition, tapEndPosition); + IfTap(maxTapDistance, _tapStartPosition, tapEndPosition, onTap); } /// /// Use this method in Blazor or other platforms where the mouse up position is unknown. Use this in combination /// with SetLastMovePosition. /// - /// /// - public void IfTap(Action onTap, double maxTapDistance) + /// + public void IfTap(double maxTapDistance, Action onTap) { if (_tapStartPosition == null) return; if (_tapEndPosition == null) return; // Note, this uses the _tapEndPosition field. - IfTap(onTap, maxTapDistance, _tapStartPosition, _tapEndPosition); + IfTap(maxTapDistance, _tapStartPosition, _tapEndPosition, onTap); } - private void IfTap(Action onTap, double maxTapDistance, MPoint tapStartPosition, MPoint tapEndPosition) + private void IfTap(double maxTapDistance, MPoint tapStartPosition, MPoint tapEndPosition, Action onTap) { if (tapStartPosition == null) return; if (tapEndPosition == null) return; From 7bdd73f268127d9efceadab40b5b836b5b0a02b8 Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 09:24:29 +0100 Subject: [PATCH 03/15] Add double tap to tap gesture tracker --- Mapsui.UI.Blazor/MapControl.cs | 10 ++++----- Mapsui.UI.Eto/MapControl.cs | 4 ++-- Mapsui.UI.Maui/MapControl.cs | 6 +++--- Mapsui.UI.Wpf/MapControl.cs | 10 ++++----- Mapsui/Manipulations/TapGestureTracker.cs | 26 +++++++++++++++++++---- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index 83043ede1e..a0e77bde4e 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -215,9 +215,9 @@ protected void OnMouseUp(MouseEventArgs e) { var location = e.ToLocation(_clientRect); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p, c) => { - if (OnWidgetTapped(p, 1, GetShiftPressed())) + if (OnWidgetTapped(p, c, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); @@ -297,11 +297,11 @@ public void OnTouchEnd(TouchEventArgs _) { Catch.Exceptions(() => { - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (position) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p, c) => { - if (OnWidgetTapped(position, 1, GetShiftPressed())) + if (OnWidgetTapped(p, c, GetShiftPressed())) return; - OnInfo(CreateMapInfoEventArgs(position, position, 1)); + OnInfo(CreateMapInfoEventArgs(p, p, c)); }); RefreshData(); diff --git a/Mapsui.UI.Eto/MapControl.cs b/Mapsui.UI.Eto/MapControl.cs index 42ebca625c..72183d2657 100644 --- a/Mapsui.UI.Eto/MapControl.cs +++ b/Mapsui.UI.Eto/MapControl.cs @@ -75,9 +75,9 @@ protected override void OnMouseUp(MouseEventArgs e) SetCursorInDefaultMode(); var mouseUpPosition = e.Location.ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, mouseUpPosition, (p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, mouseUpPosition, (p, c) => { - if (OnWidgetTapped(p, 1, GetShiftPressed())) + if (OnWidgetTapped(p, c, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); }); diff --git a/Mapsui.UI.Maui/MapControl.cs b/Mapsui.UI.Maui/MapControl.cs index 563b63a979..b14cae9891 100644 --- a/Mapsui.UI.Maui/MapControl.cs +++ b/Mapsui.UI.Maui/MapControl.cs @@ -234,11 +234,11 @@ private void OnTouch(object? sender, SKTouchEventArgs e) _flingTracker.IfFling(e.Id, (vX, vY) => Map.Navigator.Fling(vX, vY, 1000)); _flingTracker.RemoveId(e.Id); - _tapGestureTracker.IfTap(MaxTapGestureMovement, releasedTouch!, (p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement, releasedTouch!, (p, c) => { - if (OnWidgetTapped(location, 1, false)) + if (OnWidgetTapped(p, c, false)) return; - OnInfo(CreateMapInfoEventArgs(location, location, 1)); + OnInfo(CreateMapInfoEventArgs(p, p, 1)); }); diff --git a/Mapsui.UI.Wpf/MapControl.cs b/Mapsui.UI.Wpf/MapControl.cs index 5f54f2fda3..da75d9e4d8 100644 --- a/Mapsui.UI.Wpf/MapControl.cs +++ b/Mapsui.UI.Wpf/MapControl.cs @@ -137,14 +137,14 @@ private void MapControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var position = e.GetPosition(this).ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, position, (p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, position, (p, c) => { - if (OnWidgetTapped(p, 1, GetShiftPressed())) + if (OnWidgetTapped(p, c, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); }); - RefreshData(); + Refresh(); _flingTracker.IfFling(1, (vX, vY) => Map.Navigator.Fling(vX, vY, 1000)); _flingTracker.RemoveId(1); @@ -161,9 +161,9 @@ private void MapControl_TouchDown(object? sender, TouchEventArgs e) private void MapControlTouchUp(object? sender, TouchEventArgs e) { var touchUpPosition = e.GetTouchPoint(this).Position.ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, touchUpPosition, (p) => + _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, touchUpPosition, (p, c) => { - if (OnWidgetTapped(p, 1, GetShiftPressed())) + if (OnWidgetTapped(p, c, GetShiftPressed())) return; OnInfo(CreateMapInfoEventArgs(p, p, 1)); }); diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index 046a1b44ac..f40f671306 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Mapsui.Manipulations; @@ -8,8 +9,11 @@ public class TapGestureTracker private DateTime _tapStartTime; private MPoint? _tapStartPosition; private MPoint? _tapEndPosition; + private int _millisecondsToWaitForDoubleTap = 300; + private bool _waitingForDoubleTap; + private int _tapCount = 1; - public void IfTap(double maxTapDistance, MPoint tapEndPosition, Action onTap) + public void IfTap(double maxTapDistance, MPoint tapEndPosition, Action onTap) { if (_tapStartPosition == null) return; if (tapEndPosition == null) return; // Note, this uses the tapEndPosition parameter. @@ -23,7 +27,7 @@ public void IfTap(double maxTapDistance, MPoint tapEndPosition, Action o /// /// /// - public void IfTap(double maxTapDistance, Action onTap) + public void IfTap(double maxTapDistance, Action onTap) { if (_tapStartPosition == null) return; if (_tapEndPosition == null) return; // Note, this uses the _tapEndPosition field. @@ -31,7 +35,7 @@ public void IfTap(double maxTapDistance, Action onTap) IfTap(maxTapDistance, _tapStartPosition, _tapEndPosition, onTap); } - private void IfTap(double maxTapDistance, MPoint tapStartPosition, MPoint tapEndPosition, Action onTap) + private void IfTap(double maxTapDistance, MPoint tapStartPosition, MPoint tapEndPosition, Action onTap) { if (tapStartPosition == null) return; if (tapEndPosition == null) return; @@ -40,7 +44,10 @@ private void IfTap(double maxTapDistance, MPoint tapStartPosition, MPoint tapEnd var distance = tapEndPosition.Distance(tapStartPosition); var isTap = duration < _maxTapDuration && distance < maxTapDistance; - if (isTap) onTap(tapEndPosition); + if (_waitingForDoubleTap) + _tapCount = 2; + else if (isTap) + _ = OnTapAfterDelayAsync(onTap, tapEndPosition); // Fire and forget } /// @@ -57,4 +64,15 @@ public void SetDownPosition(MPoint position) _tapStartTime = DateTime.Now; _tapStartPosition = position; } + + private async Task OnTapAfterDelayAsync(Action onTap, MPoint position) + { + _waitingForDoubleTap = true; + await Task.Delay(_millisecondsToWaitForDoubleTap); + + onTap(position, _tapCount); // The tap count could be set to 2 during waiting. + + _waitingForDoubleTap = false; + _tapCount = 1; + } } From 69fc5f8ac7b17f6ed2ed746e943f37550646aa69 Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 10:25:21 +0100 Subject: [PATCH 04/15] Add double tap to tap gesture tracker --- Mapsui.UI.Android/MapControl.cs | 48 ++++++------------- Mapsui.UI.Blazor/MapControl.cs | 4 -- Mapsui.UI.Eto/MapControl.cs | 6 +-- Mapsui.UI.Maui/MapControl.cs | 12 +---- Mapsui.UI.Shared/MapControl.cs | 5 ++ Mapsui.UI.Wpf/MapControl.cs | 10 +--- Mapsui/Manipulations/TapGestureTracker.cs | 16 +++++-- .../Maps/Widgets/ButtonSample.cs | 10 +++- 8 files changed, 44 insertions(+), 67 deletions(-) diff --git a/Mapsui.UI.Android/MapControl.cs b/Mapsui.UI.Android/MapControl.cs index c0340fa6dd..a194ecaa74 100644 --- a/Mapsui.UI.Android/MapControl.cs +++ b/Mapsui.UI.Android/MapControl.cs @@ -39,10 +39,10 @@ public override bool OnFling(MotionEvent? e1, MotionEvent e2, float velocityX, f 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) @@ -74,10 +74,6 @@ private void LocalConstructor() 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) @@ -117,27 +113,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) { @@ -175,17 +150,18 @@ 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.SetDownPosition(locations[0]); + if (OnWidgetPointerPressed(locations[0], false)) + return; + } break; case MotionEventActions.Move: if (OnWidgetPointerMoved(locations[0], true, false)) @@ -193,7 +169,14 @@ public void MapControl_Touch(object? sender, TouchEventArgs args) _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; @@ -291,7 +274,6 @@ protected override void Dispose(bool disposing) _map?.Dispose(); _mainLooperHandler?.Dispose(); _canvas?.Dispose(); - _gestureDetector?.Dispose(); } CommonDispose(disposing); diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index a0e77bde4e..2a9d9b6322 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -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; - /// - /// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels. - /// - public int MaxTapGestureMovement { get; set; } = 8; private MapsuiJsInterop? Interop => _interop == null && JsRuntime != null ? _interop ??= new MapsuiJsInterop(JsRuntime) diff --git a/Mapsui.UI.Eto/MapControl.cs b/Mapsui.UI.Eto/MapControl.cs index 72183d2657..5f6bb1014f 100644 --- a/Mapsui.UI.Eto/MapControl.cs +++ b/Mapsui.UI.Eto/MapControl.cs @@ -21,10 +21,6 @@ public MapControl() SizeChanged += (s, e) => SetViewportSize(); } - /// - /// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels. - /// - public int MaxTapGestureMovement { get; set; } = 8; public Cursor MoveCursor { get; set; } = Cursors.Move; public MouseButtons MoveButton { get; set; } = MouseButtons.Primary; public Keys MoveModifier { get; set; } = Keys.None; @@ -75,7 +71,7 @@ protected override void OnMouseUp(MouseEventArgs e) SetCursorInDefaultMode(); var mouseUpPosition = e.Location.ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, mouseUpPosition, (p, c) => + _tapGestureTracker.IfTap(mouseUpPosition, MaxTapGestureMovement * PixelDensity, (p, c) => { if (OnWidgetTapped(p, c, GetShiftPressed())) return; diff --git a/Mapsui.UI.Maui/MapControl.cs b/Mapsui.UI.Maui/MapControl.cs index b14cae9891..c689aa3dc0 100644 --- a/Mapsui.UI.Maui/MapControl.cs +++ b/Mapsui.UI.Maui/MapControl.cs @@ -88,16 +88,6 @@ public MapControl() public bool UseDoubleTap { get; set; } = true; public bool UseFling { get; set; } = true; - // See http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r2.1/android/view/ViewConfiguration.java#ViewConfiguration.0PRESSED_STATE_DURATION for values - // If a finger touches down and up it counts as a tap if the distance - // between the down and up location is smaller then the touch distance. - // The distance is initialized at 8. How did we get to 8? Well you could - // read the discussion here: https://github.com/Mapsui/Mapsui/issues/602 - // We basically copied it from the Java source code: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewConfiguration.java#162 - /// - /// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels. - /// - public int MaxTapGestureMovement { get; set; } = 8; private double ViewportWidth => Width; // Used in shared code private double ViewportHeight => Height; // Used in shared code @@ -234,7 +224,7 @@ private void OnTouch(object? sender, SKTouchEventArgs e) _flingTracker.IfFling(e.Id, (vX, vY) => Map.Navigator.Fling(vX, vY, 1000)); _flingTracker.RemoveId(e.Id); - _tapGestureTracker.IfTap(MaxTapGestureMovement, releasedTouch!, (p, c) => + _tapGestureTracker.IfTap(releasedTouch!, MaxTapGestureMovement, (p, c) => { if (OnWidgetTapped(p, c, false)) return; diff --git a/Mapsui.UI.Shared/MapControl.cs b/Mapsui.UI.Shared/MapControl.cs index 3192a2e35e..0bc58c9a66 100644 --- a/Mapsui.UI.Shared/MapControl.cs +++ b/Mapsui.UI.Shared/MapControl.cs @@ -61,6 +61,11 @@ public partial class MapControl : INotifyPropertyChanged, IDisposable private readonly System.Diagnostics.Stopwatch _stopwatch = new(); private IRenderer _renderer = new MapRenderer(); + /// + /// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels. + /// + public int MaxTapGestureMovement { get; set; } = 8; + private void SharedConstructor() { PlatformUtilities.SetOpenInBrowserFunc(OpenInBrowser); diff --git a/Mapsui.UI.Wpf/MapControl.cs b/Mapsui.UI.Wpf/MapControl.cs index da75d9e4d8..6c5a31a443 100644 --- a/Mapsui.UI.Wpf/MapControl.cs +++ b/Mapsui.UI.Wpf/MapControl.cs @@ -57,12 +57,6 @@ public MapControl() RefreshGraphics(); } - - /// - /// The movement allowed between a touch down and touch up in a touch gestures in device independent pixels. - /// - public int MaxTapGestureMovement { get; set; } = 8; - private static Rectangle CreateSelectRectangle() => new() { Fill = new SolidColorBrush(Colors.Red), @@ -137,7 +131,7 @@ private void MapControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var position = e.GetPosition(this).ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, position, (p, c) => + _tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) => { if (OnWidgetTapped(p, c, GetShiftPressed())) return; @@ -161,7 +155,7 @@ private void MapControl_TouchDown(object? sender, TouchEventArgs e) private void MapControlTouchUp(object? sender, TouchEventArgs e) { var touchUpPosition = e.GetTouchPoint(this).Position.ToMapsui(); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, touchUpPosition, (p, c) => + _tapGestureTracker.IfTap(touchUpPosition, MaxTapGestureMovement * PixelDensity, (p, c) => { if (OnWidgetTapped(p, c, GetShiftPressed())) return; diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index f40f671306..f583b70b0e 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -9,16 +9,16 @@ public class TapGestureTracker private DateTime _tapStartTime; private MPoint? _tapStartPosition; private MPoint? _tapEndPosition; - private int _millisecondsToWaitForDoubleTap = 300; + private int _millisecondsToWaitForDoubleTap = 250; private bool _waitingForDoubleTap; private int _tapCount = 1; - public void IfTap(double maxTapDistance, MPoint tapEndPosition, Action onTap) + public void IfTap(MPoint tapEndPosition, double maxTapDistance, Action onTap) { if (_tapStartPosition == null) return; if (tapEndPosition == null) return; // Note, this uses the tapEndPosition parameter. - IfTap(maxTapDistance, _tapStartPosition, tapEndPosition, onTap); + IfTap(_tapStartPosition, tapEndPosition, maxTapDistance, onTap); } /// @@ -32,10 +32,10 @@ public void IfTap(double maxTapDistance, Action onTap) if (_tapStartPosition == null) return; if (_tapEndPosition == null) return; // Note, this uses the _tapEndPosition field. - IfTap(maxTapDistance, _tapStartPosition, _tapEndPosition, onTap); + IfTap(_tapStartPosition, _tapEndPosition, maxTapDistance, onTap); } - private void IfTap(double maxTapDistance, MPoint tapStartPosition, MPoint tapEndPosition, Action onTap) + private void IfTap(MPoint tapStartPosition, MPoint tapEndPosition, double maxTapDistance, Action onTap) { if (tapStartPosition == null) return; if (tapEndPosition == null) return; @@ -67,6 +67,12 @@ public void SetDownPosition(MPoint position) private async Task OnTapAfterDelayAsync(Action onTap, MPoint position) { + // In the current implementation we always wait for the double tap. This is not + // always the desired behavior. Sometimes you want to respond directly. But in that + // case a double tap will always be preceded by a single tap. Options to resolve this: + // - Make it configurable + // - Add an OnSingleTap event (so 3 types, to make it more comprehensible OnDoubleTap should be real event). + // - Invoke the OnTap also in the OnSingleTap scenario but with different parameters. _waitingForDoubleTap = true; await Task.Delay(_millisecondsToWaitForDoubleTap); diff --git a/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs b/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs index 1db36b3432..5445768d57 100644 --- a/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs +++ b/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs @@ -16,6 +16,7 @@ public class ButtonSample : ISample public string Category => "Widgets"; private int _tapCount; + private int _doubleTapCount; public Task CreateMapAsync() { @@ -36,7 +37,14 @@ public Task CreateMapAsync() map.RefreshGraphics(); return false; })); - map.Widgets.Add(CreateButtonWithImage(VerticalAlignment.Bottom, HorizontalAlignment.Left)); + map.Widgets.Add(CreateButton("Double Tap me", VerticalAlignment.Bottom, HorizontalAlignment.Left, (s, a) => + { + if (a.TapCount < 2) + return false; + s.Text = $"Double Tapped {++_doubleTapCount} times"; + map.RefreshGraphics(); + return false; + })); return Task.FromResult(map); } From 60df0ccac7e751c8fb8602e784dc2c8240b85a2b Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 10:48:34 +0100 Subject: [PATCH 05/15] Use tap gesture tracker for double tap on Blazor --- Mapsui.UI.Blazor/MapControl.cs | 29 ++++++----------- Mapsui.UI.Blazor/MapControlComponent.razor | 2 +- Mapsui/Manipulations/TapGestureTracker.cs | 37 +++------------------- 3 files changed, 15 insertions(+), 53 deletions(-) diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index 2a9d9b6322..003bfbaa02 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -158,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(() => @@ -191,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; @@ -209,18 +197,18 @@ protected void OnMouseUp(MouseEventArgs e) { Catch.Exceptions(() => { - var location = e.ToLocation(_clientRect); + var position = e.ToLocation(_clientRect); - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p, c) => + _tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) => { if (OnWidgetTapped(p, c, GetShiftPressed())) return; - OnInfo(CreateMapInfoEventArgs(p, p, 1)); + OnInfo(CreateMapInfoEventArgs(p, p, c)); }); - _manipulationTracker.Manipulate([e.ToLocation(_clientRect)], Map.Navigator.Manipulate); - RefreshData(); + _manipulationTracker.Manipulate([position], Map.Navigator.Manipulate); + Refresh(); }); } @@ -282,7 +270,7 @@ public void OnTouchMove(TouchEventArgs e) Catch.Exceptions(() => { var locations = e.TargetTouches.ToTouchLocations(_clientRect); - _tapGestureTracker.SetLastMovePosition(locations[0]); + _tapGestureTracker.LastMovePosition = locations[0]; if (OnWidgetPointerMoved(locations[0], true, GetShiftPressed())) return; _manipulationTracker.Manipulate(locations.ToArray(), Map.Navigator.Manipulate); @@ -293,7 +281,10 @@ public void OnTouchEnd(TouchEventArgs _) { Catch.Exceptions(() => { - _tapGestureTracker.IfTap(MaxTapGestureMovement * PixelDensity, (p, c) => + if (_tapGestureTracker.LastMovePosition == null) + return; + + _tapGestureTracker.IfTap(_tapGestureTracker.LastMovePosition, MaxTapGestureMovement * PixelDensity, (p, c) => { if (OnWidgetTapped(p, c, GetShiftPressed())) return; diff --git a/Mapsui.UI.Blazor/MapControlComponent.razor b/Mapsui.UI.Blazor/MapControlComponent.razor index ef86f16bef..4b28bb6e2d 100644 --- a/Mapsui.UI.Blazor/MapControlComponent.razor +++ b/Mapsui.UI.Blazor/MapControlComponent.razor @@ -4,7 +4,7 @@
diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index f583b70b0e..386f01bf50 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -8,40 +8,20 @@ public class TapGestureTracker private readonly double _maxTapDuration = 0.5; private DateTime _tapStartTime; private MPoint? _tapStartPosition; - private MPoint? _tapEndPosition; private int _millisecondsToWaitForDoubleTap = 250; private bool _waitingForDoubleTap; private int _tapCount = 1; + // This fields was added as a workaround for that in Blazor the touch up does not have a location (or I do not know how to get it). + public MPoint? LastMovePosition { get; set; } + public void IfTap(MPoint tapEndPosition, double maxTapDistance, Action onTap) { if (_tapStartPosition == null) return; if (tapEndPosition == null) return; // Note, this uses the tapEndPosition parameter. - IfTap(_tapStartPosition, tapEndPosition, maxTapDistance, onTap); - } - - /// - /// Use this method in Blazor or other platforms where the mouse up position is unknown. Use this in combination - /// with SetLastMovePosition. - /// - /// - /// - public void IfTap(double maxTapDistance, Action onTap) - { - if (_tapStartPosition == null) return; - if (_tapEndPosition == null) return; // Note, this uses the _tapEndPosition field. - - IfTap(_tapStartPosition, _tapEndPosition, maxTapDistance, onTap); - } - - private void IfTap(MPoint tapStartPosition, MPoint tapEndPosition, double maxTapDistance, Action onTap) - { - if (tapStartPosition == null) return; - if (tapEndPosition == null) return; - var duration = (DateTime.Now - _tapStartTime).TotalSeconds; - var distance = tapEndPosition.Distance(tapStartPosition); + var distance = tapEndPosition.Distance(_tapStartPosition); var isTap = duration < _maxTapDuration && distance < maxTapDistance; if (_waitingForDoubleTap) @@ -50,15 +30,6 @@ private void IfTap(MPoint tapStartPosition, MPoint tapEndPosition, double maxTap _ = OnTapAfterDelayAsync(onTap, tapEndPosition); // Fire and forget } - /// - /// Call this method during move if the platform does not provide the mouse up position. - /// - /// - public void SetLastMovePosition(MPoint position) - { - _tapEndPosition = position; - } - public void SetDownPosition(MPoint position) { _tapStartTime = DateTime.Now; From b685d237823d902445a858d35f0375fad5c151fc Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 11:41:17 +0100 Subject: [PATCH 06/15] Fire single tap immediately --- Mapsui/Manipulations/TapGestureTracker.cs | 39 ++++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index 386f01bf50..4a358aade7 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -8,10 +8,14 @@ public class TapGestureTracker private readonly double _maxTapDuration = 0.5; private DateTime _tapStartTime; private MPoint? _tapStartPosition; - private int _millisecondsToWaitForDoubleTap = 250; + private int _millisecondsToWaitForDoubleTap = 300; private bool _waitingForDoubleTap; private int _tapCount = 1; + // This causes a delay on the single tap. This is not always the desired behavior. When this is set to false + // The single tap will fire each time before the double tap. This is the default behavior in most systems. + public bool DoNotFireSingleTapOnDoubleTap { get; set; } = false; + // This fields was added as a workaround for that in Blazor the touch up does not have a location (or I do not know how to get it). public MPoint? LastMovePosition { get; set; } @@ -24,16 +28,41 @@ public void IfTap(MPoint tapEndPosition, double maxTapDistance, Action onTap, MPoint position) From 6ec2b640966235ac128f5d1245b15aa4f6767fad Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 11:41:49 +0100 Subject: [PATCH 07/15] Button should either lister to double or single tap --- Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs b/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs index 5445768d57..b5833371c0 100644 --- a/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs +++ b/Samples/Mapsui.Samples.Common/Maps/Widgets/ButtonSample.cs @@ -26,6 +26,8 @@ public Task CreateMapAsync() map.Widgets.Add(CreateButton("Tap me", VerticalAlignment.Top, HorizontalAlignment.Left, (s, a) => { + if (a.TapCount != 1) + return false; s.Text = $"Tapped {++_tapCount} times"; map.RefreshGraphics(); return false; @@ -39,7 +41,7 @@ public Task CreateMapAsync() })); map.Widgets.Add(CreateButton("Double Tap me", VerticalAlignment.Bottom, HorizontalAlignment.Left, (s, a) => { - if (a.TapCount < 2) + if (a.TapCount != 2) return false; s.Text = $"Double Tapped {++_doubleTapCount} times"; map.RefreshGraphics(); From 1230586e8e29d83e5304d1494ba099429749c9ba Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 11:43:39 +0100 Subject: [PATCH 08/15] Add more info to tap logging --- Mapsui.UI.Shared/MapControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mapsui.UI.Shared/MapControl.cs b/Mapsui.UI.Shared/MapControl.cs index 0bc58c9a66..bbfe68ea72 100644 --- a/Mapsui.UI.Shared/MapControl.cs +++ b/Mapsui.UI.Shared/MapControl.cs @@ -589,7 +589,7 @@ private bool OnWidgetTapped(MPoint position, int tapCount, bool shift) var touchedWidgets = WidgetTouch.GetTouchedWidgets(position, Map); foreach (var widget in touchedWidgets) { - Logger.Log(LogLevel.Information, $"Widget.Tapped: {widget.GetType().Name}"); + Logger.Log(LogLevel.Information, $"Widget.Tapped: {widget.GetType().Name} TapCount: {tapCount} KeyState: {shift}"); var args = new WidgetEventArgs(position, tapCount, true, shift); if (widget.OnTapped(Map.Navigator, position, args)) return true; From 3bd1ab026f493e896c08d853fd6b5c79be46ddea Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 11:55:23 +0100 Subject: [PATCH 09/15] Remove the custom fling implementation in android, we need to have a generic fling --- Mapsui.UI.Android/MapControl.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/Mapsui.UI.Android/MapControl.cs b/Mapsui.UI.Android/MapControl.cs index a194ecaa74..dc846ff352 100644 --- a/Mapsui.UI.Android/MapControl.cs +++ b/Mapsui.UI.Android/MapControl.cs @@ -17,25 +17,6 @@ public enum SkiaRenderMode Software } -internal class MapControlGestureListener : GestureDetector.SimpleOnGestureListener -{ - public EventHandler? 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; @@ -72,8 +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; } private void CanvasOnPaintSurface(object? sender, SKPaintSurfaceEventArgs args) From 55784dcd62d20f27cee4b24ba5c04ee5f64e5d70 Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 12:16:17 +0100 Subject: [PATCH 10/15] Use tap gesture tracker in Avalonia --- Mapsui.UI.Android/MapControl.cs | 2 +- Mapsui.UI.Avalonia/MapControl.cs | 54 ++++++++----------- Mapsui.UI.Blazor/MapControl.cs | 4 +- Mapsui.UI.Eto/MapControl.cs | 2 +- Mapsui.UI.Maui/MapControl.cs | 15 +++--- Mapsui.UI.Wpf/MapControl.cs | 6 +-- Mapsui/Manipulations/FlingTracker.cs | 2 +- Mapsui/Manipulations/TapGestureTracker.cs | 4 +- .../Maps/Demo/OsmSample.cs | 2 + 9 files changed, 40 insertions(+), 51 deletions(-) diff --git a/Mapsui.UI.Android/MapControl.cs b/Mapsui.UI.Android/MapControl.cs index dc846ff352..19eb0aad2b 100644 --- a/Mapsui.UI.Android/MapControl.cs +++ b/Mapsui.UI.Android/MapControl.cs @@ -137,7 +137,7 @@ public void MapControl_Touch(object? sender, TouchEventArgs args) _manipulationTracker.Restart(locations); if (locations.Length == 1) { - _tapGestureTracker.SetDownPosition(locations[0]); + _tapGestureTracker.Restart(locations[0]); if (OnWidgetPointerPressed(locations[0], false)) return; } diff --git a/Mapsui.UI.Avalonia/MapControl.cs b/Mapsui.UI.Avalonia/MapControl.cs index 1cecea11e9..897cab849c 100644 --- a/Mapsui.UI.Avalonia/MapControl.cs +++ b/Mapsui.UI.Avalonia/MapControl.cs @@ -24,6 +24,7 @@ public partial class MapControl : UserControl, IMapControl, IDisposable private readonly ConcurrentDictionary _pointerLocations = new(); private bool _shiftPressed; private readonly ManipulationTracker _manipulationTracker = new(); + private readonly TapGestureTracker _tapGestureTracker = new(); public MapControl() { @@ -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); @@ -84,22 +82,22 @@ 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); @@ -118,22 +116,28 @@ private void MapControl_PointerMoved(object? sender, PointerEventArgs e) 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, @@ -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); diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index 003bfbaa02..0858144947 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -166,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())) @@ -260,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); }); } diff --git a/Mapsui.UI.Eto/MapControl.cs b/Mapsui.UI.Eto/MapControl.cs index 5f6bb1014f..72fe0930b8 100644 --- a/Mapsui.UI.Eto/MapControl.cs +++ b/Mapsui.UI.Eto/MapControl.cs @@ -46,7 +46,7 @@ protected override void OnMouseDown(MouseEventArgs e) SetCursorInMoveMode(); var mouseDownPosition = e.Location.ToMapsui(); _manipulationTracker.Restart([mouseDownPosition]); - _tapGestureTracker.SetDownPosition(mouseDownPosition); + _tapGestureTracker.Restart(mouseDownPosition); if (OnWidgetPointerPressed(mouseDownPosition, GetShiftPressed())) return; diff --git a/Mapsui.UI.Maui/MapControl.cs b/Mapsui.UI.Maui/MapControl.cs index c689aa3dc0..cb08ea8b9a 100644 --- a/Mapsui.UI.Maui/MapControl.cs +++ b/Mapsui.UI.Maui/MapControl.cs @@ -188,14 +188,13 @@ private void OnTouch(object? sender, SKTouchEventArgs e) { _touches[e.Id] = location; if (_touches.Count == 1) - _tapGestureTracker.SetDownPosition(location); - - if (OnWidgetPointerPressed(location, false)) - return; - - _flingTracker.Clear(); - - _manipulationTracker.Restart(_touches.Values.ToArray()); + { + _tapGestureTracker.Restart(location); + _flingTracker.Restart(); + _manipulationTracker.Restart(_touches.Values.ToArray()); + if (OnWidgetPointerPressed(location, false)) + return; + } } else if (e.ActionType == SKTouchAction.Moved) { diff --git a/Mapsui.UI.Wpf/MapControl.cs b/Mapsui.UI.Wpf/MapControl.cs index 6c5a31a443..e68a14abb1 100644 --- a/Mapsui.UI.Wpf/MapControl.cs +++ b/Mapsui.UI.Wpf/MapControl.cs @@ -119,11 +119,11 @@ private void RunOnUIThread(Action action) private void MapControlMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var mousePosition = e.GetPosition(this).ToMapsui(); - _tapGestureTracker.SetDownPosition(mousePosition); + _tapGestureTracker.Restart(mousePosition); _manipulationTracker.Restart([mousePosition]); if (OnWidgetPointerPressed(mousePosition, GetShiftPressed())) return; - _flingTracker.Clear(); + _flingTracker.Restart(); CaptureMouse(); } @@ -149,7 +149,7 @@ private void MapControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e) private void MapControl_TouchDown(object? sender, TouchEventArgs e) { var touchDownPosition = e.GetTouchPoint(this).Position.ToMapsui(); - _tapGestureTracker.SetDownPosition(touchDownPosition); + _tapGestureTracker.Restart(touchDownPosition); } private void MapControlTouchUp(object? sender, TouchEventArgs e) diff --git a/Mapsui/Manipulations/FlingTracker.cs b/Mapsui/Manipulations/FlingTracker.cs index 67b03ab21b..4425a087ae 100644 --- a/Mapsui/Manipulations/FlingTracker.cs +++ b/Mapsui/Manipulations/FlingTracker.cs @@ -41,7 +41,7 @@ public void RemoveId(long id) _events.Remove(id); } - public void Clear() + public void Restart() { _events.Clear(); } diff --git a/Mapsui/Manipulations/TapGestureTracker.cs b/Mapsui/Manipulations/TapGestureTracker.cs index 4a358aade7..0360087d4c 100644 --- a/Mapsui/Manipulations/TapGestureTracker.cs +++ b/Mapsui/Manipulations/TapGestureTracker.cs @@ -56,13 +56,13 @@ private async Task StartWaitingForSecondTapAsync() { _waitingForDoubleTap = true; await Task.Delay(_millisecondsToWaitForDoubleTap); + _waitingForDoubleTap = false; } - public void SetDownPosition(MPoint position) + public void Restart(MPoint position) { _tapStartTime = DateTime.Now; _tapStartPosition = position; - _waitingForDoubleTap = false; } private async Task OnTapAfterDelayAsync(Action onTap, MPoint position) diff --git a/Samples/Mapsui.Samples.Common/Maps/Demo/OsmSample.cs b/Samples/Mapsui.Samples.Common/Maps/Demo/OsmSample.cs index f40810e207..53456fe253 100644 --- a/Samples/Mapsui.Samples.Common/Maps/Demo/OsmSample.cs +++ b/Samples/Mapsui.Samples.Common/Maps/Demo/OsmSample.cs @@ -4,6 +4,7 @@ using Mapsui.Widgets.ScaleBar; using Mapsui.Widgets.ButtonWidgets; using System.Threading.Tasks; +using Mapsui.Widgets.InfoWidgets; namespace Mapsui.Samples.Common.Maps.Demo; @@ -26,6 +27,7 @@ public static Map CreateMap() map.Layers.Add(OpenStreetMap.CreateTileLayer()); map.Widgets.Add(new ScaleBarWidget(map) { TextAlignment = Alignment.Center, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top }); map.Widgets.Add(new ZoomInOutWidget { Margin = new MRect(20, 40) }); + map.Widgets.Add(new MouseCoordinatesWidget()); return map; } } From 5f5d0bfdd6dcce607578cb2fa5d5852a6994334c Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 12:31:35 +0100 Subject: [PATCH 11/15] Removed UseDoubleTap because it was not used Not sure if we need to fix something in situations where it was set to false. --- Mapsui.UI.MapView/MapView.cs | 1 - Mapsui.UI.Maui/MapControl.cs | 1 - .../AnimatedMyLocationSample.cs | 1 - Samples/Mapsui.Samples.MapView/ManyPinsSample.cs | 1 - Samples/Mapsui.Samples.MapView/MyLocationSample.cs | 1 - Samples/Mapsui.Samples.MapView/PinSample.cs | 11 +---------- Samples/Mapsui.Samples.MapView/PolylineSample.cs | 2 -- 7 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Mapsui.UI.MapView/MapView.cs b/Mapsui.UI.MapView/MapView.cs index 2769fc2e22..a7a8e444b9 100644 --- a/Mapsui.UI.MapView/MapView.cs +++ b/Mapsui.UI.MapView/MapView.cs @@ -52,7 +52,6 @@ public MapView() MyLocationFollow = false; IsClippedToBounds = true; - UseDoubleTap = false; MyLocationLayer = new Objects.MyLocationLayer(this) { Enabled = true }; _mapCalloutLayer = new ObservableMemoryLayer(f => f.Feature) { Name = _calloutLayerName, IsMapInfoLayer = true }; diff --git a/Mapsui.UI.Maui/MapControl.cs b/Mapsui.UI.Maui/MapControl.cs index cb08ea8b9a..361c987be8 100644 --- a/Mapsui.UI.Maui/MapControl.cs +++ b/Mapsui.UI.Maui/MapControl.cs @@ -85,7 +85,6 @@ public MapControl() Content = view; } - public bool UseDoubleTap { get; set; } = true; public bool UseFling { get; set; } = true; private double ViewportWidth => Width; // Used in shared code diff --git a/Samples/Mapsui.Samples.MapView/AnimatedMyLocationSample.cs b/Samples/Mapsui.Samples.MapView/AnimatedMyLocationSample.cs index cebd9e5b7f..c9aaa70e36 100644 --- a/Samples/Mapsui.Samples.MapView/AnimatedMyLocationSample.cs +++ b/Samples/Mapsui.Samples.MapView/AnimatedMyLocationSample.cs @@ -31,7 +31,6 @@ public void Setup(IMapControl mapControl) _mapView.MyLocationLayer.IsMoving = true; _mapView.MyLocationEnabled = true; _mapView.MyLocationFollow = true; - _mapView.UseDoubleTap = true; _mapView.MyLocationLayer.UpdateMyLocation(_newLocation); _mapView.Map.Navigator.CenterOn(_newLocation.ToMapsui()); diff --git a/Samples/Mapsui.Samples.MapView/ManyPinsSample.cs b/Samples/Mapsui.Samples.MapView/ManyPinsSample.cs index 6e875fb7ba..1ad6c89e8d 100644 --- a/Samples/Mapsui.Samples.MapView/ManyPinsSample.cs +++ b/Samples/Mapsui.Samples.MapView/ManyPinsSample.cs @@ -121,7 +121,6 @@ public void Setup(IMapControl mapControl) mapControl.Map.Widgets.Add(CreatePerformanceWidget(mapControl)); mapControl.Renderer.WidgetRenders[typeof(PerformanceWidget)] = new Rendering.Skia.SkiaWidgets.PerformanceWidgetRenderer(); - ((UI.Maui.MapView)mapControl).UseDoubleTap = true; ((UI.Maui.MapView)mapControl).UniqueCallout = true; var sw = new Stopwatch(); diff --git a/Samples/Mapsui.Samples.MapView/MyLocationSample.cs b/Samples/Mapsui.Samples.MapView/MyLocationSample.cs index 15d636fa20..9b246b7a25 100644 --- a/Samples/Mapsui.Samples.MapView/MyLocationSample.cs +++ b/Samples/Mapsui.Samples.MapView/MyLocationSample.cs @@ -23,7 +23,6 @@ public bool OnClick(object? sender, EventArgs args) mapView.MyLocationLayer.IsMoving = mapView.MyLocationEnabled; mapView.MyLocationEnabled = true; - mapView.UseDoubleTap = true; return false; } diff --git a/Samples/Mapsui.Samples.MapView/PinSample.cs b/Samples/Mapsui.Samples.MapView/PinSample.cs index 20f379dedb..2b1e35f231 100644 --- a/Samples/Mapsui.Samples.MapView/PinSample.cs +++ b/Samples/Mapsui.Samples.MapView/PinSample.cs @@ -143,14 +143,5 @@ public bool OnClick(object? sender, EventArgs args) } public void Setup(IMapControl mapControl) - { - //OSM never displays.... - //mapControl.Map = OsmSample.CreateMap(); - - //I like bing Hybrid - mapControl.Map = BingSample.CreateMap(BingHybrid.DefaultCache, BruTile.Predefined.KnownTileSource.BingHybrid); - - ((UI.Maui.MapView)mapControl).UseDoubleTap = true; - //((MapView)mapControl).UniqueCallout = true; - } + => mapControl.Map = BingSample.CreateMap(BingHybrid.DefaultCache, BruTile.Predefined.KnownTileSource.BingHybrid); } diff --git a/Samples/Mapsui.Samples.MapView/PolylineSample.cs b/Samples/Mapsui.Samples.MapView/PolylineSample.cs index b91324e7cb..990c043571 100644 --- a/Samples/Mapsui.Samples.MapView/PolylineSample.cs +++ b/Samples/Mapsui.Samples.MapView/PolylineSample.cs @@ -52,7 +52,5 @@ public bool OnClick(object? sender, EventArgs args) public void Setup(IMapControl mapControl) { mapControl.Map = OsmSample.CreateMap(); - - ((UI.Maui.MapView)mapControl).UseDoubleTap = false; } } From 3025ae8a3a60ff8ff4391d71701bea3e23698ed2 Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 12:42:42 +0100 Subject: [PATCH 12/15] Use tap gesture tracker in WinUI/Uno --- Mapsui.UI.WinUI/MapControl.cs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Mapsui.UI.WinUI/MapControl.cs b/Mapsui.UI.WinUI/MapControl.cs index 83a1e6ed41..6802546c75 100644 --- a/Mapsui.UI.WinUI/MapControl.cs +++ b/Mapsui.UI.WinUI/MapControl.cs @@ -28,6 +28,7 @@ public partial class MapControl : Grid, IMapControl, IDisposable private readonly Rectangle _selectRectangle = CreateSelectRectangle(); private readonly SKXamlCanvas _canvas = CreateRenderTarget(); bool _shiftPressed; + private readonly TapGestureTracker _tapGestureTracker = new(); public MapControl() { @@ -60,9 +61,7 @@ public MapControl() PointerPressed += MapControl_PointerPressed; PointerMoved += MapControl_PointerMoved; - - Tapped += OnSingleTapped; - DoubleTapped += OnDoubleTapped; + PointerReleased += MapControl_PointerReleased; PointerWheelChanged += MapControl_PointerWheelChanged; @@ -98,6 +97,7 @@ private void OnManipulationCompleted(object sender, ManipulationCompletedRoutedE private void MapControl_PointerPressed(object sender, PointerRoutedEventArgs e) { var position = e.GetCurrentPoint(this).Position.ToMapsui(); + _tapGestureTracker.Restart(position); if (OnWidgetPointerPressed(position, e.KeyModifiers == VirtualKeyModifiers.Shift)) return; } @@ -115,6 +115,17 @@ private void MapControl_PointerMoved(object sender, PointerRoutedEventArgs e) return; } + private void MapControl_PointerReleased(object sender, PointerRoutedEventArgs e) + { + var position = e.GetCurrentPoint(this).Position.ToMapsui(); + _tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) => + { + if (OnWidgetTapped(p, c, _shiftPressed)) + return; + OnInfo(CreateMapInfoEventArgs(p, p, c)); + }); + } + private bool IsHovering(PointerRoutedEventArgs e) { if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch) @@ -122,22 +133,6 @@ private bool IsHovering(PointerRoutedEventArgs e) return !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed; } - private void OnSingleTapped(object sender, TappedRoutedEventArgs e) - { - var position = e.GetPosition(this).ToMapsui(); - if (OnWidgetTapped(position, 1, _shiftPressed)) - return; - OnInfo(CreateMapInfoEventArgs(position, position, 1)); - } - - private void OnDoubleTapped(object sender, DoubleTappedRoutedEventArgs e) - { - var position = e.GetPosition(this).ToMapsui(); - if (OnWidgetTapped(position, 2, _shiftPressed)) - return; - OnInfo(CreateMapInfoEventArgs(position, position, 2)); - } - private static Rectangle CreateSelectRectangle() { return new Rectangle From ae8ece9d5b36cb85228a45000103804318b5b83f Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 12:50:31 +0100 Subject: [PATCH 13/15] Use tap gesture tracker in iOS --- Mapsui.UI.iOS/MapControl.cs | 56 +++++++++++-------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/Mapsui.UI.iOS/MapControl.cs b/Mapsui.UI.iOS/MapControl.cs index 26ba126ff1..b01e2be394 100644 --- a/Mapsui.UI.iOS/MapControl.cs +++ b/Mapsui.UI.iOS/MapControl.cs @@ -12,8 +12,8 @@ public partial class MapControl : UIView, IMapControl private SKGLView? _glCanvas; private SKCanvasView? _canvas; private bool _canvasInitialized; - private MPoint? _pointerDownPosition; private readonly ManipulationTracker _manipulationTracker = new(); + private readonly TapGestureTracker _tapGestureTracker = new(); public MapControl(CGRect frame) : base(frame) @@ -108,49 +108,16 @@ private void LocalConstructor() MultipleTouchEnabled = true; UserInteractionEnabled = true; - var doubleTapGestureRecognizer = new UITapGestureRecognizer(OnDoubleTapped) - { - NumberOfTapsRequired = 2, - CancelsTouchesInView = false, - }; - AddGestureRecognizer(doubleTapGestureRecognizer); - - var tapGestureRecognizer = new UITapGestureRecognizer(OnSingleTapped) - { - NumberOfTapsRequired = 1, - CancelsTouchesInView = false, - }; - tapGestureRecognizer.RequireGestureRecognizerToFail(doubleTapGestureRecognizer); - AddGestureRecognizer(tapGestureRecognizer); - Map.Navigator.SetSize(ViewportWidth, ViewportHeight); } - private void OnDoubleTapped(UITapGestureRecognizer gesture) - { - var position = GetScreenPosition(gesture.LocationInView(this)); - if (OnWidgetTapped(position, 1, false)) - return; - OnInfo(CreateMapInfoEventArgs(position, position, 2)); - } - - private void OnSingleTapped(UITapGestureRecognizer gesture) - { - var position = GetScreenPosition(gesture.LocationInView(this)); - if (OnWidgetTapped(position, 2, false)) - return; - OnInfo(CreateMapInfoEventArgs(position, position, 1)); - } - private void OnPaintSurface(object? sender, SKPaintGLSurfaceEventArgs args) { if (PixelDensity <= 0) return; var canvas = args.Surface.Canvas; - canvas.Scale(PixelDensity, PixelDensity); - CommonDrawControl(canvas); } @@ -160,9 +127,7 @@ private void OnPaintSurface(object? sender, SKPaintSurfaceEventArgs args) return; var canvas = args.Surface.Canvas; - canvas.Scale(PixelDensity, PixelDensity); - CommonDrawControl(canvas); } @@ -173,12 +138,12 @@ public override void TouchesBegan(NSSet touches, UIEvent? e) base.TouchesBegan(touches, e); var locations = GetTouchLocations(e, this); - _manipulationTracker.Restart(locations); - if (locations.Length == 1) { - _pointerDownPosition = locations[0]; - if (OnWidgetPointerPressed(_pointerDownPosition, false)) + var position = locations[0]; + _tapGestureTracker.Restart(position); + _manipulationTracker.Restart([position]); + if (OnWidgetPointerPressed(position, false)) return; } }); @@ -208,6 +173,17 @@ public override void TouchesEnded(NSSet touches, UIEvent? e) base.TouchesEnded(touches, e); var locations = GetTouchLocations(e, this); + if (locations.Length == 1) + { + var position = locations[0]; + _tapGestureTracker.IfTap(position, MaxTapGestureMovement * PixelDensity, (p, c) => + { + if (OnWidgetTapped(p, c, false)) + return; + OnInfo(CreateMapInfoEventArgs(p, p, c)); + }); + } + _manipulationTracker.Manipulate(locations, Map.Navigator.Manipulate); Refresh(); From 6491221220d97a8856bd9320c95c4d2f3c26963c Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 13:27:50 +0100 Subject: [PATCH 14/15] Check widget move should be called if there is one screen position --- Mapsui.UI.Android/MapControl.cs | 5 +++-- Mapsui.UI.Avalonia/MapControl.cs | 6 +++--- Mapsui.UI.Blazor/MapControl.cs | 10 +++++++--- Mapsui.UI.Wpf/MapControl.cs | 8 ++++---- Mapsui.UI.iOS/MapControl.cs | 3 --- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Mapsui.UI.Android/MapControl.cs b/Mapsui.UI.Android/MapControl.cs index 19eb0aad2b..c76e5a2244 100644 --- a/Mapsui.UI.Android/MapControl.cs +++ b/Mapsui.UI.Android/MapControl.cs @@ -143,8 +143,9 @@ public void MapControl_Touch(object? sender, TouchEventArgs args) } 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: diff --git a/Mapsui.UI.Avalonia/MapControl.cs b/Mapsui.UI.Avalonia/MapControl.cs index 897cab849c..82deb2b043 100644 --- a/Mapsui.UI.Avalonia/MapControl.cs +++ b/Mapsui.UI.Avalonia/MapControl.cs @@ -102,14 +102,14 @@ 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); diff --git a/Mapsui.UI.Blazor/MapControl.cs b/Mapsui.UI.Blazor/MapControl.cs index 0858144947..5ca29168ee 100644 --- a/Mapsui.UI.Blazor/MapControl.cs +++ b/Mapsui.UI.Blazor/MapControl.cs @@ -270,9 +270,13 @@ public void OnTouchMove(TouchEventArgs e) Catch.Exceptions(() => { var locations = e.TargetTouches.ToTouchLocations(_clientRect); - _tapGestureTracker.LastMovePosition = 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); }); } diff --git a/Mapsui.UI.Wpf/MapControl.cs b/Mapsui.UI.Wpf/MapControl.cs index e68a14abb1..a252aad7e3 100644 --- a/Mapsui.UI.Wpf/MapControl.cs +++ b/Mapsui.UI.Wpf/MapControl.cs @@ -179,13 +179,13 @@ public void OpenInBrowser(string url) private void MapControlMouseMove(object sender, MouseEventArgs e) { var isHovering = IsHovering(e); - var mousePosition = e.GetPosition(this).ToMapsui(); - if (OnWidgetPointerMoved(mousePosition, !isHovering, GetShiftPressed())) + var position = e.GetPosition(this).ToMapsui(); + if (OnWidgetPointerMoved(position, !isHovering, GetShiftPressed())) return; if (isHovering) return; - _flingTracker.AddEvent(1, mousePosition, DateTime.Now.Ticks); - _manipulationTracker.Manipulate([mousePosition], Map.Navigator.Manipulate); + _flingTracker.AddEvent(1, position, DateTime.Now.Ticks); + _manipulationTracker.Manipulate([position], Map.Navigator.Manipulate); } private double ViewportWidth => ActualWidth; diff --git a/Mapsui.UI.iOS/MapControl.cs b/Mapsui.UI.iOS/MapControl.cs index b01e2be394..1d0c2ca83f 100644 --- a/Mapsui.UI.iOS/MapControl.cs +++ b/Mapsui.UI.iOS/MapControl.cs @@ -157,11 +157,8 @@ public override void TouchesMoved(NSSet touches, UIEvent? e) var locations = GetTouchLocations(e, this); if (locations.Length == 1) - { if (OnWidgetPointerMoved(locations[0], true, false)) return; - } - _manipulationTracker.Manipulate(locations, Map.Navigator.Manipulate); }); } From 81790f8ece9c1cac693f4b7808693a4405ddec99 Mon Sep 17 00:00:00 2001 From: pauldendulk Date: Fri, 8 Mar 2024 14:04:36 +0100 Subject: [PATCH 15/15] Update button sample reference image --- .../ButtonSample.Regression.png | Bin 397513 -> 395489 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Mapsui.Rendering.Skia.Tests/Resources/Images/OriginalRegression/ButtonSample.Regression.png b/Tests/Mapsui.Rendering.Skia.Tests/Resources/Images/OriginalRegression/ButtonSample.Regression.png index ce1bdd4f2752b50998f87b7de04d3116628dfc07..136de84511694d3b24d1ddc97b313a32fe050f11 100644 GIT binary patch delta 36247 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 VTD0Z+bOs>sboFyt$Ly5Q1OP7tC_?}M delta 38286 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 XnNE2VP1*i6Fkr^|XED#v4pILF(lU_f