1+ using Avalonia ;
2+ using Avalonia . Input ;
3+ using Avalonia . Input . GestureRecognizers ;
4+ using System ;
5+
6+ namespace CompositionScroll . Interactions
7+ {
8+ public sealed class InteractionSource : GestureRecognizer
9+ {
10+ private static int Id = 1 ;
11+ private static int GetId ( ) => Id ++ ;
12+
13+ private int _interactionId ;
14+ private IPointer _tracking ;
15+ private Visual _rootTarget ;
16+ private Point _pointerPressedPoint ;
17+ private VelocityTracker _velocityTracker ;
18+ private bool _scrolling ;
19+
20+ public InteractionSource ( IInputElement target )
21+ {
22+ target . PointerWheelChanged += Target_PointerWheelChanged ;
23+ }
24+
25+ private InteractionTracker Tracker { get ; set ; }
26+
27+ public bool CanHorizontallyScroll { get ; set ; } = false ;
28+
29+ public bool CanVerticallyScroll { get ; set ; } = true ;
30+
31+ public bool IsScrollInertiaEnabled { get ; set ; } = true ;
32+
33+ public ScrollFeaturesEnum ScrollFeatures { get ; set ; } = ScrollFeaturesEnum . None ;
34+
35+ public double ScrollStartDistance { get ; set ; } = 10 ;
36+
37+ private bool CanAny ( ScrollFeaturesEnum feature ) => ( feature & ScrollFeatures ) != ScrollFeaturesEnum . None ;
38+
39+ private void Target_PointerWheelChanged ( object sender , PointerWheelEventArgs e )
40+ {
41+ if ( Tracker == null )
42+ return ;
43+
44+ var delta = e . Delta ;
45+
46+ if ( CanAny ( ScrollFeaturesEnum . WheelSwapDirections ) )
47+ {
48+ delta = new Vector ( delta . Y , delta . X ) ;
49+ }
50+
51+ if ( e . KeyModifiers == KeyModifiers . Shift )
52+ {
53+ delta = new Vector ( delta . Y , delta . X ) ;
54+ }
55+
56+ delta = FitVector ( delta , CanHorizontallyScroll , CanVerticallyScroll ) ;
57+ delta = delta . Negate ( ) ;
58+
59+ delta = delta * 50 ;
60+
61+ Tracker . AnimatePositionBy ( new Vector3D ( delta . X , delta . Y , 0 ) ) ;
62+ e . Handled = true ;
63+ }
64+
65+ protected override void PointerPressed ( PointerPressedEventArgs e )
66+ {
67+ if ( e . Pointer . Type == PointerType . Mouse && ! CanAny ( ScrollFeaturesEnum . MousePressedScroll ) )
68+ return ;
69+
70+ EndGesture ( ) ;
71+ _tracking = e . Pointer ;
72+ _interactionId = GetId ( ) ;
73+ _rootTarget = ( Visual ) ( ( Target as Visual ) ? . VisualRoot ) ;
74+ _pointerPressedPoint = e . GetPosition ( _rootTarget ) ;
75+ _velocityTracker = new VelocityTracker ( ) ;
76+ _velocityTracker . AddPosition ( TimeSpan . FromMilliseconds ( e . Timestamp ) , default ) ;
77+ BeginUserInteraction ( ) ;
78+ }
79+
80+ protected override void PointerMoved ( PointerEventArgs e )
81+ {
82+ if ( e . Pointer != _tracking )
83+ return ;
84+
85+ var rootPoint = e . GetPosition ( _rootTarget ) ;
86+ if ( ! _scrolling )
87+ {
88+ if ( CanHorizontallyScroll && Math . Abs ( _pointerPressedPoint . X - rootPoint . X ) > ScrollStartDistance )
89+ _scrolling = true ;
90+ if ( CanVerticallyScroll && Math . Abs ( _pointerPressedPoint . Y - rootPoint . Y ) > ScrollStartDistance )
91+ _scrolling = true ;
92+
93+ if ( _scrolling )
94+ {
95+ Capture ( _tracking ) ;
96+ }
97+ }
98+
99+ if ( _scrolling )
100+ {
101+ Vector delta = _pointerPressedPoint - rootPoint ;
102+ delta = FitVector ( delta , CanHorizontallyScroll , CanVerticallyScroll ) ;
103+
104+ _velocityTracker ? . AddPosition ( TimeSpan . FromMilliseconds ( e . Timestamp ) , delta ) ;
105+ UserInteraction ( delta ) ;
106+ e . Handled = true ;
107+ }
108+ }
109+
110+ protected override void PointerReleased ( PointerReleasedEventArgs e )
111+ {
112+ if ( e . Pointer != _tracking )
113+ return ;
114+
115+ var inertia = _velocityTracker ? . GetFlingVelocity ( ) . PixelsPerSecond ?? Vector . Zero ;
116+ inertia = FitVector ( inertia , CanHorizontallyScroll , CanVerticallyScroll ) ;
117+
118+ if ( _scrolling &&
119+ IsScrollInertiaEnabled &&
120+ ( e . Pointer . Type != PointerType . Mouse || CanAny ( ScrollFeaturesEnum . MousePressedScrollEnertia ) ) )
121+ {
122+ EndGesture ( inertia ) ;
123+ }
124+ else
125+ {
126+ EndGesture ( ) ;
127+ }
128+ }
129+
130+ protected override void PointerCaptureLost ( IPointer pointer )
131+ {
132+ if ( pointer != _tracking )
133+ return ;
134+
135+ EndGesture ( ) ;
136+ }
137+
138+ private void EndGesture ( Vector ? inertia = null )
139+ {
140+ EndUserInteraction ( inertia ) ;
141+ _tracking = null ;
142+ if ( _scrolling )
143+ {
144+ _scrolling = false ;
145+ _interactionId = 0 ;
146+ _rootTarget = null ;
147+ }
148+ }
149+
150+ private static Vector FitVector ( Vector vector , bool canHoriz , bool canVer )
151+ {
152+ if ( ! canHoriz )
153+ vector = new Point ( 0 , vector . Y ) ;
154+ if ( ! canVer )
155+ vector = new Point ( vector . X , 0 ) ;
156+
157+ return vector ;
158+ }
159+
160+ private void BeginUserInteraction ( )
161+ {
162+ Tracker . InteractionStart ( _interactionId ) ;
163+ }
164+
165+ private void UserInteraction ( Vector delta )
166+ {
167+ var delta3D = new Vector3D ( delta . X , delta . Y , 0 ) ;
168+ Tracker . InteractionMove ( _interactionId , delta3D ) ;
169+ }
170+
171+ private void EndUserInteraction ( Vector ? inertia = null )
172+ {
173+ if ( inertia == null )
174+ Tracker . InteractionEnd ( _interactionId ) ;
175+ else
176+ {
177+ var inertia3D = new Vector3D ( inertia . Value . X , inertia . Value . Y , 0 ) ;
178+ Tracker . InteractionEndWithInertia ( _interactionId , inertia3D ) ;
179+ }
180+ }
181+
182+ internal void SetInteractionTracker ( InteractionTracker tracker )
183+ {
184+ Tracker = tracker ;
185+ }
186+ }
187+ }
0 commit comments