diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index c0db4f6494..4a54c1b35c 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -634,8 +634,6 @@ namespace Avalonia.Controls.Presenters offset = new Vector(offset.X + xDistance, offset.Y + yDistance); - System.Diagnostics.Debug.WriteLine($"{offset}"); - _scrollGestureSnapPoints.Add(e.Id, SnapOffset(offset)); double GetDistance(double speed) @@ -692,7 +690,7 @@ namespace Avalonia.Controls.Presenters x = Math.Min(x, Extent.Width - Viewport.Width); } - Vector newOffset = SnapOffset(new Vector(x, y)); + Vector newOffset = SnapOffset(new Vector(x, y), delta, true); bool offsetChanged = newOffset != Offset; SetCurrentValue(OffsetProperty, newOffset); @@ -930,67 +928,64 @@ namespace Avalonia.Controls.Presenters } } - private Vector SnapOffset(Vector offset) + private Vector SnapOffset(Vector offset, Vector direction = default, bool snapToNext = false) { var scrollable = GetScrollSnapPointsInfo(Content); - if(scrollable is null) + if (scrollable is null || (VerticalSnapPointsType == SnapPointsType.None && HorizontalSnapPointsType == SnapPointsType.None)) return offset; - var diff = GetAlignedDiff(); + var diff = GetAlignmentDiff(); - if (VerticalSnapPointsType != SnapPointsType.None) + if (VerticalSnapPointsType != SnapPointsType.None && (_areVerticalSnapPointsRegular || _verticalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.Y != 0)) { - offset = new Vector(offset.X, offset.Y + diff.Y); - double nearestSnapPoint = offset.Y; + var estimatedOffset = new Vector(offset.X, offset.Y + diff.Y); + double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0; if (_areVerticalSnapPointsRegular) { - var minSnapPoint = (int)(offset.Y / _verticalSnapPoint) * _verticalSnapPoint + _verticalSnapPointOffset; - var maxSnapPoint = minSnapPoint + _verticalSnapPoint; - var midPoint = (minSnapPoint + maxSnapPoint) / 2; - - nearestSnapPoint = offset.Y < midPoint ? minSnapPoint : maxSnapPoint; + previousSnapPoint = (int)(estimatedOffset.Y / _verticalSnapPoint) * _verticalSnapPoint + _verticalSnapPointOffset; + nextSnapPoint = previousSnapPoint + _verticalSnapPoint; + midPoint = (previousSnapPoint + nextSnapPoint) / 2; } - else if (_verticalSnapPoints != null && _verticalSnapPoints.Count > 0) + else if (_verticalSnapPoints?.Count > 0) { - var higherSnapPoint = FindNearestSnapPoint(_verticalSnapPoints, offset.Y, out var lowerSnapPoint); - var midPoint = (lowerSnapPoint + higherSnapPoint) / 2; - - nearestSnapPoint = offset.Y < midPoint ? lowerSnapPoint : higherSnapPoint; + (previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_verticalSnapPoints, estimatedOffset.Y); + midPoint = (previousSnapPoint + nextSnapPoint) / 2; } + var nearestSnapPoint = snapToNext ? (direction.Y > 0 ? previousSnapPoint : nextSnapPoint ) : + estimatedOffset.Y < midPoint ? previousSnapPoint : nextSnapPoint; + offset = new Vector(offset.X, nearestSnapPoint - diff.Y); } - if (HorizontalSnapPointsType != SnapPointsType.None) + if (HorizontalSnapPointsType != SnapPointsType.None && (_areHorizontalSnapPointsRegular || _horizontalSnapPoints?.Count > 0) && (!snapToNext || snapToNext && direction.X != 0)) { - offset = new Vector(offset.X + diff.X, offset.Y); - double nearestSnapPoint = offset.X; + var estimatedOffset = new Vector(offset.X + diff.X, offset.Y); + double previousSnapPoint = 0, nextSnapPoint = 0, midPoint = 0; if (_areHorizontalSnapPointsRegular) { - var minSnapPoint = (int)(offset.X / _horizontalSnapPoint) * _horizontalSnapPoint + _horizontalSnapPointOffset; - var maxSnapPoint = minSnapPoint + _horizontalSnapPoint; - var midPoint = (minSnapPoint + maxSnapPoint) / 2; - - nearestSnapPoint = offset.X < midPoint ? minSnapPoint : maxSnapPoint; + previousSnapPoint = (int)(estimatedOffset.X / _horizontalSnapPoint) * _horizontalSnapPoint + _horizontalSnapPointOffset; + nextSnapPoint = previousSnapPoint + _horizontalSnapPoint; + midPoint = (previousSnapPoint + nextSnapPoint) / 2; } - else if (_horizontalSnapPoints != null && _horizontalSnapPoints.Count > 0) + else if (_horizontalSnapPoints?.Count > 0) { - var higherSnapPoint = FindNearestSnapPoint(_horizontalSnapPoints, offset.X, out var lowerSnapPoint); - var midPoint = (lowerSnapPoint + higherSnapPoint) / 2; - - nearestSnapPoint = offset.X < midPoint ? lowerSnapPoint : higherSnapPoint; + (previousSnapPoint, nextSnapPoint) = FindNearestSnapPoint(_horizontalSnapPoints, estimatedOffset.X); + midPoint = (previousSnapPoint + nextSnapPoint) / 2; } - offset = new Vector(nearestSnapPoint - diff.X, offset.Y); + var nearestSnapPoint = snapToNext ? (direction.X > 0 ? previousSnapPoint : nextSnapPoint) : + estimatedOffset.X < midPoint ? previousSnapPoint : nextSnapPoint; + offset = new Vector(nearestSnapPoint - diff.X, offset.Y); } - Vector GetAlignedDiff() + Vector GetAlignmentDiff() { - var vector = offset; + var vector = default(Vector); switch (VerticalSnapPointsAlignment) { @@ -1010,31 +1005,33 @@ namespace Avalonia.Controls.Presenters case SnapPointsAlignment.Far: vector += new Vector(Viewport.Width, 0); break; - } + } - return vector - offset; + return vector; } return offset; } - private static double FindNearestSnapPoint(IReadOnlyList snapPoints, double value, out double lowerSnapPoint) + private static (double previous, double next) FindNearestSnapPoint(IReadOnlyList snapPoints, double value) { var point = snapPoints.BinarySearch(value, Comparer.Default); + double previousSnapPoint, nextSnapPoint; + if (point < 0) { point = ~point; - lowerSnapPoint = snapPoints[Math.Max(0, point - 1)]; + previousSnapPoint = snapPoints[Math.Max(0, point - 1)]; + nextSnapPoint = point >= snapPoints.Count ? snapPoints.Last() : snapPoints[Math.Max(0, point)]; } else { - lowerSnapPoint = snapPoints[point]; - - point += 1; + previousSnapPoint = nextSnapPoint = snapPoints[Math.Max(0, point)]; } - return snapPoints[Math.Min(point, snapPoints.Count - 1)]; + + return (previousSnapPoint, nextSnapPoint); } private IScrollSnapPointsInfo? GetScrollSnapPointsInfo(object? content)