Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class ScrollGestureRecognizer : GestureRecognizer
private TimeSpan _lastTime;
private TimeSpan _inertiaStartTime;
private int _currentInertiaGestureId;
private Point _delta;

/// <summary>
/// Defines the <see cref="CanHorizontallyScroll"/> property.
Expand Down Expand Up @@ -133,21 +134,30 @@ protected override void PointerMoved(PointerEventArgs e)
_trackedRootPoint = new Point(
_trackedRootPoint.X - (_trackedRootPoint.X >= rootPoint.X ? ScrollStartDistance : -ScrollStartDistance),
_trackedRootPoint.Y - (_trackedRootPoint.Y >= rootPoint.Y ? ScrollStartDistance : -ScrollStartDistance));

Capture(e.Pointer);
}
}

if (_scrolling)
{
var vector = _trackedRootPoint - rootPoint;

_velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), _pointerPressedPoint - rootPoint);
var oldDelta = _delta;
_delta = _pointerPressedPoint - rootPoint;

if (oldDelta == _delta)
return;

_velocityTracker?.AddPosition(TimeSpan.FromMilliseconds(e.Timestamp), _delta);

_lastMoveTimestamp = e.Timestamp;
Target!.RaiseEvent(new ScrollGestureEventArgs(_gestureId, vector));
var scrollEventArgs = new ScrollGestureEventArgs(_gestureId, vector);
Target!.RaiseEvent(scrollEventArgs);
_trackedRootPoint = rootPoint;
e.Handled = true;
e.Handled = scrollEventArgs.Handled;
if(e.Handled)
{
Capture(e.Pointer);
}
}
}
}
Expand All @@ -166,7 +176,10 @@ void EndGesture()
_stopWatch?.Stop();
_stopWatch = null;
_inertia = default;
_delta = default;
_scrolling = false;
_velocityTracker?.Dispose();
_velocityTracker = null;
Target!.RaiseEvent(new ScrollGestureEndedEventArgs(_gestureId));
_gestureId = 0;
_lastMoveTimestamp = null;
Expand Down
30 changes: 16 additions & 14 deletions src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal record struct PointAtTime(bool Valid, Vector Point, TimeSpan Time);
///
/// The quality of the velocity estimation will be better if more data points
/// have been received.
internal class VelocityTracker
internal class VelocityTracker : IDisposable
{
private const int AssumePointerMoveStoppedMilliseconds = 40;
private const int HistorySize = 20;
Expand All @@ -71,13 +71,16 @@ internal class VelocityTracker
private readonly PointAtTime[] _samples = new PointAtTime[HistorySize];
private int _index = 0;

private Stopwatch _sinceLastSample = new Stopwatch();

/// <summary>
/// Adds a position as the given time to the tracker.
/// </summary>
/// <param name="time"></param>
/// <param name="position"></param>
public void AddPosition(TimeSpan time, Vector position)
{
_sinceLastSample.Restart();
_index++;
if (_index == HistorySize)
{
Expand All @@ -94,6 +97,11 @@ public void AddPosition(TimeSpan time, Vector position)
/// Returns null if there is no data on which to base an estimate.
protected virtual VelocityEstimate? GetVelocityEstimate()
{
if (_sinceLastSample.ElapsedMilliseconds > AssumePointerMoveStoppedMilliseconds)
{
return new VelocityEstimate(default, 1.0, default, default);
}

Span<double> x = stackalloc double[HistorySize];
Span<double> y = stackalloc double[HistorySize];
Span<double> w = stackalloc double[HistorySize];
Expand Down Expand Up @@ -159,17 +167,6 @@ public void AddPosition(TimeSpan time, Vector position)
}
}
}
else if(sampleCount > 1)
{
// Return linear velocity if we don't have enough samples
var distance = newestSample.Point - oldestSample.Point;
return new VelocityEstimate(
PixelsPerSecond: new Vector(distance.X / duration.Milliseconds * 1000, distance.Y / duration.Milliseconds * 1000),
Confidence: 1,
Duration: duration,
Offset: offset
);
}

// We're unable to make a velocity estimate but we did have at least one
// valid pointer position.
Expand Down Expand Up @@ -205,6 +202,11 @@ internal virtual Velocity GetFlingVelocity()
{
return GetVelocity().ClampMagnitude(MinFlingVelocity, MaxFlingVelocity);
}

public void Dispose()
{
_sinceLastSample.Stop();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it's not technically required to Stop a Stopwatch, they don't do anything until measured.

}
}

/// An nth degree polynomial fit to a dataset.
Expand Down Expand Up @@ -302,7 +304,7 @@ internal sealed class LeastSquaresSolver
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
// We just work from bottom-right to top-left calculating B's coefficients.
// "m" isn't expected to be bigger than HistorySize=20, so allocation on stack is safe.
Span<double> wy = stackalloc double[m];
Span<double> wy = stackalloc double[m];
for (int h = 0; h < m; h += 1)
{
wy[h] = y[h] * w[h];
Expand Down Expand Up @@ -360,7 +362,7 @@ private static double Multiply(Span<double> v1, Span<double> v2)
}
return result;
}

private static double Norm(Span<double> v)
{
return Math.Sqrt(Multiply(v, v));
Expand Down
6 changes: 4 additions & 2 deletions src/Avalonia.Base/Platform/DefaultPlatformSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ namespace Avalonia.Platform
[PrivateApi]
public class DefaultPlatformSettings : IPlatformSettings
{
private const int TouchTapSize = 10;
private const int TouchDoubleTapSize = 50; // Default TouchModeN_DtapDist value on win32
public virtual Size GetTapSize(PointerType type)
{
return type switch
{
PointerType.Touch or PointerType.Pen => new(10, 10),
PointerType.Touch or PointerType.Pen => new(TouchTapSize, TouchTapSize),
_ => new(4, 4),
};
}
Expand All @@ -27,7 +29,7 @@ public virtual Size GetDoubleTapSize(PointerType type)
{
return type switch
{
PointerType.Touch or PointerType.Pen => new(16, 16),
PointerType.Touch or PointerType.Pen => new(TouchDoubleTapSize, TouchDoubleTapSize),
_ => new(4, 4),
};
}
Expand Down
12 changes: 12 additions & 0 deletions src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,8 @@ private void OnScrollGesture(object? sender, ScrollGestureEventArgs e)
var scrollable = Child as ILogicalScrollable;
var isLogical = scrollable?.IsLogicalScrollEnabled == true;
var logicalScrollItemSize = new Vector(1, 1);
var canXScroll = false;
var canYScroll = false;

double x = Offset.X;
double y = Offset.Y;
Expand Down Expand Up @@ -565,6 +567,8 @@ private void OnScrollGesture(object? sender, ScrollGestureEventArgs e)
y += dy;
y = Math.Max(y, 0);
y = Math.Min(y, Extent.Height - Viewport.Height);

canYScroll = dy != 0;
}

if (Extent.Width > Viewport.Width)
Expand All @@ -581,6 +585,8 @@ private void OnScrollGesture(object? sender, ScrollGestureEventArgs e)
x += dx;
x = Math.Max(x, 0);
x = Math.Min(x, Extent.Width - Viewport.Width);

canXScroll = dx != 0;
}

if (isLogical)
Expand Down Expand Up @@ -615,6 +621,12 @@ private void OnScrollGesture(object? sender, ScrollGestureEventArgs e)

e.Handled = !IsScrollChainingEnabled || offsetChanged;

if(!e.Handled)
{
// Gesture may cause an overscroll so we mark the event as handled if it did.
e.Handled = canXScroll || canYScroll;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the logic here, won't that prevent scroll chaining in this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed while testing, scroll chaining does not work correctly anymore.

}

e.ShouldEndScrollGesture = !IsScrollChainingEnabled && !offsetChanged;
}
}
Expand Down
29 changes: 9 additions & 20 deletions src/Avalonia.Controls/Presenters/TextPresenter.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Data;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.VisualTree;
Expand Down Expand Up @@ -334,6 +334,7 @@ public int SelectionEnd
protected override bool BypassFlowDirectionPolicies => true;

internal TextSelectionHandleCanvas? TextSelectionHandleCanvas { get; set; }
internal TextBoxTextInputMethodClient? CurrentImClient { get; set; }

/// <summary>
/// Creates the <see cref="TextLayout"/> used to render the text.
Expand Down Expand Up @@ -496,7 +497,6 @@ public void ShowCaret()
public void HideCaret()
{
_caretBlink = false;
RemoveTextSelectionCanvas();
_caretTimer?.Stop();
InvalidateTextLayout();
}
Expand Down Expand Up @@ -944,7 +944,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
ResetCaretTimer();
}

private void EnsureTextSelectionLayer()
internal void EnsureTextSelectionLayer()
{
if (TextSelectionHandleCanvas == null)
{
Expand All @@ -963,7 +963,7 @@ private void EnsureTextSelectionLayer()
_layer?.Add(TextSelectionHandleCanvas);
}

private void RemoveTextSelectionCanvas()
internal void RemoveTextSelectionCanvas()
{
if(_layer != null && TextSelectionHandleCanvas is { } canvas)
{
Expand All @@ -977,11 +977,8 @@ private void RemoveTextSelectionCanvas()
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
if (TextSelectionHandleCanvas is { } c)
{
_layer?.Remove(c);
c.SetPresenter(null);
}

RemoveTextSelectionCanvas();

if (_caretTimer != null)
{
Expand Down Expand Up @@ -1026,15 +1023,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
OnPreeditChanged(PreeditText, PreeditTextCursorPosition);
}

if(change.Property == TextProperty)
{
if (!string.IsNullOrEmpty(PreeditText))
{
SetCurrentValue(PreeditTextProperty, null);
}
}

if(change.Property == CaretIndexProperty)
if(change.Property == TextProperty || change.Property == CaretIndexProperty)
{
if (!string.IsNullOrEmpty(PreeditText))
{
Expand Down Expand Up @@ -1067,7 +1056,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
case nameof(SelectionStart):
case nameof(SelectionEnd):
case nameof(SelectionForegroundBrush):
case nameof(ShowSelectionHighlightProperty):
case nameof(ShowSelectionHighlight):

case nameof(PasswordChar):
case nameof(RevealPassword):
Expand Down
Loading
Loading