Skip to content

Commit b45d663

Browse files
mattleibowCopilot
andcommitted
fix: lock pinch zoom pivot when pan is disabled
When IsPanEnabled is false, the pinch focal point is now fixed to where the gesture started, preventing effective panning through zoom+move. The _pinchFocalPointOverride field is cleared when the gesture ends. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fbe6b20 commit b45d663

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

source/SkiaSharp.Extended/Gestures/SKGestureTracker.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public sealed class SKGestureTracker : IDisposable
7272
private float _zoomTargetFactor;
7373
private SKPoint _zoomFocalPoint;
7474
private long _zoomStartTicks;
75+
private SKPoint? _pinchFocalPointOverride;
7576

7677
/// <summary>
7778
/// Initializes a new instance of the <see cref="SKGestureTracker"/> class with default options.
@@ -683,8 +684,16 @@ private void OnEnginePinchDetected(object? s, SKPinchGestureEventArgs e)
683684

684685
if (IsPinchEnabled)
685686
{
687+
// When pan is disabled, lock the zoom pivot to where the pinch started
688+
// so that moving fingers during a pinch doesn't cause effective panning.
689+
if (!IsPanEnabled)
690+
_pinchFocalPointOverride ??= e.FocalPoint;
691+
else
692+
_pinchFocalPointOverride = null;
693+
694+
var pivot = _pinchFocalPointOverride ?? e.FocalPoint;
686695
var newScale = Clamp(_scale * e.ScaleDelta, Options.MinScale, Options.MaxScale);
687-
AdjustOffsetForPivot(e.FocalPoint, _scale, newScale, _rotation, _rotation);
696+
AdjustOffsetForPivot(pivot, _scale, newScale, _rotation, _rotation);
688697
_scale = newScale;
689698
}
690699

@@ -749,6 +758,8 @@ private void OnEngineGestureStarted(object? s, SKGestureLifecycleEventArgs e)
749758

750759
private void OnEngineGestureEnded(object? s, SKGestureLifecycleEventArgs e)
751760
{
761+
_pinchFocalPointOverride = null;
762+
752763
if (_isDragging)
753764
{
754765
_isDragging = false;

tests/SkiaSharp.Extended.Tests/Gestures/SKGestureTrackerTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,5 +1152,33 @@ public void SetScaleRange_NegativeMin_Throws()
11521152
var options = new SKGestureTrackerOptions();
11531153
Assert.Throws<ArgumentOutOfRangeException>(() => options.SetScaleRange(-1f, 10f));
11541154
}
1155+
[Fact]
1156+
public void PinchWithPanDisabled_FingersTranslate_OffsetDoesNotChange()
1157+
{
1158+
// Bug fix: when IsPanEnabled=false, moving the finger midpoint during a pinch
1159+
// (translation, no scale change) should not cause the content to pan.
1160+
var tracker = CreateTracker();
1161+
tracker.IsPanEnabled = false;
1162+
tracker.IsPinchEnabled = true;
1163+
tracker.IsRotateEnabled = false; // isolate pinch-only behavior
1164+
1165+
var initialOffset = tracker.Offset;
1166+
1167+
// Both fingers go down 100px apart
1168+
tracker.ProcessTouchDown(1, new SKPoint(150, 200));
1169+
tracker.ProcessTouchDown(2, new SKPoint(250, 200));
1170+
1171+
// Both fingers translate upward together (same distance = scale delta 1.0)
1172+
tracker.ProcessTouchMove(1, new SKPoint(150, 100));
1173+
tracker.ProcessTouchMove(2, new SKPoint(250, 100));
1174+
1175+
// Translate further
1176+
tracker.ProcessTouchMove(1, new SKPoint(150, 50));
1177+
tracker.ProcessTouchMove(2, new SKPoint(250, 50));
1178+
1179+
// Offset must not change -- pure translation with pan disabled should be ignored
1180+
Assert.Equal(initialOffset.X, tracker.Offset.X, 1f);
1181+
Assert.Equal(initialOffset.Y, tracker.Offset.Y, 1f);
1182+
}
11551183

11561184
}

0 commit comments

Comments
 (0)