@@ -15,6 +15,18 @@ export 'events.dart' show PointerDownEvent, PointerEvent, PointerPanZoomStartEve
15
15
export 'recognizer.dart' show DragStartBehavior;
16
16
export 'velocity_tracker.dart' show Velocity;
17
17
18
+ /// The default conversion factor when treating mouse scrolling as scaling.
19
+ ///
20
+ /// The value was arbitrarily chosen to feel natural for most mousewheels on
21
+ /// all supported platforms.
22
+ const double kDefaultMouseScrollToScaleFactor = 200 ;
23
+
24
+ /// The default conversion factor when treating trackpad scrolling as scaling.
25
+ ///
26
+ /// This factor matches the default [kDefaultMouseScrollToScaleFactor] of 200 to
27
+ /// feel natural for most trackpads, and the convention that scrolling up means
28
+ /// zooming in.
29
+ const Offset kDefaultTrackpadScrollToScaleFactor = Offset (0 , - 1 / kDefaultMouseScrollToScaleFactor);
18
30
19
31
/// The possible states of a [ScaleGestureRecognizer] .
20
32
enum _ScaleState {
@@ -36,26 +48,61 @@ enum _ScaleState {
36
48
}
37
49
38
50
class _PointerPanZoomData {
39
- _PointerPanZoomData ({
40
- required this .focalPoint,
41
- required this .scale,
42
- required this .rotation
43
- });
44
- Offset focalPoint;
45
- double scale;
46
- double rotation;
51
+ _PointerPanZoomData .fromStartEvent (
52
+ this .parent,
53
+ PointerPanZoomStartEvent event
54
+ ) : _position = event.position,
55
+ _pan = Offset .zero,
56
+ _scale = 1 ,
57
+ _rotation = 0 ;
58
+
59
+ _PointerPanZoomData .fromUpdateEvent (
60
+ this .parent,
61
+ PointerPanZoomUpdateEvent event
62
+ ) : _position = event.position,
63
+ _pan = event.pan,
64
+ _scale = event.scale,
65
+ _rotation = event.rotation;
66
+
67
+ final ScaleGestureRecognizer parent;
68
+ final Offset _position;
69
+ final Offset _pan;
70
+ final double _scale;
71
+ final double _rotation;
72
+
73
+ Offset get focalPoint {
74
+ if (parent.trackpadScrollCausesScale) {
75
+ return _position;
76
+ }
77
+ return _position + _pan;
78
+ }
79
+
80
+ double get scale {
81
+ if (parent.trackpadScrollCausesScale) {
82
+ return _scale * math.exp (
83
+ (_pan.dx * parent.trackpadScrollToScaleFactor.dx) +
84
+ (_pan.dy * parent.trackpadScrollToScaleFactor.dy)
85
+ );
86
+ }
87
+ return _scale;
88
+ }
89
+
90
+ double get rotation => _rotation;
47
91
48
92
@override
49
- String toString () => '_PointerPanZoomData(focalPoint : $focalPoint , scale : $scale , angle : $rotation )' ;
93
+ String toString () => '_PointerPanZoomData(parent : $parent , _position : $_position , _pan : $_pan , _scale: $ _scale , _rotation: $ _rotation )' ;
50
94
}
51
95
52
96
/// Details for [GestureScaleStartCallback] .
53
97
class ScaleStartDetails {
54
98
/// Creates details for [GestureScaleStartCallback] .
55
99
///
56
100
/// The [focalPoint] argument must not be null.
57
- ScaleStartDetails ({ this .focalPoint = Offset .zero, Offset ? localFocalPoint, this .pointerCount = 0 })
58
- : assert (focalPoint != null ), localFocalPoint = localFocalPoint ?? focalPoint;
101
+ ScaleStartDetails ({
102
+ this .focalPoint = Offset .zero,
103
+ Offset ? localFocalPoint,
104
+ this .pointerCount = 0 ,
105
+ }) : assert (focalPoint != null ), localFocalPoint = localFocalPoint ?? focalPoint;
59
106
60
107
/// The initial focal point of the pointers in contact with the screen.
61
108
///
@@ -201,20 +248,23 @@ class ScaleEndDetails {
201
248
/// Creates details for [GestureScaleEndCallback] .
202
249
///
203
250
/// The [velocity] argument must not be null.
204
- ScaleEndDetails ({ this .velocity = Velocity .zero, this .pointerCount = 0 })
251
+ ScaleEndDetails ({ this .velocity = Velocity .zero, this .scaleVelocity = 0 , this . pointerCount = 0 })
205
252
: assert (velocity != null );
206
253
207
254
/// The velocity of the last pointer to be lifted off of the screen.
208
255
final Velocity velocity;
209
256
257
+ /// The final velocity of the scale factor reported by the gesture.
258
+ final double scaleVelocity;
259
+
210
260
/// The number of pointers being tracked by the gesture recognizer.
211
261
///
212
262
/// Typically this is the number of fingers being used to pan the widget using the gesture
213
263
/// recognizer.
214
264
final int pointerCount;
215
265
216
266
@override
217
- String toString () => 'ScaleEndDetails(velocity: $velocity , pointerCount: $pointerCount )' ;
267
+ String toString () => 'ScaleEndDetails(velocity: $velocity , scaleVelocity: $ scaleVelocity , pointerCount: $pointerCount )' ;
218
268
}
219
269
220
270
/// Signature for when the pointers in contact with the screen have established
@@ -285,6 +335,8 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
285
335
super .kind,
286
336
super .supportedDevices,
287
337
this .dragStartBehavior = DragStartBehavior .down,
338
+ this .trackpadScrollCausesScale = false ,
339
+ this .trackpadScrollToScaleFactor = kDefaultTrackpadScrollToScaleFactor,
288
340
}) : assert (dragStartBehavior != null );
289
341
290
342
/// Determines what point is used as the starting point in all calculations
@@ -332,6 +384,26 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
332
384
333
385
Matrix4 ? _lastTransform;
334
386
387
+ /// {@template flutter.gestures.scale.trackpadScrollCausesScale}
388
+ /// Whether scrolling up/down on a trackpad should cause scaling instead of
389
+ /// panning.
390
+ ///
391
+ /// Defaults to false.
392
+ /// {@endtemplate}
393
+ bool trackpadScrollCausesScale;
394
+
395
+ /// {@template flutter.gestures.scale.trackpadScrollToScaleFactor}
396
+ /// A factor to control the direction and magnitude of scale when converting
397
+ /// trackpad scrolling.
398
+ ///
399
+ /// Incoming trackpad pan offsets will be divided by this factor to get scale
400
+ /// values. Increasing this offset will reduce the amount of scaling caused by
401
+ /// a fixed amount of trackpad scrolling.
402
+ ///
403
+ /// Defaults to [kDefaultTrackpadScrollToScaleFactor] .
404
+ /// {@endtemplate}
405
+ Offset trackpadScrollToScaleFactor;
406
+
335
407
late Offset _initialFocalPoint;
336
408
Offset ? _currentFocalPoint;
337
409
late double _initialSpan;
@@ -346,6 +418,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
346
418
final Map <int , Offset > _pointerLocations = < int , Offset > {};
347
419
final List <int > _pointerQueue = < int > []; // A queue to sort pointers in order of entrance
348
420
final Map <int , VelocityTracker > _velocityTrackers = < int , VelocityTracker > {};
421
+ VelocityTracker ? _scaleVelocityTracker;
349
422
late Offset _delta;
350
423
final Map <int , _PointerPanZoomData > _pointerPanZooms = < int , _PointerPanZoomData > {};
351
424
double _initialPanZoomScaleFactor = 1 ;
@@ -466,23 +539,16 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
466
539
_lastTransform = event.transform;
467
540
} else if (event is PointerPanZoomStartEvent ) {
468
541
assert (_pointerPanZooms[event.pointer] == null );
469
- _pointerPanZooms[event.pointer] = _PointerPanZoomData (
470
- focalPoint: event.position,
471
- scale: 1 ,
472
- rotation: 0
473
- );
542
+ _pointerPanZooms[event.pointer] = _PointerPanZoomData .fromStartEvent (this , event);
474
543
didChangeConfiguration = true ;
475
544
shouldStartIfAccepted = true ;
545
+ _lastTransform = event.transform;
476
546
} else if (event is PointerPanZoomUpdateEvent ) {
477
547
assert (_pointerPanZooms[event.pointer] != null );
478
- if (! event.synthesized) {
548
+ if (! event.synthesized && ! trackpadScrollCausesScale ) {
479
549
_velocityTrackers[event.pointer]! .addPosition (event.timeStamp, event.pan);
480
550
}
481
- _pointerPanZooms[event.pointer] = _PointerPanZoomData (
482
- focalPoint: event.position + event.pan,
483
- scale: event.scale,
484
- rotation: event.rotation
485
- );
551
+ _pointerPanZooms[event.pointer] = _PointerPanZoomData .fromUpdateEvent (this , event);
486
552
_lastTransform = event.transform;
487
553
shouldStartIfAccepted = true ;
488
554
} else if (event is PointerPanZoomEndEvent ) {
@@ -495,7 +561,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
495
561
_update ();
496
562
497
563
if (! didChangeConfiguration || _reconfigure (event.pointer)) {
498
- _advanceStateMachine (shouldStartIfAccepted, event.kind );
564
+ _advanceStateMachine (shouldStartIfAccepted, event);
499
565
}
500
566
stopTrackingIfPointerNoLongerDown (event);
501
567
}
@@ -607,26 +673,28 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
607
673
if (pixelsPerSecond.distanceSquared > kMaxFlingVelocity * kMaxFlingVelocity) {
608
674
velocity = Velocity (pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * kMaxFlingVelocity);
609
675
}
610
- invokeCallback <void >('onEnd' , () => onEnd !(ScaleEndDetails (velocity: velocity, pointerCount: _pointerCount)));
676
+ invokeCallback <void >('onEnd' , () => onEnd !(ScaleEndDetails (velocity: velocity, scaleVelocity : _scaleVelocityTracker ? . getVelocity ().pixelsPerSecond.dx ?? - 1 , pointerCount: _pointerCount)));
611
677
} else {
612
- invokeCallback <void >('onEnd' , () => onEnd !(ScaleEndDetails (pointerCount: _pointerCount)));
678
+ invokeCallback <void >('onEnd' , () => onEnd !(ScaleEndDetails (scaleVelocity : _scaleVelocityTracker ? . getVelocity ().pixelsPerSecond.dx ?? - 1 , pointerCount: _pointerCount)));
613
679
}
614
680
}
615
681
_state = _ScaleState .accepted;
682
+ _scaleVelocityTracker = VelocityTracker .withKind (PointerDeviceKind .touch); // arbitrary PointerDeviceKind
616
683
return false ;
617
684
}
685
+ _scaleVelocityTracker = VelocityTracker .withKind (PointerDeviceKind .touch); // arbitrary PointerDeviceKind
618
686
return true ;
619
687
}
620
688
621
- void _advanceStateMachine (bool shouldStartIfAccepted, PointerDeviceKind pointerDeviceKind ) {
689
+ void _advanceStateMachine (bool shouldStartIfAccepted, PointerEvent event ) {
622
690
if (_state == _ScaleState .ready) {
623
691
_state = _ScaleState .possible;
624
692
}
625
693
626
694
if (_state == _ScaleState .possible) {
627
695
final double spanDelta = (_currentSpan - _initialSpan).abs ();
628
696
final double focalPointDelta = (_currentFocalPoint! - _initialFocalPoint).distance;
629
- if (spanDelta > computeScaleSlop (pointerDeviceKind ) || focalPointDelta > computePanSlop (pointerDeviceKind , gestureSettings) || math.max (_scaleFactor / _pointerScaleFactor, _pointerScaleFactor / _scaleFactor) > 1.05 ) {
697
+ if (spanDelta > computeScaleSlop (event.kind ) || focalPointDelta > computePanSlop (event.kind , gestureSettings) || math.max (_scaleFactor / _pointerScaleFactor, _pointerScaleFactor / _scaleFactor) > 1.05 ) {
630
698
resolve (GestureDisposition .accepted);
631
699
}
632
700
} else if (_state.index >= _ScaleState .accepted.index) {
@@ -638,19 +706,22 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
638
706
_dispatchOnStartCallbackIfNeeded ();
639
707
}
640
708
641
- if (_state == _ScaleState .started && onUpdate != null ) {
642
- invokeCallback <void >('onUpdate' , () {
643
- onUpdate !(ScaleUpdateDetails (
644
- scale: _scaleFactor,
645
- horizontalScale: _horizontalScaleFactor,
646
- verticalScale: _verticalScaleFactor,
647
- focalPoint: _currentFocalPoint! ,
648
- localFocalPoint: _localFocalPoint,
649
- rotation: _computeRotationFactor (),
650
- pointerCount: _pointerCount,
651
- focalPointDelta: _delta,
652
- ));
653
- });
709
+ if (_state == _ScaleState .started) {
710
+ _scaleVelocityTracker? .addPosition (event.timeStamp, Offset (_scaleFactor, 0 ));
711
+ if (onUpdate != null ) {
712
+ invokeCallback <void >('onUpdate' , () {
713
+ onUpdate !(ScaleUpdateDetails (
714
+ scale: _scaleFactor,
715
+ horizontalScale: _horizontalScaleFactor,
716
+ verticalScale: _verticalScaleFactor,
717
+ focalPoint: _currentFocalPoint! ,
718
+ localFocalPoint: _localFocalPoint,
719
+ rotation: _computeRotationFactor (),
720
+ pointerCount: _pointerCount,
721
+ focalPointDelta: _delta,
722
+ ));
723
+ });
724
+ }
654
725
}
655
726
}
656
727
0 commit comments