From 2cbd050f92980de6b2e4e8fab53af280c85268be Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Wed, 13 Feb 2019 18:51:22 -0500 Subject: [PATCH] Adding horizontal and vertical scale parameter to ScaleUpdateDetails. (#27752) --- packages/flutter/lib/src/gestures/scale.dart | 92 ++++++++++++++++--- .../flutter/test/gestures/scale_test.dart | 25 ++++- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/packages/flutter/lib/src/gestures/scale.dart b/packages/flutter/lib/src/gestures/scale.dart index 90c0e1c40c..320dd5c9a6 100644 --- a/packages/flutter/lib/src/gestures/scale.dart +++ b/packages/flutter/lib/src/gestures/scale.dart @@ -49,30 +49,67 @@ class ScaleStartDetails { class ScaleUpdateDetails { /// Creates details for [GestureScaleUpdateCallback]. /// - /// The [focalPoint], [scale], [rotation] arguments must not be null. The [scale] + /// The [focalPoint], [scale], [horizontalScale], [verticalScale], [rotation] + /// arguments must not be null. The [scale], [horizontalScale], and [verticalScale] /// argument must be greater than or equal to zero. ScaleUpdateDetails({ this.focalPoint = Offset.zero, this.scale = 1.0, + this.horizontalScale = 1.0, + this.verticalScale = 1.0, this.rotation = 0.0, }) : assert(focalPoint != null), assert(scale != null && scale >= 0.0), + assert(horizontalScale != null && horizontalScale >= 0.0), + assert(verticalScale != null && verticalScale >= 0.0), assert(rotation != null); - /// The focal point of the pointers in contact with the screen. Reported in - /// global coordinates. + /// The focal point of the pointers in contact with the screen. + /// + /// Reported in global coordinates. final Offset focalPoint; - /// The scale implied by the pointers in contact with the screen. A value - /// greater than or equal to zero. + /// The scale implied by the average distance between the pointers in contact + /// with the screen. + /// + /// This value must be greater than or equal to zero. + /// + /// See also: + /// + /// * [horizontalScale], which is the scale along the horizontal axis. + /// * [verticalScale], which is the scale along the vertical axis. final double scale; + /// The scale implied by the average distance along the horizontal axis + /// between the pointers in contact with the screen. + /// + /// This value must be greater than or equal to zero. + /// + /// See also: + /// + /// * [scale], which is the general scale implied by the pointers. + /// * [verticalScale], which is the scale along the vertical axis. + final double horizontalScale; + + /// The scale implied by the average distance along the vertical axis + /// between the pointers in contact with the screen. + /// + /// This value must be greater than or equal to zero. + /// + /// See also: + /// + /// * [scale], which is the general scale implied by the pointers. + /// * [horizontalScale], which is the scale along the horizontal axis. + final double verticalScale; + /// The angle implied by the first two pointers to enter in contact with - /// the screen. Expressed in radians. + /// the screen. + /// + /// Expressed in radians. final double rotation; @override - String toString() => 'ScaleUpdateDetails(focalPoint: $focalPoint, scale: $scale, rotation: $rotation)'; + String toString() => 'ScaleUpdateDetails(focalPoint: $focalPoint, scale: $scale, horizontalScale: $horizontalScale, verticalScale: $verticalScale, rotation: $rotation)'; } /// Details for [GestureScaleEndCallback]. @@ -165,6 +202,10 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { Offset _currentFocalPoint; double _initialSpan; double _currentSpan; + double _initialHorizontalSpan; + double _currentHorizontalSpan; + double _initialVerticalSpan; + double _currentVerticalSpan; _LineBetweenPointers _initialLine; _LineBetweenPointers _currentLine; Map _pointerLocations; @@ -173,6 +214,10 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { double get _scaleFactor => _initialSpan > 0.0 ? _currentSpan / _initialSpan : 1.0; + double get _horizontalScaleFactor => _initialHorizontalSpan > 0.0 ? _currentHorizontalSpan / _initialHorizontalSpan : 1.0; + + double get _verticalScaleFactor => _initialVerticalSpan > 0.0 ? _currentVerticalSpan / _initialVerticalSpan : 1.0; + double _computeRotationFactor() { if (_initialLine == null || _currentLine == null) { return 0.0; @@ -201,6 +246,10 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { _state = _ScaleState.possible; _initialSpan = 0.0; _currentSpan = 0.0; + _initialHorizontalSpan = 0.0; + _currentHorizontalSpan = 0.0; + _initialVerticalSpan = 0.0; + _currentVerticalSpan = 0.0; _pointerLocations = {}; _pointerQueue = []; } @@ -246,11 +295,20 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { focalPoint += _pointerLocations[pointer]; _currentFocalPoint = count > 0 ? focalPoint / count.toDouble() : Offset.zero; - // Span is the average deviation from focal point + // Span is the average deviation from focal point. Horizontal and vertical + // spans are the average deviations from the focal point's horizontal and + // vertical coordinates, respectively. double totalDeviation = 0.0; - for (int pointer in _pointerLocations.keys) + double totalHorizontalDeviation = 0.0; + double totalVerticalDeviation = 0.0; + for (int pointer in _pointerLocations.keys) { totalDeviation += (_currentFocalPoint - _pointerLocations[pointer]).distance; + totalHorizontalDeviation += (_currentFocalPoint.dx - _pointerLocations[pointer].dx).abs(); + totalVerticalDeviation += (_currentFocalPoint.dy - _pointerLocations[pointer].dy).abs(); + } _currentSpan = count > 0 ? totalDeviation / count : 0.0; + _currentHorizontalSpan = count > 0 ? totalHorizontalDeviation / count : 0.0; + _currentVerticalSpan = count > 0 ? totalVerticalDeviation / count : 0.0; } /// Updates [_initialLine] and [_currentLine] accordingly to the situation of @@ -269,7 +327,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { pointerStartId: _pointerQueue[0], pointerStartLocation: _pointerLocations[_pointerQueue[0]], pointerEndId: _pointerQueue[1], - pointerEndLocation: _pointerLocations[ _pointerQueue[1]] + pointerEndLocation: _pointerLocations[_pointerQueue[1]] ); } else { /// A new rotation process is on the way, set the [_initialLine] @@ -277,7 +335,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { pointerStartId: _pointerQueue[0], pointerStartLocation: _pointerLocations[_pointerQueue[0]], pointerEndId: _pointerQueue[1], - pointerEndLocation: _pointerLocations[ _pointerQueue[1]] + pointerEndLocation: _pointerLocations[_pointerQueue[1]] ); _currentLine = null; } @@ -287,6 +345,8 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { _initialFocalPoint = _currentFocalPoint; _initialSpan = _currentSpan; _initialLine = _currentLine; + _initialHorizontalSpan = _currentHorizontalSpan; + _initialVerticalSpan = _currentVerticalSpan; if (_state == _ScaleState.started) { if (onEnd != null) { final VelocityTracker tracker = _velocityTrackers[pointer]; @@ -327,7 +387,15 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { } if (_state == _ScaleState.started && onUpdate != null) - invokeCallback('onUpdate', () => onUpdate(ScaleUpdateDetails(scale: _scaleFactor, focalPoint: _currentFocalPoint, rotation: _computeRotationFactor()))); + invokeCallback('onUpdate', () { + onUpdate(ScaleUpdateDetails( + scale: _scaleFactor, + horizontalScale: _horizontalScaleFactor, + verticalScale: _verticalScaleFactor, + focalPoint: _currentFocalPoint, + rotation: _computeRotationFactor(), + )); + }); } void _dispatchOnStartCallbackIfNeeded() { diff --git a/packages/flutter/test/gestures/scale_test.dart b/packages/flutter/test/gestures/scale_test.dart index 05e897a656..e201d318b4 100644 --- a/packages/flutter/test/gestures/scale_test.dart +++ b/packages/flutter/test/gestures/scale_test.dart @@ -24,8 +24,12 @@ void main() { }; double updatedScale; + double updatedHorizontalScale; + double updatedVerticalScale; scale.onUpdate = (ScaleUpdateDetails details) { updatedScale = details.scale; + updatedHorizontalScale = details.horizontalScale; + updatedVerticalScale = details.verticalScale; updatedFocalPoint = details.focalPoint; }; @@ -91,18 +95,35 @@ void main() { expect(updatedFocalPoint, const Offset(10.0, 20.0)); updatedFocalPoint = null; expect(updatedScale, 2.0); + expect(updatedHorizontalScale, 2.0); + expect(updatedVerticalScale, 2.0); updatedScale = null; + updatedHorizontalScale = null; + updatedVerticalScale = null; expect(didEndScale, isFalse); expect(didTap, isFalse); // Zoom out tester.route(pointer2.move(const Offset(15.0, 25.0))); expect(updatedFocalPoint, const Offset(17.5, 27.5)); - updatedFocalPoint = null; expect(updatedScale, 0.5); - updatedScale = null; + expect(updatedHorizontalScale, 0.5); + expect(updatedVerticalScale, 0.5); expect(didTap, isFalse); + // Horizontal scaling + tester.route(pointer2.move(const Offset(0.0, 20.0))); + expect(updatedHorizontalScale, 2.0); + expect(updatedVerticalScale, 1.0); + + // Vertical scaling + tester.route(pointer2.move(const Offset(10.0, 10.0))); + expect(updatedHorizontalScale, 1.0); + expect(updatedVerticalScale, 2.0); + tester.route(pointer2.move(const Offset(15.0, 25.0))); + updatedFocalPoint = null; + updatedScale = null; + // Three-finger scaling final TestPointer pointer3 = TestPointer(3); final PointerDownEvent down3 = pointer3.down(const Offset(25.0, 35.0));