Browse Source

Improve scroll snapping with mouse wheel (#14562)

* improve scroll snapping

* remove debug code, ensure snappoints are in range
pull/16303/head
Emmanuel Hansen 2 years ago
committed by GitHub
parent
commit
e1203becc6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 83
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

83
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<double> snapPoints, double value, out double lowerSnapPoint)
private static (double previous, double next) FindNearestSnapPoint(IReadOnlyList<double> snapPoints, double value)
{
var point = snapPoints.BinarySearch(value, Comparer<double>.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)

Loading…
Cancel
Save